From 0edf82ddcf0b917026d9b6d7a86f7c78169ce98b Mon Sep 17 00:00:00 2001 From: Aleksey Proshutinskiy Date: Fri, 15 Mar 2024 01:22:37 +0200 Subject: [PATCH] feat: migrate to alloy and more (#2166) --- Cargo.lock | 482 ++++++++++++- Cargo.toml | 7 +- crates/chain-connector/Cargo.toml | 6 +- crates/chain-connector/src/connector.rs | 407 ++++++----- crates/chain-connector/src/error.rs | 14 +- .../chain-connector/src/function/capacity.rs | 101 +++ crates/chain-connector/src/function/core.rs | 20 + .../src/function/current_epoch.rs | 23 - crates/chain-connector/src/function/deal.rs | 20 + .../src/function/difficulty.rs | 21 - .../src/function/epoch_duration.rs | 24 - .../src/function/get_commitment.rs | 28 - .../src/function/get_commitment_status.rs | 29 - .../src/function/get_compute_peer.rs | 53 -- .../src/function/get_compute_units.rs | 48 -- .../src/function/get_deal_status.rs | 23 - .../src/function/global_nonce.rs | 23 - .../src/function/init_timestamp.rs | 26 - crates/chain-connector/src/function/mod.rs | 32 +- crates/chain-connector/src/function/offer.rs | 133 ++++ .../src/function/remove_cu_from_deal.rs | 27 - .../src/function/submit_proof.rs | 48 -- crates/chain-connector/src/lib.rs | 1 + crates/chain-data/Cargo.toml | 3 + crates/chain-data/src/function.rs | 33 - crates/chain-data/src/lib.rs | 6 +- crates/chain-data/src/log.rs | 96 +-- crates/chain-data/src/u256.rs | 36 - crates/chain-data/src/utils.rs | 6 +- crates/chain-listener/Cargo.toml | 5 +- .../chain-listener/src/event/cc_activated.rs | 173 ++--- .../src/event/compute_unit_matched.rs | 127 ++++ .../chain-listener/src/event/deal_matched.rs | 203 ------ crates/chain-listener/src/event/mod.rs | 9 +- .../src/event/unit_activated.rs | 141 ++-- .../src/event/unit_deactivated.rs | 116 +--- crates/chain-listener/src/lib.rs | 2 + crates/chain-listener/src/listener.rs | 651 +++++++++++------- crates/chain-listener/src/persistence.rs | 12 +- crates/chain-types/Cargo.toml | 18 - crates/chain-types/src/commitment.rs | 72 -- crates/chain-types/src/commitment_status.rs | 79 --- crates/chain-types/src/compute_peer.rs | 92 --- crates/chain-types/src/compute_unit.rs | 137 ---- crates/chain-types/src/deal_status.rs | 57 -- crates/chain-types/src/errors.rs | 5 - crates/chain-types/src/lib.rs | 15 - crates/chain-types/src/types.rs | 12 - 48 files changed, 1657 insertions(+), 2045 deletions(-) create mode 100644 crates/chain-connector/src/function/capacity.rs create mode 100644 crates/chain-connector/src/function/core.rs delete mode 100644 crates/chain-connector/src/function/current_epoch.rs create mode 100644 crates/chain-connector/src/function/deal.rs delete mode 100644 crates/chain-connector/src/function/difficulty.rs delete mode 100644 crates/chain-connector/src/function/epoch_duration.rs delete mode 100644 crates/chain-connector/src/function/get_commitment.rs delete mode 100644 crates/chain-connector/src/function/get_commitment_status.rs delete mode 100644 crates/chain-connector/src/function/get_compute_peer.rs delete mode 100644 crates/chain-connector/src/function/get_compute_units.rs delete mode 100644 crates/chain-connector/src/function/get_deal_status.rs delete mode 100644 crates/chain-connector/src/function/global_nonce.rs delete mode 100644 crates/chain-connector/src/function/init_timestamp.rs create mode 100644 crates/chain-connector/src/function/offer.rs delete mode 100644 crates/chain-connector/src/function/remove_cu_from_deal.rs delete mode 100644 crates/chain-connector/src/function/submit_proof.rs delete mode 100644 crates/chain-data/src/function.rs delete mode 100644 crates/chain-data/src/u256.rs create mode 100644 crates/chain-listener/src/event/compute_unit_matched.rs delete mode 100644 crates/chain-listener/src/event/deal_matched.rs delete mode 100644 crates/chain-types/Cargo.toml delete mode 100644 crates/chain-types/src/commitment.rs delete mode 100644 crates/chain-types/src/commitment_status.rs delete mode 100644 crates/chain-types/src/compute_peer.rs delete mode 100644 crates/chain-types/src/compute_unit.rs delete mode 100644 crates/chain-types/src/deal_status.rs delete mode 100644 crates/chain-types/src/errors.rs delete mode 100644 crates/chain-types/src/lib.rs delete mode 100644 crates/chain-types/src/types.rs diff --git a/Cargo.lock b/Cargo.lock index e0862959d7..dd09f8246f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -154,6 +154,80 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +[[package]] +name = "alloy-primitives" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "600d34d8de81e23b6d909c094e23b3d357e01ca36b78a8c5424c501eedbe86f0" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if", + "const-hex", + "derive_more", + "hex-literal", + "itoa", + "k256", + "keccak-asm", + "proptest", + "rand 0.8.5", + "ruint", + "serde", + "tiny-keccak", +] + +[[package]] +name = "alloy-rlp" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d58d9f5da7b40e9bfff0b7e7816700be4019db97d4b6359fe7f94a9e22e42ac" +dependencies = [ + "arrayvec", + "bytes", +] + +[[package]] +name = "alloy-sol-macro" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86ec0a47740b20bc5613b8712d0d321d031c4efc58e9645af96085d5cccfc27" +dependencies = [ + "const-hex", + "dunce", + "heck 0.4.1", + "indexmap 2.2.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.46", + "syn-solidity", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-types" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad09ec5853fa700d12d778ad224dcdec636af424d29fad84fb9a2f16a5b0ef09" +dependencies = [ + "alloy-primitives", + "alloy-sol-macro", + "const-hex", + "serde", +] + +[[package]] +name = "alloy_serde_macro" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7dbc9782ace5a026aa8be577038474708868e0c364f98f782546aec915f9510" +dependencies = [ + "alloy-primitives", + "hex", + "paste", + "serde", +] + [[package]] name = "ambient-authority" version = "0.0.2" @@ -296,6 +370,130 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +[[package]] +name = "ark-ff" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" +dependencies = [ + "ark-ff-asm 0.3.0", + "ark-ff-macros 0.3.0", + "ark-serialize 0.3.0", + "ark-std 0.3.0", + "derivative", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.3.3", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm 0.4.2", + "ark-ff-macros 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "digest 0.10.7", + "itertools 0.10.5", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.4.0", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" +dependencies = [ + "num-bigint", + "num-traits", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-serialize" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" +dependencies = [ + "ark-std 0.3.0", + "digest 0.9.0", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-std 0.4.0", + "digest 0.10.7", + "num-bigint", +] + +[[package]] +name = "ark-std" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + [[package]] name = "arrayref" version = "0.3.7" @@ -500,6 +698,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "auto_impl" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.46", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -707,6 +916,12 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base64" version = "0.13.1" @@ -1087,11 +1302,12 @@ dependencies = [ name = "chain-connector" version = "0.1.0" dependencies = [ + "alloy-primitives", + "alloy-sol-types", "ccp-shared", "chain-data", - "chain-types", "clarity", - "ethabi", + "const-hex", "eyre", "fluence-libp2p", "futures", @@ -1102,6 +1318,7 @@ dependencies = [ "particle-args", "particle-builtins", "particle-execution", + "serde", "serde_json", "server-config", "thiserror", @@ -1114,6 +1331,9 @@ dependencies = [ name = "chain-data" version = "0.1.0" dependencies = [ + "alloy-sol-types", + "alloy_serde_macro", + "const-hex", "ethabi", "eyre", "hex", @@ -1128,12 +1348,14 @@ dependencies = [ name = "chain-listener" version = "0.1.0" dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "alloy_serde_macro", "backoff", "ccp-rpc-client", "ccp-shared", "chain-connector", "chain-data", - "chain-types", "core-manager", "cpu-utils", "ethabi", @@ -1158,20 +1380,6 @@ dependencies = [ "types", ] -[[package]] -name = "chain-types" -version = "0.1.0" -dependencies = [ - "ccp-shared", - "chain-data", - "ethabi", - "eyre", - "hex", - "serde", - "tokio", - "types", -] - [[package]] name = "chrono" version = "0.4.34" @@ -1388,6 +1596,19 @@ dependencies = [ "tracing", ] +[[package]] +name = "const-hex" +version = "1.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ba00838774b4ab0233e355d26710fbfc8327a05c017f6dc4873f876d1f79f78" +dependencies = [ + "cfg-if", + "cpufeatures", + "hex", + "proptest", + "serde", +] + [[package]] name = "const-oid" version = "0.9.5" @@ -1404,6 +1625,12 @@ checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" name = "control-macro" version = "0.1.0" +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + [[package]] name = "core-foundation" version = "0.9.3" @@ -1689,6 +1916,18 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -1880,8 +2119,10 @@ version = "0.99.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ + "convert_case", "proc-macro2", "quote", + "rustc_version 0.4.0", "syn 1.0.109", ] @@ -1917,6 +2158,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", + "const-oid", "crypto-common", "subtle", ] @@ -1985,6 +2227,26 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + [[package]] name = "ed25519" version = "2.2.3" @@ -2017,6 +2279,25 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + [[package]] name = "encoding_rs" version = "0.8.33" @@ -2172,6 +2453,17 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +[[package]] +name = "fastrlp" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] + [[package]] name = "fd-lock" version = "4.0.0" @@ -2183,6 +2475,16 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "fiat-crypto" version = "0.2.5" @@ -2560,6 +2862,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -2657,6 +2960,17 @@ dependencies = [ "web-sys", ] +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "h2" version = "0.3.24" @@ -2766,6 +3080,12 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + [[package]] name = "hex-utils" version = "0.1.0" @@ -3617,6 +3937,20 @@ dependencies = [ "url", ] +[[package]] +name = "k256" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2 0.10.8", + "signature", +] + [[package]] name = "kademlia" version = "0.1.0" @@ -3654,6 +3988,16 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "keccak-asm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb8515fff80ed850aea4a1595f2e519c003e2a00a82fe168ebf5269196caf444" +dependencies = [ + "digest 0.10.7", + "sha3-asm", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -6635,6 +6979,16 @@ dependencies = [ "quick-error", ] +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac 0.12.1", + "subtle", +] + [[package]] name = "ring" version = "0.16.20" @@ -6728,6 +7082,36 @@ dependencies = [ "tokio", ] +[[package]] +name = "ruint" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f308135fef9fc398342da5472ce7c484529df23743fb7c734e0f3d472971e62" +dependencies = [ + "alloy-rlp", + "ark-ff 0.3.0", + "ark-ff 0.4.2", + "bytes", + "fastrlp", + "num-bigint", + "num-traits", + "parity-scale-codec", + "primitive-types", + "proptest", + "rand 0.8.5", + "rlp", + "ruint-macro", + "serde", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f86854cf50259291520509879a5c294c3c9a4c334e9ff65071c51e42ef1e2343" + [[package]] name = "rust-ini" version = "0.18.0" @@ -6765,6 +7149,15 @@ dependencies = [ "semver 0.1.20", ] +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", +] + [[package]] name = "rustc_version" version = "0.4.0" @@ -6961,6 +7354,20 @@ dependencies = [ "untrusted 0.9.0", ] +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + [[package]] name = "secp256k1" version = "0.28.2" @@ -7008,6 +7415,15 @@ version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac" +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + [[package]] name = "semver" version = "1.0.20" @@ -7017,6 +7433,15 @@ dependencies = [ "serde", ] +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + [[package]] name = "send_wrapper" version = "0.4.0" @@ -7251,6 +7676,16 @@ dependencies = [ "keccak", ] +[[package]] +name = "sha3-asm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bac61da6b35ad76b195eb4771210f947734321a8d81d7738e1580d953bc7a15e" +dependencies = [ + "cc", + "cfg-if", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -7284,6 +7719,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ + "digest 0.10.7", "rand_core 0.6.4", ] @@ -7573,6 +8009,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn-solidity" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3d0961cd53c23ea94eeec56ba940f636f6394788976e9f16ca5ee0aca7464a" +dependencies = [ + "paste", + "proc-macro2", + "quote", + "syn 2.0.46", +] + [[package]] name = "sync_wrapper" version = "0.1.2" diff --git a/Cargo.toml b/Cargo.toml index b406f49de6..d670cdb22d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,6 @@ members = [ "crates/chain-connector", "crates/hex-utils", "crates/chain-data", - "crates/chain-types", "crates/types", "crates/core-manager", ] @@ -101,7 +100,6 @@ hex-utils = { path = "crates/hex-utils" } chain-data = { path = "crates/chain-data" } chain-listener = { path = "crates/chain-listener" } chain-connector = { path = "crates/chain-connector" } -chain-types = { path = "crates/chain-types" } types = { path = "crates/types" } core-manager = { path = "crates/core-manager" } @@ -176,7 +174,10 @@ clarity = "1.3.0" cpu-utils = "0.7.0" ccp-shared = "0.7.0" ccp-rpc-client = "0.7.0" - +alloy-sol-types = "0.6.4" +alloy-primitives = "0.6.4" +alloy_serde_macro = "0.1.2" +const-hex = "1.11.3" [profile.dev] opt-level = 0 diff --git a/crates/chain-connector/Cargo.toml b/crates/chain-connector/Cargo.toml index 3ba9998af7..a572bbe045 100644 --- a/crates/chain-connector/Cargo.toml +++ b/crates/chain-connector/Cargo.toml @@ -10,8 +10,6 @@ particle-builtins = { workspace = true } particle-execution = { workspace = true } particle-args = { workspace = true } chain-data = { workspace = true } -chain-types = { workspace = true } -ethabi = { workspace = true } jsonrpsee = { workspace = true, features = ["macros", "server", "client"] } eyre = { workspace = true } fluence-libp2p = { workspace = true } @@ -26,6 +24,10 @@ ccp-shared = { workspace = true } thiserror = { workspace = true } tracing = { workspace = true } types = { workspace = true } +alloy-sol-types = { workspace = true } +alloy-primitives = { workspace = true } +const-hex = { workspace = true } +serde = { workspace = true } [dev-dependencies] mockito = { workspace = true } diff --git a/crates/chain-connector/src/connector.rs b/crates/chain-connector/src/connector.rs index 3857521ee8..6000a409d3 100644 --- a/crates/chain-connector/src/connector.rs +++ b/crates/chain-connector/src/connector.rs @@ -1,11 +1,15 @@ +use alloy_primitives::hex::ToHexExt; +use alloy_primitives::{uint, FixedBytes, U256}; +use alloy_sol_types::sol_data::Array; +use alloy_sol_types::{SolCall, SolType}; use std::collections::HashMap; +use std::ops::Div; +use std::str::FromStr; use std::sync::Arc; use ccp_shared::proof::CCProof; use ccp_shared::types::{Difficulty, GlobalNonce, CUID}; -use clarity::Transaction; -use ethabi::ethereum_types::U256; -use ethabi::Token; +use clarity::{Transaction, Uint256}; use eyre::eyre; use futures::FutureExt; use jsonrpsee::core::client::{BatchResponse, ClientT}; @@ -16,12 +20,11 @@ use serde_json::Value as JValue; use serde_json::{json, Value}; use tokio::sync::Mutex; -use chain_data::ChainDataError::InvalidTokenSize; -use chain_data::{next_opt, parse_chain_data, peer_id_to_bytes, ChainFunction}; -use chain_types::{ - Commitment, CommitmentId, CommitmentStatus, ComputePeer, ComputeUnit, DealStatus, -}; +use crate::ConnectorError::{InvalidU256, ResponseParseError}; +use crate::{CCStatus, Capacity, CommitmentId, Core, Deal, Offer}; +use chain_data::peer_id_to_bytes; use fluence_libp2p::PeerId; +use hex_utils::decode_hex; use particle_args::{Args, JError}; use particle_builtins::{wrap, CustomService}; use particle_execution::{ParticleParams, ServiceFunction}; @@ -29,15 +32,9 @@ use server_config::ChainConfig; use types::DealId; use crate::error::{process_response, ConnectorError}; -use crate::function::{GetCommitmentFunction, GetCommitmentStatusFunction, SubmitProofFunction}; -use crate::ConnectorError::InvalidBaseFeePerGas; -use crate::{ - CurrentEpochFunction, DifficultyFunction, EpochDurationFunction, GetComputePeerFunction, - GetComputeUnitsFunction, GetDealStatusFunction, GetGlobalNonceFunction, InitTimestampFunction, - ReturnComputeUnitFromDeal, -}; +use crate::Offer::{ComputePeer, ComputeUnit}; -const BASE_FEE_MULTIPLIER: f64 = 0.125; +const BASE_FEE_PREMIUM_DIVIDER: U256 = uint!(8_U256); pub struct ChainConnector { client: Arc, @@ -52,6 +49,8 @@ pub struct CCInitParams { pub global_nonce: GlobalNonce, pub current_epoch: U256, pub epoch_duration: U256, + pub min_proofs_per_epoch: U256, + pub max_proofs_per_epoch: U256, } impl ChainConnector { @@ -117,11 +116,11 @@ impl ChainConnector { .as_object() .and_then(|o| o.get("baseFeePerGas")) .and_then(Value::as_str) - .ok_or(InvalidBaseFeePerGas(block.to_string()))? + .ok_or(ResponseParseError(block.to_string()))? .to_string(); let base_fee_per_gas = - U256::from_str_radix(&fee, 16).map_err(|_| InvalidBaseFeePerGas(fee))?; + U256::from_str(&fee).map_err(|err| InvalidU256(fee, err.to_string()))?; Ok(base_fee_per_gas) } @@ -134,8 +133,7 @@ impl ChainConnector { .await, )?; - let nonce = - U256::from_str_radix(&resp, 16).map_err(|_| ConnectorError::InvalidNonce(resp))?; + let nonce = U256::from_str(&resp).map_err(|err| InvalidU256(resp, err.to_string()))?; Ok(nonce) } @@ -146,7 +144,7 @@ impl ChainConnector { .await, )?; let max_priority_fee_per_gas = - U256::from_str_radix(&resp, 16).map_err(|_| ConnectorError::InvalidGasLimit(resp))?; + U256::from_str(&resp).map_err(|err| InvalidU256(resp, err.to_string()))?; Ok(max_priority_fee_per_gas) } @@ -163,8 +161,7 @@ impl ChainConnector { ) .await, )?; - let limit = - U256::from_str_radix(&resp, 16).map_err(|_| ConnectorError::InvalidGasLimit(resp))?; + let limit = U256::from_str(&resp).map_err(|err| InvalidU256(resp, err.to_string()))?; Ok(limit) } @@ -173,11 +170,8 @@ impl ChainConnector { tracing::info!(target: "chain-connector", "Estimating gas for tx from {} to {} data {}", self.config.wallet_key.to_address(), to, hex::encode(&data)); let gas_limit = self.estimate_gas_limit(&data, to).await?; let max_priority_fee_per_gas = self.max_priority_fee_per_gas().await?; - - let increase = (base_fee_per_gas.as_u64() as f64 * BASE_FEE_MULTIPLIER) as u128; - let base_fee = base_fee_per_gas - .checked_add(increase.into()) - .ok_or(InvalidBaseFeePerGas("AAAA".to_string()))?; + let increase = base_fee_per_gas.div(BASE_FEE_PREMIUM_DIVIDER); + let base_fee = base_fee_per_gas.checked_add(increase).unwrap_or(U256::MAX); // (base fee + priority fee). let max_fee_per_gas = base_fee + max_priority_fee_per_gas; @@ -188,14 +182,16 @@ impl ChainConnector { // Create a new transaction let tx = Transaction::Eip1559 { chain_id: self.config.network_id.into(), - nonce: nonce.as_u128().into(), - max_priority_fee_per_gas: max_priority_fee_per_gas.as_u128().into(), - gas_limit: gas_limit.as_u128().into(), + nonce: Uint256::from_le_bytes(&nonce.to_le_bytes_vec()), + max_priority_fee_per_gas: Uint256::from_le_bytes( + &max_priority_fee_per_gas.to_le_bytes_vec(), + ), + gas_limit: Uint256::from_le_bytes(&gas_limit.to_le_bytes_vec()), to: to.parse()?, value: 0u32.into(), data, signature: None, // Not signed. Yet. - max_fee_per_gas: max_fee_per_gas.as_u128().into(), + max_fee_per_gas: Uint256::from_le_bytes(&max_fee_per_gas.to_le_bytes_vec()), access_list: vec![], }; @@ -218,8 +214,12 @@ impl ChainConnector { } pub async fn get_current_commitment_id(&self) -> Result, ConnectorError> { - let peer_id = Token::FixedBytes(peer_id_to_bytes(self.host_id)); - let data = GetComputePeerFunction::data(&[peer_id])?; + let peer_id = peer_id_to_bytes(self.host_id); + let data: String = Offer::getComputePeerCall { + peerId: peer_id.into(), + } + .abi_encode() + .encode_hex(); let resp: String = process_response( self.client .request( @@ -234,36 +234,20 @@ impl ChainConnector { ) .await, )?; - Ok(ComputePeer::from(&resp)?.commitment_id) + let compute_peer = ::abi_decode(&decode_hex(&resp)?, true)?; + Ok(CommitmentId::new(compute_peer.commitmentId.0)) } pub async fn get_commitment_status( &self, commitment_id: CommitmentId, - ) -> Result { - let data = GetCommitmentStatusFunction::data(&[Token::FixedBytes(commitment_id.0)])?; - let resp: String = process_response( - self.client - .request( - "eth_call", - rpc_params![ - json!({ - "data": data, - "to": self.config.cc_contract_address, - }), - "latest" - ], - ) - .await, - )?; - Ok(CommitmentStatus::from(&resp)?) - } + ) -> Result { + let data: String = Capacity::getStatusCall { + commitmentId: commitment_id.0.into(), + } + .abi_encode() + .encode_hex(); - pub async fn get_commitment( - &self, - commitment_id: CommitmentId, - ) -> Result { - let data = GetCommitmentFunction::data(&[Token::FixedBytes(commitment_id.0)])?; let resp: String = process_response( self.client .request( @@ -278,45 +262,41 @@ impl ChainConnector { ) .await, )?; - Ok(Commitment::from(&resp)?) + Ok(::abi_decode( + &decode_hex(&resp)?, + true, + )?) } pub async fn get_global_nonce(&self) -> Result { - let data = GetGlobalNonceFunction::data(&[])?; let resp: String = process_response( self.client - .request( - "eth_call", - rpc_params![ - json!({ - "data": data, - "to": self.config.cc_contract_address - }), - "latest" - ], - ) + .request("eth_call", self.global_nonce_params()) .await, )?; - let bytes = GetGlobalNonceFunction::decode_fixed_bytes(&resp)?; - Ok(GlobalNonce::new( - bytes.try_into().map_err(|_| InvalidTokenSize)?, - )) + let bytes: FixedBytes<32> = FixedBytes::from_str(&resp)?; + Ok(GlobalNonce::new(bytes.0)) } pub async fn submit_proof(&self, proof: CCProof) -> Result { - let data = SubmitProofFunction::data_bytes(&[ - Token::FixedBytes(proof.cu_id.as_ref().to_vec()), - Token::FixedBytes(proof.local_nonce.as_ref().to_vec()), - Token::FixedBytes(proof.result_hash.as_ref().to_vec()), - ])?; + let data = Capacity::submitProofCall { + unitId: proof.cu_id.as_ref().into(), + localUnitNonce: proof.local_nonce.as_ref().into(), + resultHash: proof.result_hash.as_ref().into(), + } + .abi_encode(); self.send_tx(data, &self.config.cc_contract_address).await } pub async fn get_compute_units(&self) -> Result, ConnectorError> { - let data = - GetComputeUnitsFunction::data(&[Token::FixedBytes(peer_id_to_bytes(self.host_id))])?; + let data: String = Offer::getComputeUnitsCall { + peerId: peer_id_to_bytes(self.host_id).into(), + } + .abi_encode() + .encode_hex(); + let resp: String = process_response( self.client .request( @@ -331,12 +311,8 @@ impl ChainConnector { ) .await, )?; - let mut tokens = - parse_chain_data(&resp, &GetComputeUnitsFunction::result_signature())?.into_iter(); - let units = next_opt(&mut tokens, "units", Token::into_array)?.into_iter(); - let compute_units = units - .map(ComputeUnit::from_token) - .collect::, _>>()?; + let bytes = decode_hex(&resp)?; + let compute_units = as SolType>::abi_decode(&bytes, true)?; Ok(compute_units) } @@ -344,68 +320,80 @@ impl ChainConnector { pub async fn get_cc_init_params(&self) -> eyre::Result { let mut batch = BatchRequestBuilder::new(); - batch.insert("eth_call", self.difficulty_params()?)?; - batch.insert("eth_call", self.init_timestamp_params()?)?; - batch.insert("eth_call", self.global_nonce_params()?)?; - batch.insert("eth_call", self.current_epoch_params()?)?; - batch.insert("eth_call", self.epoch_duration_params()?)?; + batch.insert("eth_call", self.difficulty_params())?; + batch.insert("eth_call", self.init_timestamp_params())?; + batch.insert("eth_call", self.global_nonce_params())?; + batch.insert("eth_call", self.current_epoch_params())?; + batch.insert("eth_call", self.epoch_duration_params())?; + batch.insert("eth_call", self.min_proofs_per_epoch_params())?; + batch.insert("eth_call", self.max_proofs_per_epoch_params())?; + tracing::debug!("Sending batch request: {batch:?}"); let resp: BatchResponse = self.client.batch_request(batch).await?; + tracing::debug!("Got response for batch request: {resp:?}"); let mut results = resp .into_ok() .map_err(|err| eyre!("Some request failed in a batch {err:?}"))?; - let difficulty = DifficultyFunction::decode_fixed_bytes( - &results.next().ok_or(eyre!("No response for difficulty"))?, - )?; - let init_timestamp = InitTimestampFunction::decode_uint( + let difficulty: FixedBytes<32> = + FixedBytes::from_str(&results.next().ok_or(eyre!("No response for difficulty"))?)?; + let init_timestamp = U256::from_str( &results .next() .ok_or(eyre!("No response for init_timestamp"))?, )?; - let global_nonce = GetGlobalNonceFunction::decode_fixed_bytes( + + let global_nonce = FixedBytes::from_str( &results .next() .ok_or(eyre!("No response for global_nonce"))?, )?; - let current_epoch = CurrentEpochFunction::decode_uint( + + let current_epoch = U256::from_str( &results .next() .ok_or(eyre!("No response for current_epoch"))?, )?; - let epoch_duration = EpochDurationFunction::decode_uint( + + let epoch_duration = U256::from_str( &results .next() .ok_or(eyre!("No response for epoch_duration"))?, )?; + let min_proofs_per_epoch = U256::from_str( + &results + .next() + .ok_or(eyre!("No response for min_proofs_per_epoch"))?, + )?; + + let max_proofs_per_epoch = U256::from_str( + &results + .next() + .ok_or(eyre!("No response for max_proofs_per_epoch"))?, + )?; + Ok(CCInitParams { - difficulty: Difficulty::new( - difficulty - .try_into() - .map_err(|_| eyre!("Failed to convert difficulty"))?, - ), + difficulty: Difficulty::new(difficulty.0), init_timestamp, - global_nonce: GlobalNonce::new( - global_nonce - .try_into() - .map_err(|_| eyre!("Failed to convert global_nonce"))?, - ), + global_nonce: GlobalNonce::new(global_nonce.0), current_epoch, epoch_duration, + min_proofs_per_epoch, + max_proofs_per_epoch, }) } pub async fn get_deal_statuses<'a, I>( &self, deal_ids: I, - ) -> Result>, ConnectorError> + ) -> Result>, ConnectorError> where I: Iterator, { let mut batch = BatchRequestBuilder::new(); for deal_id in deal_ids { - let data = GetDealStatusFunction::data(&[])?; + let data: String = Deal::getStatusCall {}.abi_encode().encode_hex(); batch.insert( "eth_call", rpc_params![ @@ -423,8 +411,60 @@ impl ChainConnector { for status in resp.into_iter() { let status = status - .map(|r| DealStatus::from(&r).map_err(ConnectorError::ParseChainDataFailed)) - .map_err(|e| ConnectorError::RpcError(e.to_owned().into()))?; + .map(|r| { + decode_hex(&r) + .map(|bytes| { + ::abi_decode(&bytes, true) + .map_err(ConnectorError::ParseChainDataFailedAlloy) + }) + .map_err(ConnectorError::DecodeHex) + }) + .map_err(|e| ConnectorError::RpcError(e.to_owned().into()))??; + statuses.push(status); + } + + Ok(statuses) + } + + pub async fn get_tx_receipts<'a, I>( + &self, + tx_hashes: I, + ) -> Result>, ConnectorError> + where + I: Iterator, + { + let mut batch = BatchRequestBuilder::new(); + for tx_hash in tx_hashes { + batch.insert("eth_getTransactionReceipt", rpc_params![tx_hash])?; + } + let resp: BatchResponse = self.client.batch_request(batch).await?; + let mut receipts = vec![]; + for receipt in resp.into_iter() { + let receipt = receipt.map_err(|e| ConnectorError::RpcError(e.to_owned().into())); + receipts.push(receipt); + } + Ok(receipts) + } + + pub async fn get_tx_statuses<'a, I>( + &self, + tx_hashes: I, + ) -> Result, ConnectorError>>, ConnectorError> + where + I: Iterator, + { + let mut statuses = vec![]; + + for receipt in self.get_tx_receipts(tx_hashes).await? { + let status = receipt.map(|receipt| { + if let Some(obj) = receipt.as_object() { + obj.get("status") + .and_then(Value::as_str) + .map(|s| s == "0x1") + } else { + None + } + }); statuses.push(status); } @@ -432,52 +472,74 @@ impl ChainConnector { } pub async fn exit_deal(&self, cu_id: &CUID) -> Result { - let data = - ReturnComputeUnitFromDeal::data_bytes(&[Token::FixedBytes(cu_id.as_ref().to_vec())])?; + let data = Offer::returnComputeUnitFromDealCall { + unitId: cu_id.as_ref().into(), + } + .abi_encode(); + self.send_tx(data, &self.config.market_contract_address) .await } - fn difficulty_params(&self) -> eyre::Result { - let data = DifficultyFunction::data(&[])?; - Ok(rpc_params![ + fn difficulty_params(&self) -> ArrayParams { + let data: String = Capacity::difficultyCall {}.abi_encode().encode_hex(); + rpc_params![ json!({"data": data, "to": self.config.cc_contract_address}), "latest" - ]) + ] } - fn init_timestamp_params(&self) -> eyre::Result { - let data = InitTimestampFunction::data(&[])?; - Ok(rpc_params![ + fn init_timestamp_params(&self) -> ArrayParams { + let data: String = Core::initTimestampCall {}.abi_encode().encode_hex(); + rpc_params![ json!({"data": data, "to": self.config.core_contract_address}), "latest" - ]) + ] } - fn global_nonce_params(&self) -> eyre::Result { - let data = GetGlobalNonceFunction::data(&[])?; - Ok(rpc_params![ + fn global_nonce_params(&self) -> ArrayParams { + let data: String = Capacity::getGlobalNonceCall {}.abi_encode().encode_hex(); + rpc_params![ json!({"data": data, "to": self.config.cc_contract_address}), "latest" - ]) + ] } - fn current_epoch_params(&self) -> eyre::Result { - let data = CurrentEpochFunction::data(&[])?; - Ok(rpc_params![ + fn current_epoch_params(&self) -> ArrayParams { + let data: String = Core::currentEpochCall {}.abi_encode().encode_hex(); + rpc_params![ json!({"data": data, "to": self.config.core_contract_address}), "latest" - ]) + ] } - fn epoch_duration_params(&self) -> eyre::Result { - let data = EpochDurationFunction::data(&[])?; - Ok(rpc_params![ + fn epoch_duration_params(&self) -> ArrayParams { + let data: String = Core::epochDurationCall {}.abi_encode().encode_hex(); + rpc_params![ json!({"data": data, "to": self.config.core_contract_address}), "latest" - ]) + ] + } + + fn min_proofs_per_epoch_params(&self) -> ArrayParams { + let data: String = Capacity::minProofsPerEpochCall {}.abi_encode().encode_hex(); + rpc_params![ + json!({"data": data, "to": self.config.cc_contract_address}), + "latest" + ] + } + + fn max_proofs_per_epoch_params(&self) -> ArrayParams { + let data: String = Capacity::maxProofsPerEpochCall {}.abi_encode().encode_hex(); + rpc_params![ + json!({"data": data, "to": self.config.cc_contract_address}), + "latest" + ] } } #[cfg(test)] mod tests { + + use alloy_primitives::uint; + use alloy_primitives::U256; use std::assert_matches::assert_matches; use std::str::FromStr; use std::sync::Arc; @@ -490,17 +552,17 @@ mod tests { use serde_json::json; use chain_data::peer_id_from_hex; - use chain_types::{CommitmentId, COMMITMENT_IS_NOT_ACTIVE}; + use hex_utils::decode_hex; - use crate::{ChainConnector, ConnectorError}; + use crate::{is_commitment_not_active, CCStatus, ChainConnector, CommitmentId, ConnectorError}; fn get_connector(url: &str) -> Arc { let (connector, _) = ChainConnector::new( server_config::ChainConfig { http_endpoint: url.to_string(), - cc_contract_address: "0x8dc7d48492b9fD2519b65A54816be03758742c60".to_string(), - core_contract_address: "0x0B306BF915C4d645ff596e518fAf3F9669b97016".to_string(), - market_contract_address: "0x68B1D87F95878fE05B998F19b66F4baba5De1aed".to_string(), + cc_contract_address: "0xb2f922f9660200662f5B38B77e848c946AC8E426".to_string(), + core_contract_address: "0xDFfaD436cD2863245e5db3419955f90B7FB3E0d1".to_string(), + market_contract_address: "0x49a6C3b528f31D45F09b1E490539FBC0436bA462".to_string(), network_id: 3525067388221321, wallet_key: PrivateKey::from_str( "0x97a2456e78c4894c62eef6031972d1ca296ed40bf311ab54c231f13db59fc428", @@ -536,10 +598,10 @@ mod tests { mock.assert(); assert_eq!(units.len(), 2); - assert_eq!(units[0].start_epoch, 0.into()); - assert!(units[0].deal.is_none()); - assert_eq!(units[1].start_epoch, 0.into()); - assert!(units[1].deal.is_none()); + assert_eq!(units[0].startEpoch, U256::from(0)); + assert!(units[0].deal.is_zero()); + assert_eq!(units[1].startEpoch, U256::from(0)); + assert!(units[1].deal.is_zero()); } #[tokio::test] @@ -596,38 +658,6 @@ mod tests { ); } - #[tokio::test] - async fn test_get_commitment() { - let commitment_id = "0xa98dc43600773b162bcdb8175eadc037412cd7ad83555fafa507702011a53992"; - - let expected_data = "0x00000000000000000000000000000000000000000000000000000000000000016497db93b32e4cdd979ada46a23249f444da1efb186cd74b9666bd03f710028b000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012c00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; - let expected_response = - format!("{{\"jsonrpc\":\"2.0\",\"result\":\"{expected_data}\",\"id\":0}}"); - let mut server = mockito::Server::new(); - let url = server.url(); - let mock = server - .mock("POST", "/") - // expect exactly 1 POST request - .expect(1) - .with_status(200) - .with_header("content-type", "application/json") - .with_body(expected_response) - .create(); - let commitment_id = CommitmentId(hex::decode(&commitment_id[2..]).unwrap()); - let commitment = get_connector(&url) - .get_commitment(commitment_id) - .await - .unwrap(); - - mock.assert(); - assert_eq!( - commitment.status, - chain_types::CommitmentStatus::WaitDelegation - ); - assert_eq!(commitment.start_epoch, 0.into()); - assert_eq!(commitment.end_epoch, 300.into()); - } - #[tokio::test] async fn get_commitment_status() { let commitment_id = "0xa98dc43600773b162bcdb8175eadc037412cd7ad83555fafa507702011a53992"; @@ -648,14 +678,14 @@ mod tests { }))) .with_body(expected_response) .create(); - let commitment_id = CommitmentId(hex::decode(&commitment_id[2..]).unwrap()); + let commitment_id = CommitmentId(decode_hex(&commitment_id).unwrap().try_into().unwrap()); let status = get_connector(&url) .get_commitment_status(commitment_id) .await .unwrap(); mock.assert(); - assert_eq!(status, chain_types::CommitmentStatus::WaitDelegation); + assert_eq!(status, CCStatus::Active); } #[tokio::test] @@ -685,8 +715,19 @@ mod tests { "jsonrpc": "2.0", "result": "0x000000000000000000000000000000000000000000000000000000000000000f", "id": 4 + }, + { + "jsonrpc": "2.0", + "result": "0x5", + "id": 5 + }, + { + "jsonrpc": "2.0", + "result": "0x8", + "id": 6 } ]"#; + let mut server = mockito::Server::new(); let url = server.url(); let mock = server @@ -708,7 +749,7 @@ mod tests { ) .unwrap() ); - assert_eq!(init_params.init_timestamp, 1707760129.into()); + assert_eq!(init_params.init_timestamp, uint!(1707760129_U256)); assert_eq!( init_params.global_nonce, ::from_hex( @@ -718,12 +759,14 @@ mod tests { ); assert_eq!( init_params.current_epoch, - 0x00000000000000000000000000000000000000000000000000000000000016be.into() + U256::from(0x00000000000000000000000000000000000000000000000000000000000016be) ); assert_eq!( init_params.epoch_duration, - 0x000000000000000000000000000000000000000000000000000000000000000f.into() + U256::from(0x000000000000000000000000000000000000000000000000000000000000000f) ); + assert_eq!(init_params.min_proofs_per_epoch, U256::from(5)); + assert_eq!(init_params.max_proofs_per_epoch, U256::from(8)); } #[tokio::test] @@ -784,7 +827,7 @@ mod tests { code: _, message: _, data, - } if data.contains(COMMITMENT_IS_NOT_ACTIVE) + } if is_commitment_not_active(&data) ); } diff --git a/crates/chain-connector/src/error.rs b/crates/chain-connector/src/error.rs index 1d6356addc..bc3f987b8d 100644 --- a/crates/chain-connector/src/error.rs +++ b/crates/chain-connector/src/error.rs @@ -22,18 +22,20 @@ pub enum ConnectorError { }, #[error("Failed to parse chain data: {0}")] ParseChainDataFailed(#[from] ChainDataError), + #[error("Failed to parse chain data: {0}")] + ParseChainDataFailedAlloy(#[from] alloy_sol_types::Error), #[error("Failed to parse address: {0}")] AddressParseError(#[from] clarity::Error), #[error("data is not a valid hex: '{0}'")] DecodeHex(#[from] hex::FromHexError), + #[error(transparent)] + DecodeConstHex(#[from] const_hex::FromHexError), #[error("data is not a valid string: '{0}'")] DecodeData(#[from] FromUtf8Error), - #[error("Failed to parse baseFeePerGas: {0}")] - InvalidBaseFeePerGas(String), - #[error("Invalid transaction nonce: {0}")] - InvalidNonce(String), - #[error("Invalid gas limit: {0}")] - InvalidGasLimit(String), + #[error("Failed to parse u256, got: {0}, error: {1}")] + InvalidU256(String, String), + #[error("Failed to parse response: {0}")] + ResponseParseError(String), #[error("Parse error: {0}")] ParseError(#[from] serde_json::Error), } diff --git a/crates/chain-connector/src/function/capacity.rs b/crates/chain-connector/src/function/capacity.rs new file mode 100644 index 0000000000..a52761ec46 --- /dev/null +++ b/crates/chain-connector/src/function/capacity.rs @@ -0,0 +1,101 @@ +use alloy_sol_types::{sol, SolError}; +use serde::{Deserialize, Serialize}; +use std::fmt::{Display, Formatter}; + +sol! { + #[derive(PartialEq, Debug)] + enum CCStatus { + Inactive, + Active, + // WaitDelegation - before collateral is deposited. + WaitDelegation, + // Status is WaitStart - means collateral deposited, and epoch should be proceed before Active. + WaitStart, + Failed, + Removed + } + + + contract Capacity { + + /// @dev Throws if peer sent too many proofs for the commitment by unit per epoch + error TooManyProofs(); + + /// @dev Capacity commitment is not active + error CapacityCommitmentIsNotActive(CCStatus status); + + /// @dev Returns the difficulty for randomX + function difficulty() external view returns (bytes32); + function getGlobalNonce() external view returns (bytes32); + + /// @dev Returns the min required randomX proofs per epoch for the 1 CU. + /// @dev If lower than this - CU is failed and CC slashed. + function minProofsPerEpoch() external view returns (uint256); + + /// @dev Returns the max randomX proofs per epoch + function maxProofsPerEpoch() external view returns (uint256); + + /// @dev Returns the commitment status + /// @param commitmentId Commitment id + /// @return status commitment status + function getStatus(bytes32 commitmentId) external view returns (CCStatus); + + /// @dev Submits a proof for the commitment + /// @param unitId Compute unit id which provied the proof + /// @param localUnitNonce The local nonce of the unit for calculating the target hash. It's the proof + /// @param resultHash The target hash of this proof + function submitProof(bytes32 unitId, bytes32 localUnitNonce, bytes32 resultHash) external; + } +} + +pub fn is_too_many_proofs(data: &str) -> bool { + data.contains(&hex::encode(Capacity::TooManyProofs::SELECTOR)) +} + +pub fn is_commitment_not_active(data: &str) -> bool { + data.contains(&hex::encode( + Capacity::CapacityCommitmentIsNotActive::SELECTOR, + )) +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct CommitmentId(pub [u8; 32]); + +impl CommitmentId { + pub fn new(data: [u8; 32]) -> Option { + if data.iter().all(|&x| x == 0) { + None + } else { + Some(Self(data)) + } + } +} + +impl Display for CommitmentId { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", hex::encode(self.0)) + } +} +#[cfg(test)] +mod tests { + + use crate::function::capacity::CCStatus; + use alloy_sol_types::SolType; + + #[tokio::test] + async fn decode_commitment_status() { + let data = "0000000000000000000000000000000000000000000000000000000000000001"; + let status: CCStatus = CCStatus::abi_decode(&hex::decode(data).unwrap(), true).unwrap(); + + assert_eq!(format!("{:?}", status), "Active"); + assert_eq!(status, CCStatus::Active); + } + + #[tokio::test] + async fn decode_commitment_status_removed() { + let data = "0000000000000000000000000000000000000000000000000000000000000005"; + let status = CCStatus::abi_decode(&hex::decode(data).unwrap(), true).unwrap(); + + assert_eq!(status, super::CCStatus::Removed); + } +} diff --git a/crates/chain-connector/src/function/core.rs b/crates/chain-connector/src/function/core.rs new file mode 100644 index 0000000000..d3eb79e148 --- /dev/null +++ b/crates/chain-connector/src/function/core.rs @@ -0,0 +1,20 @@ +use alloy_sol_types::sol; +sol! { + contract Core { + function capacity() external view returns (address); + + function market() external view returns (address); + + /// @dev Returns current epoch + /// @return current epoch number + function currentEpoch() external view returns (uint256); + + /// @dev Returns epoch duration + /// @return epochDuration in seconds + function epochDuration() external view returns (uint256); + + /// @dev Returns epoch init timestamp + /// @return initTimestamp in seconds + function initTimestamp() external view returns (uint256); + } +} diff --git a/crates/chain-connector/src/function/current_epoch.rs b/crates/chain-connector/src/function/current_epoch.rs deleted file mode 100644 index 1a441acb03..0000000000 --- a/crates/chain-connector/src/function/current_epoch.rs +++ /dev/null @@ -1,23 +0,0 @@ -use chain_data::ChainFunction; -use ethabi::{Function, ParamType, StateMutability}; - -/// @dev Returns current epoch -/// @return current epoch number -/// function currentEpoch() external view returns (uint256); -pub struct CurrentEpochFunction; -impl ChainFunction for CurrentEpochFunction { - fn function() -> Function { - #[allow(deprecated)] - Function { - name: "currentEpoch".to_string(), - inputs: vec![], - outputs: vec![], - constant: None, - state_mutability: StateMutability::View, - } - } - - fn result_signature() -> Vec { - vec![ParamType::Uint(256)] - } -} diff --git a/crates/chain-connector/src/function/deal.rs b/crates/chain-connector/src/function/deal.rs new file mode 100644 index 0000000000..46a713dfd1 --- /dev/null +++ b/crates/chain-connector/src/function/deal.rs @@ -0,0 +1,20 @@ +use alloy_sol_types::sol; +sol! { + contract Deal { + #[derive(Debug)] + enum Status { + // the deal does have enough funds to pay for the workers + INSUFFICIENT_FUNDS, + ACTIVE, + // the deal is stopped + ENDED, + // the deal has a balance and waiting for workers + NOT_ENOUGH_WORKERS, + // the deal has balance less than the minimal balance. Min balance: 2 * targetWorkers * pricePerWorkerEpoch + SMALL_BALANCE + } + + /// @dev Returns the status of the deal + function getStatus() external view returns (Status); + } +} diff --git a/crates/chain-connector/src/function/difficulty.rs b/crates/chain-connector/src/function/difficulty.rs deleted file mode 100644 index 4fc5efb306..0000000000 --- a/crates/chain-connector/src/function/difficulty.rs +++ /dev/null @@ -1,21 +0,0 @@ -use chain_data::ChainFunction; -use ethabi::{Function, ParamType, StateMutability}; - -/// function difficulty() external view returns (bytes32); -pub struct DifficultyFunction; -impl ChainFunction for DifficultyFunction { - fn function() -> Function { - #[allow(deprecated)] - Function { - name: "difficulty".to_string(), - inputs: vec![], - outputs: vec![], - constant: None, - state_mutability: StateMutability::View, - } - } - - fn result_signature() -> Vec { - vec![ParamType::FixedBytes(32)] - } -} diff --git a/crates/chain-connector/src/function/epoch_duration.rs b/crates/chain-connector/src/function/epoch_duration.rs deleted file mode 100644 index 91ca14783a..0000000000 --- a/crates/chain-connector/src/function/epoch_duration.rs +++ /dev/null @@ -1,24 +0,0 @@ -use chain_data::ChainFunction; -use ethabi::{Function, ParamType, StateMutability}; - -/// @dev Returns epoch duration -/// @return epochDuration in seconds -/// function epochDuration() external view returns (uint256); -pub struct EpochDurationFunction; - -impl ChainFunction for EpochDurationFunction { - fn function() -> Function { - #[allow(deprecated)] - Function { - name: "epochDuration".to_string(), - inputs: vec![], - outputs: vec![], - constant: None, - state_mutability: StateMutability::View, - } - } - - fn result_signature() -> Vec { - vec![ParamType::Uint(256)] - } -} diff --git a/crates/chain-connector/src/function/get_commitment.rs b/crates/chain-connector/src/function/get_commitment.rs deleted file mode 100644 index 3024f184b1..0000000000 --- a/crates/chain-connector/src/function/get_commitment.rs +++ /dev/null @@ -1,28 +0,0 @@ -use chain_data::ChainFunction; -use chain_types::Commitment; - -/// @dev Returns the commitment info -/// @param commitmentId Commitment id -/// @return info commitment info -/// function getCommitment(bytes32 commitmentId) external view returns (CommitmentView memory); -pub struct GetCommitmentFunction; - -impl ChainFunction for GetCommitmentFunction { - fn function() -> ethabi::Function { - #[allow(deprecated)] - ethabi::Function { - name: "getCommitment".to_string(), - inputs: vec![ethabi::Param { - name: "commitmentId".to_string(), - kind: ethabi::ParamType::FixedBytes(32), - internal_type: None, - }], - outputs: vec![], - constant: None, - state_mutability: ethabi::StateMutability::View, - } - } - fn result_signature() -> Vec { - Commitment::signature() - } -} diff --git a/crates/chain-connector/src/function/get_commitment_status.rs b/crates/chain-connector/src/function/get_commitment_status.rs deleted file mode 100644 index ae9303f794..0000000000 --- a/crates/chain-connector/src/function/get_commitment_status.rs +++ /dev/null @@ -1,29 +0,0 @@ -use chain_data::ChainFunction; - -/// @dev Returns the commitment status -/// @param commitmentId Commitment id -/// @return status commitment status -/// function getStatus(bytes32 commitmentId) external view returns (CCStatus); -pub struct GetCommitmentStatusFunction; - -impl ChainFunction for GetCommitmentStatusFunction { - fn function() -> ethabi::Function { - #[allow(deprecated)] - let function = ethabi::Function { - name: "getStatus".to_string(), - inputs: vec![ethabi::Param { - name: "commitmentId".to_string(), - kind: ethabi::ParamType::FixedBytes(32), - internal_type: None, - }], - outputs: vec![], - constant: None, - state_mutability: ethabi::StateMutability::View, - }; - function - } - - fn result_signature() -> Vec { - vec![ethabi::ParamType::FixedBytes(32)] - } -} diff --git a/crates/chain-connector/src/function/get_compute_peer.rs b/crates/chain-connector/src/function/get_compute_peer.rs deleted file mode 100644 index 761c57991d..0000000000 --- a/crates/chain-connector/src/function/get_compute_peer.rs +++ /dev/null @@ -1,53 +0,0 @@ -use chain_data::ChainFunction; -use chain_types::ComputePeer; -use ethabi::{Function, Param, ParamType, StateMutability}; - -/// struct ComputePeer { -/// bytes32 offerId; -/// bytes32 commitmentId; -/// uint256 unitCount; -/// address owner; -/// } -/// @dev Returns the compute peer info -/// function getComputePeer(bytes32 peerId) external view returns (ComputePeer memory); -pub struct GetComputePeerFunction; - -impl ChainFunction for GetComputePeerFunction { - fn function() -> Function { - #[allow(deprecated)] - Function { - name: "getComputePeer".to_string(), - inputs: vec![Param { - name: String::from("peerId"), - kind: ParamType::FixedBytes(32), - internal_type: None, - }], - outputs: vec![], - constant: None, - state_mutability: StateMutability::View, - } - } - - fn result_signature() -> Vec { - ComputePeer::signature() - } -} - -#[cfg(test)] -mod tests { - use crate::GetComputePeerFunction; - use chain_data::ChainFunction; - - #[tokio::test] - async fn test_data() { - let peer_id = "0x6497db93b32e4cdd979ada46a23249f444da1efb186cd74b9666bd03f710028b"; - let data = GetComputePeerFunction::data(&[ethabi::Token::FixedBytes( - hex::decode(&peer_id[2..]).unwrap(), - )]) - .unwrap(); - assert_eq!( - data, - "0x86e682546497db93b32e4cdd979ada46a23249f444da1efb186cd74b9666bd03f710028b" - ); - } -} diff --git a/crates/chain-connector/src/function/get_compute_units.rs b/crates/chain-connector/src/function/get_compute_units.rs deleted file mode 100644 index ce95fbf5cc..0000000000 --- a/crates/chain-connector/src/function/get_compute_units.rs +++ /dev/null @@ -1,48 +0,0 @@ -use chain_data::ChainFunction; -use chain_types::ComputeUnit; -use ethabi::ParamType; - -/// @dev Returns the compute units info of a peer -/// function getComputeUnits(bytes32 peerId) external view returns (ComputeUnitView[] memory); -pub struct GetComputeUnitsFunction; - -impl ChainFunction for GetComputeUnitsFunction { - fn function() -> ethabi::Function { - #[allow(deprecated)] - ethabi::Function { - name: "getComputeUnits".to_string(), - inputs: vec![ethabi::Param { - name: "peerId".to_string(), - kind: ParamType::FixedBytes(32), - internal_type: None, - }], - outputs: vec![], - constant: None, - state_mutability: ethabi::StateMutability::View, - } - } - fn result_signature() -> Vec { - vec![ParamType::Array(Box::new(ParamType::Tuple( - ComputeUnit::signature(), - )))] - } -} - -#[cfg(test)] -mod tests { - use crate::GetComputeUnitsFunction; - use chain_data::ChainFunction; - - #[tokio::test] - async fn test_data() { - let peer_id = "0x6497db93b32e4cdd979ada46a23249f444da1efb186cd74b9666bd03f710028b"; - let data = GetComputeUnitsFunction::data(&[ethabi::Token::FixedBytes( - hex::decode(&peer_id[2..]).unwrap(), - )]) - .unwrap(); - assert_eq!( - data, - "0xb6015c6e6497db93b32e4cdd979ada46a23249f444da1efb186cd74b9666bd03f710028b" - ); - } -} diff --git a/crates/chain-connector/src/function/get_deal_status.rs b/crates/chain-connector/src/function/get_deal_status.rs deleted file mode 100644 index 7d0f5eb31a..0000000000 --- a/crates/chain-connector/src/function/get_deal_status.rs +++ /dev/null @@ -1,23 +0,0 @@ -use chain_data::ChainFunction; -use ethabi::ParamType; - -/// function getStatus() public view returns (Status) -pub struct GetDealStatusFunction; - -impl ChainFunction for GetDealStatusFunction { - fn function() -> ethabi::Function { - #[allow(deprecated)] - let function = ethabi::Function { - name: "getStatus".to_string(), - inputs: vec![], - outputs: vec![], - constant: None, - state_mutability: ethabi::StateMutability::View, - }; - function - } - - fn result_signature() -> Vec { - vec![ParamType::FixedBytes(32)] - } -} diff --git a/crates/chain-connector/src/function/global_nonce.rs b/crates/chain-connector/src/function/global_nonce.rs deleted file mode 100644 index 240e4bfad1..0000000000 --- a/crates/chain-connector/src/function/global_nonce.rs +++ /dev/null @@ -1,23 +0,0 @@ -use chain_data::ChainFunction; -use ethabi::{Function, ParamType, StateMutability}; - -/// function getGlobalNonce() external view returns (bytes32); - -pub struct GetGlobalNonceFunction; - -impl ChainFunction for GetGlobalNonceFunction { - fn function() -> Function { - #[allow(deprecated)] - Function { - name: "getGlobalNonce".to_string(), - inputs: vec![], - outputs: vec![], - constant: None, - state_mutability: StateMutability::View, - } - } - - fn result_signature() -> Vec { - vec![ParamType::FixedBytes(32)] - } -} diff --git a/crates/chain-connector/src/function/init_timestamp.rs b/crates/chain-connector/src/function/init_timestamp.rs deleted file mode 100644 index 3dcd2d5642..0000000000 --- a/crates/chain-connector/src/function/init_timestamp.rs +++ /dev/null @@ -1,26 +0,0 @@ -use chain_data::ChainFunction; -use ethabi::{Function, ParamType, StateMutability}; - -/// @dev Returns epoch init timestamp -/// @return initTimestamp in seconds -/// function initTimestamp() external view returns (uint256); -/// - -pub struct InitTimestampFunction; - -impl ChainFunction for InitTimestampFunction { - fn function() -> Function { - #[allow(deprecated)] - Function { - name: "initTimestamp".to_string(), - inputs: vec![], - outputs: vec![], - constant: None, - state_mutability: StateMutability::View, - } - } - - fn result_signature() -> Vec { - vec![ParamType::Uint(256)] - } -} diff --git a/crates/chain-connector/src/function/mod.rs b/crates/chain-connector/src/function/mod.rs index 0e3c6ddc48..234292af1c 100644 --- a/crates/chain-connector/src/function/mod.rs +++ b/crates/chain-connector/src/function/mod.rs @@ -1,25 +1,9 @@ -mod current_epoch; -mod difficulty; -mod epoch_duration; -mod get_commitment; -mod get_commitment_status; -mod get_compute_peer; -mod get_compute_units; -mod get_deal_status; -mod global_nonce; -mod init_timestamp; -mod remove_cu_from_deal; -mod submit_proof; +mod capacity; +mod core; +mod deal; +mod offer; -pub use current_epoch::CurrentEpochFunction; -pub use difficulty::DifficultyFunction; -pub use epoch_duration::EpochDurationFunction; -pub use get_commitment::GetCommitmentFunction; -pub use get_commitment_status::GetCommitmentStatusFunction; -pub use get_compute_peer::GetComputePeerFunction; -pub use get_compute_units::GetComputeUnitsFunction; -pub use get_deal_status::GetDealStatusFunction; -pub use global_nonce::GetGlobalNonceFunction; -pub use init_timestamp::InitTimestampFunction; -pub use remove_cu_from_deal::ReturnComputeUnitFromDeal; -pub use submit_proof::SubmitProofFunction; +pub use capacity::*; +pub use core::*; +pub use deal::*; +pub use offer::*; diff --git a/crates/chain-connector/src/function/offer.rs b/crates/chain-connector/src/function/offer.rs new file mode 100644 index 0000000000..e679b7a059 --- /dev/null +++ b/crates/chain-connector/src/function/offer.rs @@ -0,0 +1,133 @@ +use crate::Offer::ComputeUnit; +use alloy_primitives::U256; +use alloy_sol_types::sol; +use ccp_shared::types::CUID; + +sol! { + contract Offer { + struct ComputePeer { + bytes32 offerId; + bytes32 commitmentId; + uint256 unitCount; + address owner; + } + + struct ComputeUnit { + bytes32 id; + address deal; + uint256 startEpoch; + } + + /// @dev Returns the compute peer info + function getComputePeer(bytes32 peerId) external view returns (ComputePeer memory); + /// @dev Returns the compute units info of a peer + function getComputeUnits(bytes32 peerId) external view returns (ComputeUnit[] memory); + + /// @dev Return the compute unit from a deal + function returnComputeUnitFromDeal(bytes32 unitId) external; + } +} + +/// "Peer doesn't exists" in Market.sol +pub const PEER_NOT_EXISTS: &str = "08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000125065657220646f65736e27742065786973740000000000000000000000000000"; + +#[derive(Debug, PartialEq, Eq, Hash, Ord, PartialOrd)] +pub struct PendingUnit { + pub id: CUID, + pub start_epoch: U256, +} + +impl PendingUnit { + pub fn new(id: CUID, start_epoch: U256) -> Self { + Self { id, start_epoch } + } +} +impl From for PendingUnit { + fn from(unit: ComputeUnit) -> Self { + Self { + id: CUID::new(unit.id.0), + start_epoch: unit.startEpoch, + } + } +} + +#[cfg(test)] +mod tests { + use crate::Offer::ComputePeer; + use alloy_primitives::{hex, U256}; + use alloy_sol_types::SolType; + use hex_utils::decode_hex; + + #[tokio::test] + async fn decode_compute_unit() { + let data = "aa3046a12a1aac6e840625e6329d70b427328fec36dc8d273e5e6454b85633d50000000000000000000000005e3d0fde6f793b3115a9e7f5ebc195bbeed35d6c00000000000000000000000000000000000000000000000000000000000003e8"; + let compute_unit = super::ComputeUnit::abi_decode(&decode_hex(&data).unwrap(), true); + assert!(compute_unit.is_ok()); + let compute_unit = compute_unit.unwrap(); + + assert_eq!( + compute_unit.id, + hex!("aa3046a12a1aac6e840625e6329d70b427328fec36dc8d273e5e6454b85633d5") + ); + assert!(!compute_unit.deal.is_zero()); + assert_eq!( + compute_unit.deal.to_string().to_lowercase(), + "0x5e3d0fde6f793b3115a9e7f5ebc195bbeed35d6c" + ); + assert_eq!(compute_unit.startEpoch, U256::from(1000)); + } + + #[tokio::test] + async fn decode_compute_unit_no_deal() { + let data = "aa3046a12a1aac6e840625e6329d70b427328fec36dc8d273e5e6454b85633d5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003e8"; + let compute_unit = super::ComputeUnit::abi_decode(&decode_hex(&data).unwrap(), true); + assert!(compute_unit.is_ok()); + let compute_unit = compute_unit.unwrap(); + assert_eq!( + compute_unit.id, + hex!("aa3046a12a1aac6e840625e6329d70b427328fec36dc8d273e5e6454b85633d5") + ); + assert!(compute_unit.deal.is_zero()); + assert_eq!(compute_unit.startEpoch, U256::from(1000)); + } + + #[tokio::test] + async fn decode_compute_peer_no_commitment() { + let data = "0xaa3046a12a1aac6e840625e6329d70b427328fec36dc8d273e5e6454b85633d5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000005b73c5498c1e3b4dba84de0f1833c4a029d90519"; + let compute_peer = ComputePeer::abi_decode(&decode_hex(&data).unwrap(), true); + assert!(compute_peer.is_ok()); + let compute_peer = compute_peer.unwrap(); + assert_eq!( + hex::encode(compute_peer.offerId), + "aa3046a12a1aac6e840625e6329d70b427328fec36dc8d273e5e6454b85633d5" + ); + assert!(compute_peer.commitmentId.is_zero()); + assert_eq!(compute_peer.unitCount, U256::from(2)); + assert_eq!( + compute_peer.owner.to_string().to_lowercase(), + "0x5b73c5498c1e3b4dba84de0f1833c4a029d90519" + ); + } + + #[tokio::test] + async fn decode_compute_peer() { + let data = "0xaa3046a12a1aac6e840625e6329d70b427328fec36dc8d273e5e6454b85633d5aa3046a12a1aac6e840625e6329d70b427328feceedc8d273e5e6454b85633b5000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000005b73c5498c1e3b4dba84de0f1833c4a029d90519"; + let compute_peer = ComputePeer::abi_decode(&decode_hex(&data).unwrap(), true); + assert!(compute_peer.is_ok()); + let compute_peer = compute_peer.unwrap(); + assert_eq!( + hex::encode(compute_peer.offerId), + "aa3046a12a1aac6e840625e6329d70b427328fec36dc8d273e5e6454b85633d5" + ); + assert!(!compute_peer.commitmentId.is_zero()); + assert_eq!( + hex::encode(compute_peer.commitmentId.0), + "aa3046a12a1aac6e840625e6329d70b427328feceedc8d273e5e6454b85633b5" + ); + assert_eq!(compute_peer.unitCount, U256::from(10)); + assert_eq!( + compute_peer.owner.to_string().to_lowercase(), + "0x5b73c5498c1e3b4dba84de0f1833c4a029d90519" + ); + } +} diff --git a/crates/chain-connector/src/function/remove_cu_from_deal.rs b/crates/chain-connector/src/function/remove_cu_from_deal.rs deleted file mode 100644 index 4a974091c8..0000000000 --- a/crates/chain-connector/src/function/remove_cu_from_deal.rs +++ /dev/null @@ -1,27 +0,0 @@ -use chain_data::ChainFunction; -use ethabi::{Function, Param, ParamType, StateMutability}; - -/// @dev Return the compute unit from a deal -/// function returnComputeUnitFromDeal(bytes32 unitId) external; -pub struct ReturnComputeUnitFromDeal; - -impl ChainFunction for ReturnComputeUnitFromDeal { - fn function() -> Function { - #[allow(deprecated)] - Function { - name: "returnComputeUnitFromDeal".to_string(), - inputs: vec![Param { - name: "unitId".to_string(), - kind: ParamType::FixedBytes(32), - internal_type: None, - }], - outputs: vec![], - constant: None, - state_mutability: StateMutability::NonPayable, - } - } - - fn result_signature() -> Vec { - vec![] - } -} diff --git a/crates/chain-connector/src/function/submit_proof.rs b/crates/chain-connector/src/function/submit_proof.rs deleted file mode 100644 index 76f52f4b70..0000000000 --- a/crates/chain-connector/src/function/submit_proof.rs +++ /dev/null @@ -1,48 +0,0 @@ -use chain_data::ChainFunction; - -/// @dev Submits a proof for the commitment -/// @param unitId Compute unit id which provied the proof -/// @param globalUnitNonce The global nonce of the unit for calculating the target hash -/// @param localUnitNonce The local nonce of the unit for calculating the target hash. It's the proof -/// @param targetHash The target hash of this proof -/// function submitProof(bytes32 unitId, bytes32 globalUnitNonce, bytes32 localUnitNonce, bytes32 targetHash) external; - -pub struct SubmitProofFunction; - -impl ChainFunction for SubmitProofFunction { - fn function() -> ethabi::Function { - #[allow(deprecated)] - let function = ethabi::Function { - name: "submitProof".to_string(), - inputs: vec![ - ethabi::Param { - name: "unitId".to_string(), - kind: ethabi::ParamType::FixedBytes(32), - internal_type: None, - }, - ethabi::Param { - name: "localUnitNonce".to_string(), - kind: ethabi::ParamType::FixedBytes(32), - internal_type: None, - }, - ethabi::Param { - name: "targetHash".to_string(), - kind: ethabi::ParamType::FixedBytes(32), - internal_type: None, - }, - ], - outputs: vec![], - constant: None, - state_mutability: ethabi::StateMutability::NonPayable, - }; - function - } - - fn result_signature() -> Vec { - vec![ - ethabi::ParamType::FixedBytes(32), - ethabi::ParamType::FixedBytes(32), - ethabi::ParamType::FixedBytes(32), - ] - } -} diff --git a/crates/chain-connector/src/lib.rs b/crates/chain-connector/src/lib.rs index 4ea2e96cf6..7e7503123a 100644 --- a/crates/chain-connector/src/lib.rs +++ b/crates/chain-connector/src/lib.rs @@ -1,4 +1,5 @@ #![feature(assert_matches)] +#![feature(result_flattening)] mod connector; mod error; diff --git a/crates/chain-data/Cargo.toml b/crates/chain-data/Cargo.toml index 1a0c3734d2..7c21a014c9 100644 --- a/crates/chain-data/Cargo.toml +++ b/crates/chain-data/Cargo.toml @@ -14,3 +14,6 @@ hex = { workspace = true } log = { workspace = true } thiserror = { workspace = true } eyre = { workspace = true } +alloy-sol-types = { workspace = true } +alloy_serde_macro = { workspace = true } +const-hex = { workspace = true } \ No newline at end of file diff --git a/crates/chain-data/src/function.rs b/crates/chain-data/src/function.rs deleted file mode 100644 index 4581f5a4a3..0000000000 --- a/crates/chain-data/src/function.rs +++ /dev/null @@ -1,33 +0,0 @@ -use crate::{next_opt, ChainDataError}; -use ethabi::ethereum_types::U256; -use ethabi::Token; - -pub trait ChainFunction { - fn function() -> ethabi::Function; - fn result_signature() -> Vec; - - fn data(inputs: &[Token]) -> Result { - let function = Self::function(); - let data = function.encode_input(inputs)?; - Ok(format!("0x{}", hex::encode(data))) - } - - fn data_bytes(inputs: &[Token]) -> Result, ChainDataError> { - let function = Self::function(); - Ok(function.encode_input(inputs)?) - } - - fn decode_uint(data: &str) -> Result { - let mut tokens = crate::parse_chain_data(data, &Self::result_signature())?.into_iter(); - next_opt(&mut tokens, "uint", Token::into_uint) - } - - fn decode_fixed_bytes(data: &str) -> Result, ChainDataError> { - let mut tokens = crate::parse_chain_data(data, &Self::result_signature())?.into_iter(); - next_opt(&mut tokens, "bytes", Token::into_fixed_bytes) - } - - fn decode_tuple(data: &str) -> Result, ChainDataError> { - crate::parse_chain_data(data, &Self::result_signature()) - } -} diff --git a/crates/chain-data/src/lib.rs b/crates/chain-data/src/lib.rs index 456a55c168..0d57097d0c 100644 --- a/crates/chain-data/src/lib.rs +++ b/crates/chain-data/src/lib.rs @@ -1,16 +1,14 @@ #![feature(try_blocks)] +#![feature(slice_as_chunks)] + mod chain_data; mod data_tokens; mod error; -mod function; mod log; -mod u256; mod utils; pub use chain_data::{parse_chain_data, ChainData, ChainEvent, EventField}; pub use data_tokens::{next, next_opt}; pub use error::ChainDataError; -pub use function::ChainFunction; pub use log::{parse_log, Log, LogParseError}; -pub use u256::U256; pub use utils::{parse_peer_id, peer_id_from_hex, peer_id_to_bytes, peer_id_to_hex}; diff --git a/crates/chain-data/src/log.rs b/crates/chain-data/src/log.rs index 677570f440..7fcf15dcb3 100644 --- a/crates/chain-data/src/log.rs +++ b/crates/chain-data/src/log.rs @@ -1,11 +1,13 @@ +use alloy_sol_types::{SolEvent, Word}; +use hex_utils::decode_hex; use libp2p_identity::ParseError; use serde::{Deserialize, Serialize}; +use std::str::FromStr; use thiserror::Error; -use crate::chain_data::EventField::{Indexed, NotIndexed}; -use crate::chain_data::{parse_chain_data, ChainData, ChainEvent, EventField}; +use crate::chain_data::EventField; use crate::error::ChainDataError; -use crate::log::LogParseError::{MissingToken, MissingTopic}; +use crate::LogParseError::EthError; #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(rename_all = "camelCase")] @@ -23,11 +25,11 @@ pub struct Log { #[derive(Debug, Error)] pub enum LogParseError { #[error(transparent)] - EthError(#[from] ethabi::Error), + EthError(#[from] alloy_sol_types::Error), #[error(transparent)] DecodeHex(#[from] hex::FromHexError), - #[error("parsed data doesn't correspond to the expected signature: {0:?}")] - SignatureMismatch(Vec), + #[error(transparent)] + DecodeConstHex(#[from] const_hex::FromHexError), #[error( "incorrect log signature: not found token for field #{position} of type ${event_field:?}" )] @@ -52,82 +54,10 @@ pub enum LogParseError { NoTokens, } -/// Parse Event Log to specified DTO -/// -/// Logs consist of data fields, much like ADT. Fields can indexed and not indexed. -/// -/// Data for indexed fields is encoded in 'log.topics', starting from 1th topic, i.e. 0th is skipped -/// Data for non indexed fields is encoded in 'log.data'. -/// -/// Indexed and non indexed data fields can be interleaved. -/// That forces a certain parsing scheme, which is implemented below. -pub fn parse_log>(log: Log) -> Result { - log::debug!("Parse log from block {:?}", log.block_number); - let result: Result<_, LogParseError> = try { - // event log signature, i.e. data field types - let signature = U::signature(); - // gather data types for non indexed ("indexless") fields - let indexless = signature - .clone() - .into_iter() - .filter_map(|t| match t { - NotIndexed(t) => Some(t), - Indexed(_) => None, - }) - .collect::>(); - // parse all non indexed fields to tokens - let indexless = parse_chain_data(&log.data, &indexless)?; - - // iterate through data field types (signature), and take - // data `Token` from either 'indexless' or 'topics' - let mut indexless = indexless.into_iter(); - // skip first topic, because it contains actual topic, and not indexed data field - let mut topics = log.topics.into_iter().skip(1); - // accumulate tokens here - let mut tokens = vec![]; - for (position, event_field) in signature.into_iter().enumerate() { - match event_field { - NotIndexed(_) => { - // take next token for non indexed data field - let token = indexless.next().ok_or(MissingToken { - position, - event_field, - })?; - tokens.push(token); - } - ef @ Indexed(_) => { - let topic = topics.next().ok_or(MissingTopic { - position, - event_field: ef.clone(), - })?; - // parse indexed field to token one by one - let parsed = parse_chain_data(&topic, &[ef.clone().param_type()])?; - debug_assert!(parsed.len() == 1, "parse of an indexed event fields yielded several tokens, expected a single one"); - let token = parsed.into_iter().next().ok_or(MissingToken { - position, - event_field: ef, - })?; - tokens.push(token) - } - } - } - - if tokens.is_empty() { - return Err(LogParseError::NoTokens); - } - - let block_number = log.block_number.clone(); - let log = U::parse(&mut tokens.into_iter())?; - T::new(block_number, log) - }; - - if let Err(e) = result.as_ref() { - log::warn!(target: "connector", - "Cannot parse deal log from block {}: {:?}", - log.block_number, - e.to_string() - ); +pub fn parse_log(log: Log) -> Result { + let mut topics = vec![]; + for t in log.topics { + topics.push(Word::from_str(&t)?); } - - result + SolEvent::decode_raw_log(topics.iter(), &decode_hex(&log.data)?, true).map_err(EthError) } diff --git a/crates/chain-data/src/u256.rs b/crates/chain-data/src/u256.rs deleted file mode 100644 index db0be96914..0000000000 --- a/crates/chain-data/src/u256.rs +++ /dev/null @@ -1,36 +0,0 @@ -use ethabi::Token; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] -pub struct U256 { - bytes: Vec, -} - -impl U256 { - pub fn from_bytes(bs: &[u8; 32]) -> Self { - U256 { bytes: bs.to_vec() } - } - - pub fn to_eth(&self) -> ethabi::ethereum_types::U256 { - ethabi::ethereum_types::U256::from_little_endian(&self.bytes) - } - - pub fn to_u64_trunc(&self) -> u64 { - let mut bytes = [0u8; 8]; - bytes.copy_from_slice(&self.bytes[0..8]); - u64::from_le_bytes(bytes) - } - - pub fn from_eth(num: ethabi::ethereum_types::U256) -> U256 { - let bytes = num - .0 - .iter() - .flat_map(|x| x.to_le_bytes()) - .collect::>(); - U256 { bytes } - } - - pub fn from_token(token: Token) -> Option { - token.into_uint().map(Self::from_eth) - } -} diff --git a/crates/chain-data/src/utils.rs b/crates/chain-data/src/utils.rs index a4b7758781..b16dd196fa 100644 --- a/crates/chain-data/src/utils.rs +++ b/crates/chain-data/src/utils.rs @@ -10,9 +10,11 @@ pub fn parse_peer_id(bytes: Vec) -> Result { PeerId::from_bytes(&peer_id) } -pub fn peer_id_to_bytes(peer_id: PeerId) -> Vec { +pub fn peer_id_to_bytes(peer_id: PeerId) -> [u8; 32] { let peer_id = peer_id.to_bytes(); - peer_id[PEER_ID_PREFIX.len()..].to_vec() + // peer_id is 38 bytes but we need 32 for chain + let res = peer_id[PEER_ID_PREFIX.len()..].as_chunks::<32>(); + res.0[0] } pub fn peer_id_to_hex(peer_id: PeerId) -> String { format!("0x{:0>64}", hex::encode(peer_id_to_bytes(peer_id))) diff --git a/crates/chain-listener/Cargo.toml b/crates/chain-listener/Cargo.toml index 2f4b3b459b..10ec32b1fb 100644 --- a/crates/chain-listener/Cargo.toml +++ b/crates/chain-listener/Cargo.toml @@ -9,7 +9,6 @@ edition = "2021" ethabi = { workspace = true } thiserror = { workspace = true } chain-data = { workspace = true } -chain-types = { workspace = true } chain-connector = { workspace = true } jsonrpsee = { workspace = true, features = ["ws-client", "macros", "server"] } @@ -26,6 +25,10 @@ server-config = { workspace = true } types = { workspace = true } libipld = "0.16.0" +alloy-sol-types = { workspace = true } +alloy-primitives = { workspace = true } +alloy_serde_macro = { workspace = true } + fluence-libp2p = { workspace = true } fs-utils = { workspace = true } futures = { workspace = true } diff --git a/crates/chain-listener/src/event/cc_activated.rs b/crates/chain-listener/src/event/cc_activated.rs index 7edcab4c78..33536022b3 100644 --- a/crates/chain-listener/src/event/cc_activated.rs +++ b/crates/chain-listener/src/event/cc_activated.rs @@ -1,121 +1,34 @@ -use chain_data::ChainDataError::InvalidTokenSize; -use chain_data::EventField::{Indexed, NotIndexed}; -use chain_data::{next_opt, parse_peer_id, ChainData, ChainDataError, ChainEvent, EventField}; -use chain_types::CommitmentId; -use core_manager::CUID; -use ethabi::ethereum_types::U256; -use ethabi::param_type::ParamType; -use ethabi::Token; -use libp2p_identity::PeerId; -use serde::{Deserialize, Serialize}; -use types::peer_id; - -/// @dev Emitted when a commitment is activated. Commitment can be activated only if delegator deposited collateral. -/// @param peerId Peer id which linked to the commitment -/// @param commitmentId Commitment id which activated -/// @param startEpoch The start epoch of the commitment -/// @param endEpoch The end epoch of the commitment -/// @param unitIds Compute unit ids which linked to the commitment -/// event CommitmentActivated( -/// bytes32 indexed peerId, -/// bytes32 indexed commitmentId, -/// uint256 startEpoch, -/// uint256 endEpoch, -/// bytes32[] unitIds -/// ); -#[derive(Debug, Serialize, Deserialize)] -pub struct CommitmentActivatedData { - #[serde( - serialize_with = "peer_id::serde::serialize", - deserialize_with = "peer_id::serde::deserialize" - )] - pub peer_id: PeerId, - pub commitment_id: CommitmentId, - pub start_epoch: U256, - pub end_epoch: U256, - pub unit_ids: Vec, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct CommitmentActivated { - pub block_number: String, - pub info: CommitmentActivatedData, -} - -impl CommitmentActivated { - pub const EVENT_NAME: &'static str = "CommitmentActivated"; -} - -impl ChainData for CommitmentActivatedData { - fn event_name() -> &'static str { - CommitmentActivated::EVENT_NAME - } - - fn signature() -> Vec { - vec![ - Indexed(ParamType::FixedBytes(32)), // peerId - Indexed(ParamType::FixedBytes(32)), // commitmentId - NotIndexed(ParamType::Uint(256)), // startEpoch - NotIndexed(ParamType::Uint(256)), // endEpoch - NotIndexed(ParamType::Array(Box::new(ParamType::FixedBytes(32)))), // unitIds - ] - } - - /// Parse data from chain. Accepts data with and without "0x" prefix. - fn parse(data_tokens: &mut impl Iterator) -> Result { - let peer_id = next_opt(data_tokens, "peer_id", Token::into_fixed_bytes)?; - let peer_id = parse_peer_id(peer_id)?; - - let commitment_id = CommitmentId(next_opt( - data_tokens, - "commitment_id", - Token::into_fixed_bytes, - )?); - - let start_epoch = next_opt(data_tokens, "start_epoch", Token::into_uint)?; - let end_epoch = next_opt(data_tokens, "end_epoch", Token::into_uint)?; - - let units: Vec> = next_opt(data_tokens, "unit_ids", |t| { - t.into_array()? - .into_iter() - .map(Token::into_fixed_bytes) - .collect() - })?; - - let mut unit_ids = vec![]; - for cu in units { - unit_ids.push(CUID::new(cu.try_into().map_err(|_| InvalidTokenSize)?)); - } - - Ok(CommitmentActivatedData { - peer_id, - commitment_id, - start_epoch, - end_epoch, - unit_ids, - }) - } -} - -impl ChainEvent for CommitmentActivated { - fn new(block_number: String, info: CommitmentActivatedData) -> Self { - Self { block_number, info } - } +use alloy_sol_types::sol; +sol! { + /// @dev Emitted when a commitment is activated. Commitment can be activated only if delegator deposited collateral. + /// @param peerId Peer id which linked to the commitment + /// @param commitmentId Commitment id which activated + /// @param startEpoch The start epoch of the commitment + /// @param endEpoch The end epoch of the commitment + /// @param unitIds Compute unit ids which linked to the commitment + #[derive(Debug)] + event CommitmentActivated( + bytes32 indexed peerId, + bytes32 indexed commitmentId, + uint256 startEpoch, + uint256 endEpoch, + bytes32[] unitIds + ); } #[cfg(test)] mod test { - - use super::{CommitmentActivated, CommitmentActivatedData}; - use chain_data::{parse_log, ChainData, Log}; - use core_manager::CUID; - use hex; - use hex::FromHex; + use super::CommitmentActivated; + use alloy_primitives::Uint; + use alloy_sol_types::{SolEvent, Word}; + use chain_data::parse_peer_id; + use hex_utils::decode_hex; + use std::str::FromStr; #[tokio::test] async fn test_cc_activated_topic() { assert_eq!( - CommitmentActivatedData::topic(), + CommitmentActivated::SIGNATURE_HASH.to_string(), "0x0b0a4688a90d1b24732d05ddf4925af69f02cd7d9a921b1cdcd4a7c2b6d57d68" ); } @@ -123,37 +36,35 @@ mod test { #[tokio::test] async fn test_chain_parsing_ok() { let data = "0x000000000000000000000000000000000000000000000000000000000000007b00000000000000000000000000000000000000000000000000000000000001c800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001c04d94f1e85788b245471c87490f42149b09503fe3af46733e4b5adf94583105".to_string(); - let log = Log { - data, - block_number: "0x0".to_string(), - removed: false, - topics: vec![ - CommitmentActivatedData::topic(), - "0xc586dcbfc973643dc5f885bf1a38e054d2675b03fe283a5b7337d70dda9f7171".to_string(), - "0x27e42c090aa007a4f2545547425aaa8ea3566e1f18560803ac48f8e98cb3b0c9".to_string(), - ], - }; - let result = parse_log::(log); + let topics = vec![ + CommitmentActivated::SIGNATURE_HASH.to_string(), + "0xc586dcbfc973643dc5f885bf1a38e054d2675b03fe283a5b7337d70dda9f7171".to_string(), + "0x27e42c090aa007a4f2545547425aaa8ea3566e1f18560803ac48f8e98cb3b0c9".to_string(), + ]; + let result = CommitmentActivated::decode_raw_log( + topics.into_iter().map(|t| Word::from_str(&t).unwrap()), + &decode_hex(&data).unwrap(), + true, + ); assert!(result.is_ok(), "can't parse data: {:?}", result); - let result = result.unwrap().info; + let result = result.unwrap(); assert_eq!( - result.peer_id.to_string(), + parse_peer_id(result.peerId.to_vec()).unwrap().to_string(), "12D3KooWP7RkvkBhbe7ATd451zxTifzF6Gm1uzCDadqQueET7EMe" // it's also the second topic ); assert_eq!( - hex::encode(result.commitment_id.0), - "27e42c090aa007a4f2545547425aaa8ea3566e1f18560803ac48f8e98cb3b0c9" // it's the third topic + result.commitmentId.to_string(), + "0x27e42c090aa007a4f2545547425aaa8ea3566e1f18560803ac48f8e98cb3b0c9" // it's the third topic ); - assert_eq!(result.start_epoch, 123.into()); - assert_eq!(result.end_epoch, 456.into()); + assert_eq!(result.startEpoch, Uint::from(123)); + assert_eq!(result.endEpoch, Uint::from(456)); - assert_eq!(result.unit_ids.len(), 1); + assert_eq!(result.unitIds.len(), 1); assert_eq!( - result.unit_ids[0], - ::from_hex("c04d94f1e85788b245471c87490f42149b09503fe3af46733e4b5adf94583105") - .unwrap() + result.unitIds[0].to_string(), + "0xc04d94f1e85788b245471c87490f42149b09503fe3af46733e4b5adf94583105" ) } } diff --git a/crates/chain-listener/src/event/compute_unit_matched.rs b/crates/chain-listener/src/event/compute_unit_matched.rs new file mode 100644 index 0000000000..36bb1ac1f8 --- /dev/null +++ b/crates/chain-listener/src/event/compute_unit_matched.rs @@ -0,0 +1,127 @@ +use alloy_sol_types::sol; + +sol! { + struct CIDV1 { + bytes4 prefixes; + bytes32 hash; + } + + event ComputeUnitMatched( + bytes32 indexed peerId, + address deal, + bytes32 unitId, + uint256 dealCreationBlock, + CIDV1 appCID + ); +} + +#[cfg(test)] +mod tests { + use crate::event::compute_unit_matched::ComputeUnitMatched; + use alloy_primitives::Uint; + use alloy_sol_types::SolEvent; + use chain_data::{parse_log, parse_peer_id, Log}; + use hex_utils::decode_hex; + + #[test] + fn topic() { + assert_eq!( + ComputeUnitMatched::SIGNATURE_HASH.to_string(), + String::from("0xb1c5a9179c3104a43de668491f14c45778f00ec34d5deee023af204820483bdb") + ); + } + + #[test] + fn peer_id() { + let bytes = [ + 88, 198, 255, 218, 126, 170, 188, 84, 84, 39, 255, 137, 18, 55, 7, 139, 121, 207, 149, + 42, 196, 115, 102, 160, 4, 47, 227, 62, 7, 53, 189, 15, + ]; + let peer_id = parse_peer_id(bytes.into()).expect("parse peer_id from Token"); + assert_eq!( + peer_id.to_string(), + String::from("12D3KooWFnv3Qc25eKpTDCNBoW1jXHMHHHSzcJoPkHai1b2dHNra") + ); + + let hex = "0x7a82a5feefcaad4a89c689412031e5f87c02b29e3fced583be5f05c7077354b7"; + let bytes = decode_hex(hex).expect("parse peer_id from hex"); + let peer_id = parse_peer_id(bytes).expect("parse peer_id from Token"); + assert_eq!( + peer_id.to_string(), + String::from("12D3KooWJ4bTHirdTFNZpCS72TAzwtdmavTBkkEXtzo6wHL25CtE") + ); + } + + #[test] + fn parse() { + let data1 = "000000000000000000000000ffa0611a099ab68ad7c3c67b4ca5bbbee7a58b9900000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000506a866cfa000000000000000000000000000000000000000000000000000000005a5a0f4fa4d41a4f976e799895cce944d5080041dba7d528d30e81c67973bac3".to_string(); + let data2 = "00000000000000000000000067b2ad3866429282e16e55b715d12a77f85b7ce800000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000566a866cfa000000000000000000000000000000000000000000000000000000005a5a0f4fa4d41a4f976e799895cce944d5080041dba7d528d30e81c67973bac3".to_string(); + let log1 = Log { + data: data1, + block_number: "0x0".to_string(), + removed: false, + topics: vec![ + ComputeUnitMatched::SIGNATURE_HASH.to_string(), + "0x7a82a5feefcaad4a89c689412031e5f87c02b29e3fced583be5f05c7077354b7".to_string(), + ], + }; + let log2 = Log { + data: data2, + block_number: "0x1".to_string(), + removed: false, + topics: vec![ + ComputeUnitMatched::SIGNATURE_HASH.to_string(), + "0x7a82a5feefcaad4a89c689412031e5f87c02b29e3fced583be5f05c7077354b7".to_string(), + ], + }; + + let m = parse_log::(log1).expect("error parsing Match from log"); + assert_eq!( + parse_peer_id(m.peerId.to_vec()).unwrap().to_string(), + "12D3KooWJ4bTHirdTFNZpCS72TAzwtdmavTBkkEXtzo6wHL25CtE" + ); + assert_eq!( + m.deal.to_string(), + "0xFfA0611a099AB68AD7C3C67B4cA5bbBEE7a58B99" + ); + assert_eq!( + m.unitId.to_string(), + "0x00000000000000000000000000000000000000000000000000000000000000a0" + ); + assert_eq!(m.dealCreationBlock, Uint::from(80)); + + // let cid_bytes = [m.appCID.prefixes.0.to_vec(), m.appCID.hash.0.to_vec()].concat(); + // assert_eq!("0x6a866cfa", hex::encode(&cid_bytes)); + // let app_cid = libipld::Cid::read_bytes(cid_bytes.as_slice()) + // .unwrap() + // .to_string(); + // assert_eq!( + // app_cid, + // "bafkreifolrizgmusl4y7or5e5xmvr623a6i3ca4d5rwv457cezhschqj4m" + // ); + + let m = parse_log::(log2).expect("error parsing Match from log"); + assert_eq!( + parse_peer_id(m.peerId.to_vec()).unwrap().to_string(), + "12D3KooWJ4bTHirdTFNZpCS72TAzwtdmavTBkkEXtzo6wHL25CtE" + ); + assert_eq!( + m.deal.to_string(), + "0x67b2AD3866429282e16e55B715d12A77F85B7CE8" + ); + assert_eq!( + m.unitId.to_string(), + "0x00000000000000000000000000000000000000000000000000000000000000a0" + ); + assert_eq!(m.dealCreationBlock, Uint::from(86)); + // TODO: fix cids + // let cid_bytes = [m.appCID.prefixes.to_vec(), m.appCID.hash.to_vec()].concat(); + // let app_cid = libipld::Cid::read_bytes(cid_bytes.as_slice()) + // .unwrap() + // .to_string(); + // assert_eq!( + // app_cid, + // "bafkreifolrizgmusl4y7or5e5xmvr623a6i3ca4d5rwv457cezhschqj4m" + // ); + } +} diff --git a/crates/chain-listener/src/event/deal_matched.rs b/crates/chain-listener/src/event/deal_matched.rs deleted file mode 100644 index ea0898acf3..0000000000 --- a/crates/chain-listener/src/event/deal_matched.rs +++ /dev/null @@ -1,203 +0,0 @@ -use ccp_shared::types::CUID; -use chain_data::ChainDataError::InvalidTokenSize; -use chain_data::EventField::{Indexed, NotIndexed}; -use chain_data::{next_opt, parse_peer_id, ChainData, ChainDataError, ChainEvent, EventField}; -use ethabi::ethereum_types::U256; -use ethabi::param_type::ParamType; -use ethabi::Token; -use types::DealId; - -/// Corresponding Solidity type: -/// ```solidity -/// struct CIDV1 { -/// bytes4 prefixes; -/// bytes32 hash; -/// } -/// -/// event ComputeUnitMatched( -/// bytes32 indexed peerId, -/// address deal -/// bytes32 unitId, -/// uint256 dealCreationBlock, -/// CIDV1 appCID -/// ); -/// ``` - -#[allow(dead_code)] -#[derive(Debug, Clone)] -pub struct DealMatchedData { - compute_peer: String, - pub deal_id: DealId, - pub(crate) unit_id: CUID, - deal_creation_block: U256, - app_cid: String, -} - -#[allow(dead_code)] -#[derive(Debug, Clone)] -pub struct DealMatched { - block_number: String, - pub info: DealMatchedData, -} - -impl DealMatched { - pub const EVENT_NAME: &'static str = "ComputeUnitMatched"; -} - -impl ChainData for DealMatchedData { - fn event_name() -> &'static str { - DealMatched::EVENT_NAME - } - - fn signature() -> Vec { - vec![ - // compute_provider - Indexed(ParamType::FixedBytes(32)), - // deal - NotIndexed(ParamType::Address), - // unit_id - NotIndexed(ParamType::FixedBytes(32)), - // deal_creation_block - NotIndexed(ParamType::Uint(256)), - // app_cid - NotIndexed(ParamType::Tuple(vec![ - // prefixes - ParamType::FixedBytes(4), - // hash - ParamType::FixedBytes(32), - ])), - ] - } - - /// Parse data from chain. Accepts data with and without "0x" prefix. - fn parse(data_tokens: &mut impl Iterator) -> Result { - let tokens = &mut data_tokens.into_iter(); - - let compute_peer = next_opt(tokens, "compute_peer", Token::into_fixed_bytes)?; - let compute_peer = parse_peer_id(compute_peer)?.to_string(); - - let deal = next_opt(tokens, "deal", Token::into_address)?; - let unit_id = next_opt(tokens, "unit_id", Token::into_fixed_bytes)?; - let deal_creation_block = next_opt(tokens, "deal_creation_block", Token::into_uint)?; - - let app_cid = &mut next_opt(tokens, "app_cid", Token::into_tuple)?.into_iter(); - let cid_prefixes = next_opt(app_cid, "app_cid.prefixes", Token::into_fixed_bytes)?; - let cid_hash = next_opt(app_cid, "app_cid.cid_hash", Token::into_fixed_bytes)?; - let cid_bytes = [cid_prefixes, cid_hash].concat(); - let app_cid = libipld::Cid::read_bytes(cid_bytes.as_slice()) - .map_err(|_| ChainDataError::InvalidParsedToken("app_cid"))? - .to_string(); - - Ok(DealMatchedData { - compute_peer, - deal_id: format!("{deal:#x}").into(), - unit_id: CUID::new(unit_id.try_into().map_err(|_| InvalidTokenSize)?), - deal_creation_block, - app_cid, - }) - } -} - -impl ChainEvent for DealMatched { - fn new(block_number: String, info: DealMatchedData) -> Self { - Self { block_number, info } - } -} - -#[cfg(test)] -mod tests { - use super::{DealMatched, DealMatchedData}; - use chain_data::{parse_log, parse_peer_id, ChainData, Log}; - use hex_utils::decode_hex; - - #[test] - fn topic() { - assert_eq!( - DealMatchedData::topic(), - String::from("0xb1c5a9179c3104a43de668491f14c45778f00ec34d5deee023af204820483bdb") - ); - } - - #[test] - fn peer_id() { - let bytes = [ - 88, 198, 255, 218, 126, 170, 188, 84, 84, 39, 255, 137, 18, 55, 7, 139, 121, 207, 149, - 42, 196, 115, 102, 160, 4, 47, 227, 62, 7, 53, 189, 15, - ]; - let peer_id = parse_peer_id(bytes.into()).expect("parse peer_id from Token"); - assert_eq!( - peer_id.to_string(), - String::from("12D3KooWFnv3Qc25eKpTDCNBoW1jXHMHHHSzcJoPkHai1b2dHNra") - ); - - let hex = "0x7a82a5feefcaad4a89c689412031e5f87c02b29e3fced583be5f05c7077354b7"; - let bytes = decode_hex(hex).expect("parse peer_id from hex"); - let peer_id = parse_peer_id(bytes).expect("parse peer_id from Token"); - assert_eq!( - peer_id.to_string(), - String::from("12D3KooWJ4bTHirdTFNZpCS72TAzwtdmavTBkkEXtzo6wHL25CtE") - ); - } - - #[test] - fn parse() { - let data1 = "0x000000000000000000000000ffa0611a099ab68ad7c3c67b4ca5bbbee7a58b9900000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000500155122000000000000000000000000000000000000000000000000000000000ae5c519332925f31f747a4edd958fb5b0791b10383ec6d5e77e2264f211e09e300000000000000000000000000000000000000000000000000000000000000036c9d5e8bcc73a422dd6f968f13cd6fc92ccd5609b455cf2c7978cbc694297853fef3b95696986bf289166835e05f723f0fdea97d2bc5fea0ebbbf87b6a866cfa5a5a0f4fa4d41a4f976e799895cce944d5080041dba7d528d30e81c67973bac3".to_string(); - let data2 = "0x00000000000000000000000067b2ad3866429282e16e55b715d12a77f85b7ce800000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000560155122000000000000000000000000000000000000000000000000000000000ae5c519332925f31f747a4edd958fb5b0791b10383ec6d5e77e2264f211e09e300000000000000000000000000000000000000000000000000000000000000036c9d5e8bcc73a422dd6f968f13cd6fc92ccd5609b455cf2c7978cbc694297853fef3b95696986bf289166835e05f723f0fdea97d2bc5fea0ebbbf87b6a866cfa5a5a0f4fa4d41a4f976e799895cce944d5080041dba7d528d30e81c67973bac3".to_string(); - let log1 = Log { - data: data1, - block_number: "0x0".to_string(), - removed: false, - topics: vec![ - DealMatchedData::topic(), - "0x7a82a5feefcaad4a89c689412031e5f87c02b29e3fced583be5f05c7077354b7".to_string(), - ], - }; - let log2 = Log { - data: data2, - block_number: "0x1".to_string(), - removed: false, - topics: vec![ - DealMatchedData::topic(), - "0x7a82a5feefcaad4a89c689412031e5f87c02b29e3fced583be5f05c7077354b7".to_string(), - ], - }; - - let m = - parse_log::(log1).expect("error parsing Match from log"); - assert_eq!(m.block_number, "0x0"); - let m = m.info; - assert_eq!( - m.compute_peer, - "12D3KooWJ4bTHirdTFNZpCS72TAzwtdmavTBkkEXtzo6wHL25CtE" - ); - assert_eq!(m.deal_id, "0xFfA0611a099AB68AD7C3C67B4cA5bbBEE7a58B99"); - assert_eq!( - m.unit_id.to_string(), - "00000000000000000000000000000000000000000000000000000000000000a0" - ); - assert_eq!(m.deal_creation_block, 80.into()); - assert_eq!( - m.app_cid, - "bafkreifolrizgmusl4y7or5e5xmvr623a6i3ca4d5rwv457cezhschqj4m" - ); - - let m = - parse_log::(log2).expect("error parsing Match from log"); - assert_eq!(m.block_number, "0x1"); - let m = m.info; - assert_eq!( - m.compute_peer, - "12D3KooWJ4bTHirdTFNZpCS72TAzwtdmavTBkkEXtzo6wHL25CtE" - ); - assert_eq!(m.deal_id, "0x67b2AD3866429282e16e55B715d12A77F85B7CE8"); - assert_eq!( - m.unit_id.to_string(), - "00000000000000000000000000000000000000000000000000000000000000a0" - ); - assert_eq!(m.deal_creation_block, 86.into()); - assert_eq!( - m.app_cid, - "bafkreifolrizgmusl4y7or5e5xmvr623a6i3ca4d5rwv457cezhschqj4m" - ); - } -} diff --git a/crates/chain-listener/src/event/mod.rs b/crates/chain-listener/src/event/mod.rs index 150b9a284b..0d2c0df494 100644 --- a/crates/chain-listener/src/event/mod.rs +++ b/crates/chain-listener/src/event/mod.rs @@ -1,9 +1,8 @@ pub mod cc_activated; -mod deal_matched; +mod compute_unit_matched; mod unit_activated; mod unit_deactivated; -pub use cc_activated::CommitmentActivatedData; -pub use deal_matched::{DealMatched, DealMatchedData}; -pub use unit_activated::{UnitActivated, UnitActivatedData}; -pub use unit_deactivated::{UnitDeactivated, UnitDeactivatedData}; +pub use compute_unit_matched::ComputeUnitMatched; +pub use unit_activated::UnitActivated; +pub use unit_deactivated::UnitDeactivated; diff --git a/crates/chain-listener/src/event/unit_activated.rs b/crates/chain-listener/src/event/unit_activated.rs index b77ba0adaa..2473b9c1c6 100644 --- a/crates/chain-listener/src/event/unit_activated.rs +++ b/crates/chain-listener/src/event/unit_activated.rs @@ -1,131 +1,72 @@ -use chain_data::ChainDataError::InvalidTokenSize; -use chain_data::EventField::{Indexed, NotIndexed}; -use chain_data::{next_opt, ChainData, ChainDataError, ChainEvent, EventField}; -use chain_types::{CommitmentId, PendingUnit}; -use core_manager::CUID; -use ethabi::ethereum_types::U256; -use ethabi::param_type::ParamType; -use ethabi::Token; -use serde::{Deserialize, Serialize}; - -/// @dev Emitted when a unit activated. Unit is activated when it returned from deal -/// @param commitmentId Commitment id -/// @param unitId Compute unit id which activated -/// event UnitActivated( -/// bytes32 indexed commitmentId, -/// bytes32 indexed unitId, -/// uint256 startEpoch -/// ); - -#[derive(Debug, Serialize, Deserialize)] -pub struct UnitActivatedData { - pub commitment_id: CommitmentId, - pub unit_id: CUID, - pub start_epoch: U256, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct UnitActivated { - pub block_number: String, - pub info: UnitActivatedData, -} +use alloy_sol_types::sol; +use chain_connector::PendingUnit; -impl UnitActivated { - pub const EVENT_NAME: &'static str = "UnitActivated"; -} - -impl ChainData for UnitActivatedData { - fn event_name() -> &'static str { - UnitActivated::EVENT_NAME - } - - fn signature() -> Vec { - vec![ - Indexed(ParamType::FixedBytes(32)), // commitmentId - Indexed(ParamType::FixedBytes(32)), // unitId - NotIndexed(ParamType::Uint(256)), // startEpoch - ] - } - - /// Parse data from chain. Accepts data with and without "0x" prefix. - fn parse(data_tokens: &mut impl Iterator) -> Result { - let commitment_id = CommitmentId(next_opt( - data_tokens, - "commitment_id", - Token::into_fixed_bytes, - )?); - - let unit_id = next_opt(data_tokens, "unit_id", Token::into_fixed_bytes)?; - - let start_epoch = next_opt(data_tokens, "start_epoch", Token::into_uint)?; - - Ok(UnitActivatedData { - commitment_id, - unit_id: CUID::new(unit_id.try_into().map_err(|_| InvalidTokenSize)?), - start_epoch, - }) - } -} +use core_manager::CUID; -impl ChainEvent for UnitActivated { - fn new(block_number: String, info: UnitActivatedData) -> Self { - Self { block_number, info } - } +sol! { + /// @dev Emitted when a unit activated. + /// Unit is activated when it returned from deal + /// @param commitmentId Commitment id + /// @param unitId Compute unit id which activated + #[derive(Debug)] + event UnitActivated( + bytes32 indexed commitmentId, + bytes32 indexed unitId, + uint256 startEpoch + ); } -impl From for PendingUnit { - fn from(data: UnitActivatedData) -> Self { +impl From for PendingUnit { + fn from(data: UnitActivated) -> Self { PendingUnit { - id: data.unit_id, - start_epoch: data.start_epoch, + id: CUID::new(data.unitId.0), + start_epoch: data.startEpoch, } } } #[cfg(test)] mod test { - use super::UnitActivated; - use crate::event::UnitActivatedData; - use chain_data::{parse_log, ChainData, Log}; - use core_manager::CUID; + use alloy_primitives::Uint; + use alloy_sol_types::{SolEvent, Word}; + use std::str::FromStr; - use hex::FromHex; + use hex_utils::decode_hex; #[tokio::test] async fn test_unit_activated_topic() { assert_eq!( - UnitActivatedData::topic(), + UnitActivated::SIGNATURE_HASH.to_string(), "0x8e4b27eeb3194deef0b3140997e6b82f53eb7350daceb9355268009b92f70add" ); } #[tokio::test] async fn test_chain_parsing_ok() { - let data = "0x000000000000000000000000000000000000000000000000000000000000007b00000000000000000000000000000000000000000000000000000000000001c800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001c04d94f1e85788b245471c87490f42149b09503fe3af46733e4b5adf94583105".to_string(); - let log = Log { - data, - block_number: "0x0".to_string(), - removed: false, - topics: vec![ - UnitActivatedData::topic(), - "0x431688393bc518ef01e11420af290b92f3668dca24fc171eeb11dd15bcefad72".to_string(), - "0xd33bc101f018e42351fbe2adc8682770d164e27e2e4c6454e0faaf5b8b63b90e".to_string(), - ], - }; - let result = parse_log::(log); + let data = "0x000000000000000000000000000000000000000000000000000000000000007b".to_string(); + let topics = vec![ + UnitActivated::SIGNATURE_HASH.to_string(), + "0x431688393bc518ef01e11420af290b92f3668dca24fc171eeb11dd15bcefad72".to_string(), + "0xd33bc101f018e42351fbe2adc8682770d164e27e2e4c6454e0faaf5b8b63b90e".to_string(), + ]; + + let result = UnitActivated::decode_raw_log( + topics.into_iter().map(|t| Word::from_str(&t).unwrap()), + &decode_hex(&data).unwrap(), + true, + ); assert!(result.is_ok(), "can't parse data: {:?}", result); - let result = result.unwrap().info; + let result = result.unwrap(); assert_eq!( - hex::encode(result.commitment_id.0), - "431688393bc518ef01e11420af290b92f3668dca24fc171eeb11dd15bcefad72" // it's the second topic + result.commitmentId.to_string(), + "0x431688393bc518ef01e11420af290b92f3668dca24fc171eeb11dd15bcefad72" // it's the second topic ); assert_eq!( - result.unit_id, - ::from_hex("d33bc101f018e42351fbe2adc8682770d164e27e2e4c6454e0faaf5b8b63b90e") - .unwrap() // it's also the third topic + result.unitId.to_string(), + "0xd33bc101f018e42351fbe2adc8682770d164e27e2e4c6454e0faaf5b8b63b90e" // it's also the third topic ); - assert_eq!(result.start_epoch, 123.into()); + assert_eq!(result.startEpoch, Uint::from(123)); } } diff --git a/crates/chain-listener/src/event/unit_deactivated.rs b/crates/chain-listener/src/event/unit_deactivated.rs index ca10e5b708..b6c1d2b45d 100644 --- a/crates/chain-listener/src/event/unit_deactivated.rs +++ b/crates/chain-listener/src/event/unit_deactivated.rs @@ -1,85 +1,27 @@ -use chain_data::ChainDataError::InvalidTokenSize; -use chain_data::EventField::Indexed; -use chain_data::{next_opt, ChainData, ChainDataError, ChainEvent, EventField}; -use chain_types::CommitmentId; -use core_manager::CUID; -use ethabi::param_type::ParamType; -use ethabi::Token; -use serde::{Deserialize, Serialize}; - -/// @dev Emitted when a unit deactivated. Unit is deactivated when it moved to deal -/// @param commitmentId Commitment id -/// @param unitId Compute unit id which deactivated -/// event UnitDeactivated( -/// bytes32 indexed commitmentId, -/// bytes32 indexed unitId -/// ); - -#[derive(Debug, Serialize, Deserialize)] -pub struct UnitDeactivatedData { - pub commitment_id: CommitmentId, - pub unit_id: CUID, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct UnitDeactivated { - pub block_number: String, - pub info: UnitDeactivatedData, -} - -impl UnitDeactivated { - pub const EVENT_NAME: &'static str = "UnitDeactivated"; -} - -impl ChainData for UnitDeactivatedData { - fn event_name() -> &'static str { - UnitDeactivated::EVENT_NAME - } - - fn signature() -> Vec { - vec![ - Indexed(ParamType::FixedBytes(32)), // commitmentId - Indexed(ParamType::FixedBytes(32)), // unitId - ] - } - - /// Parse data from chain. Accepts data with and without "0x" prefix. - fn parse(data_tokens: &mut impl Iterator) -> Result { - let commitment_id = CommitmentId(next_opt( - data_tokens, - "commitment_id", - Token::into_fixed_bytes, - )?); - - let unit_id = next_opt(data_tokens, "unit_id", Token::into_fixed_bytes)?; - - Ok(UnitDeactivatedData { - commitment_id, - unit_id: CUID::new(unit_id.try_into().map_err(|_| InvalidTokenSize)?), - }) - } -} - -impl ChainEvent for UnitDeactivated { - fn new(block_number: String, info: UnitDeactivatedData) -> Self { - Self { block_number, info } - } +use alloy_sol_types::sol; + +sol! { + /// @dev Emitted when a unit deactivated. Unit is deactivated when it moved to deal + /// @param commitmentId Commitment id + /// @param unitId Compute unit id which deactivated + #[derive(Debug)] + event UnitDeactivated( + bytes32 indexed commitmentId, + bytes32 indexed unitId + ); } #[cfg(test)] mod test { - use crate::event::unit_deactivated::UnitDeactivated; - use crate::event::UnitDeactivatedData; - use chain_data::{parse_log, ChainData, Log}; - use core_manager::CUID; + use alloy_sol_types::SolEvent; - use hex::FromHex; + use chain_data::{parse_log, Log}; #[tokio::test] async fn test_unit_activated_topic() { assert_eq!( - UnitDeactivatedData::topic(), + UnitDeactivated::SIGNATURE_HASH.to_string(), "0xbd9cde1bbc961036d34368ae328c38917036a98eacfb025a1ff6d2c6235d0a14" ); } @@ -87,28 +29,28 @@ mod test { #[tokio::test] async fn test_chain_parsing_ok() { let data = "0x".to_string(); - let log = Log { + + let topics = vec![ + UnitDeactivated::SIGNATURE_HASH.to_string(), + "0x91cfcc4a139573b08646960be31b278152ef3480710ab15d9b39262be37038a1".to_string(), + "0xf3660ca1eaf461cbbb5e1d06ade6ba4a9a503c0d680ba825e09cddd3f9b45fc6".to_string(), + ]; + let result = parse_log::(Log { data, - block_number: "0x0".to_string(), + block_number: "".to_string(), removed: false, - topics: vec![ - UnitDeactivatedData::topic(), - "0x91cfcc4a139573b08646960be31b278152ef3480710ab15d9b39262be37038a1".to_string(), - "0xf3660ca1eaf461cbbb5e1d06ade6ba4a9a503c0d680ba825e09cddd3f9b45fc6".to_string(), - ], - }; - let result = parse_log::(log); + topics, + }); assert!(result.is_ok(), "can't parse data: {:?}", result); - let result = result.unwrap().info; + let result = result.unwrap(); assert_eq!( - hex::encode(result.commitment_id.0), - "91cfcc4a139573b08646960be31b278152ef3480710ab15d9b39262be37038a1" // it's the second topic + result.commitmentId.to_string(), + "0x91cfcc4a139573b08646960be31b278152ef3480710ab15d9b39262be37038a1" // it's the second topic ); assert_eq!( - result.unit_id, - ::from_hex("f3660ca1eaf461cbbb5e1d06ade6ba4a9a503c0d680ba825e09cddd3f9b45fc6") - .unwrap() // it's also the third topic + result.unitId.to_string(), + "0xf3660ca1eaf461cbbb5e1d06ade6ba4a9a503c0d680ba825e09cddd3f9b45fc6" // it's also the third topic ); } } diff --git a/crates/chain-listener/src/lib.rs b/crates/chain-listener/src/lib.rs index ad1ff206a9..465ebc3337 100644 --- a/crates/chain-listener/src/lib.rs +++ b/crates/chain-listener/src/lib.rs @@ -3,6 +3,8 @@ #![feature(extract_if)] #![feature(btree_extract_if)] +extern crate core; + pub use listener::ChainListener; mod event; diff --git a/crates/chain-listener/src/listener.rs b/crates/chain-listener/src/listener.rs index 65033bf86a..63e6e87000 100644 --- a/crates/chain-listener/src/listener.rs +++ b/crates/chain-listener/src/listener.rs @@ -1,7 +1,12 @@ +use alloy_primitives::{Address, FixedBytes, Uint, U256}; +use alloy_sol_types::SolEvent; use backoff::Error::Permanent; use std::collections::{BTreeMap, BTreeSet, HashMap}; +use std::future::pending; +use std::ops::Add; use std::path::PathBuf; use std::process::exit; +use std::str::FromStr; use std::sync::Arc; use std::time::Duration; @@ -11,7 +16,7 @@ use ccp_rpc_client::CCPRpcHttpClient; use ccp_shared::proof::{CCProof, CCProofId, ProofIdx}; use ccp_shared::types::{Difficulty, GlobalNonce, LocalNonce, ResultHash}; use cpu_utils::PhysicalCoreId; -use ethabi::ethereum_types::U256; + use eyre::eyre; use jsonrpsee::core::client::{Client as WsClient, Subscription, SubscriptionClientT}; use jsonrpsee::core::{client, JsonValue}; @@ -24,12 +29,12 @@ use tokio::time::interval; use tokio_stream::wrappers::IntervalStream; use tokio_stream::StreamExt; -use chain_connector::{ChainConnector, ConnectorError}; -use chain_data::{parse_log, peer_id_to_hex, ChainData, Log}; -use chain_types::{ - CommitmentId, CommitmentStatus, ComputeUnit, DealStatus, PendingUnit, COMMITMENT_IS_NOT_ACTIVE, - PEER_NOT_EXISTS, TOO_MANY_PROOFS, +use chain_connector::Offer::ComputeUnit; +use chain_connector::{ + is_commitment_not_active, is_too_many_proofs, CCStatus, ChainConnector, CommitmentId, + ConnectorError, Deal, PEER_NOT_EXISTS, }; +use chain_data::{parse_log, peer_id_to_hex, Log}; use core_manager::manager::{CoreManager, CoreManagerFunctions}; use core_manager::types::{AcquireRequest, WorkType}; use core_manager::CUID; @@ -37,10 +42,7 @@ use server_config::{ChainConfig, ChainListenerConfig}; use types::DealId; use crate::event::cc_activated::CommitmentActivated; -use crate::event::{ - CommitmentActivatedData, DealMatched, DealMatchedData, UnitActivated, UnitActivatedData, - UnitDeactivated, UnitDeactivatedData, -}; +use crate::event::{ComputeUnitMatched, UnitActivated, UnitDeactivated}; use crate::persistence; const PROOF_POLL_LIMIT: usize = 10; @@ -61,19 +63,19 @@ pub struct ChainListener { global_nonce: GlobalNonce, current_epoch: U256, epoch_duration: U256, + min_proofs_per_epoch: U256, + max_proofs_per_epoch: U256, - // proof_counter: HashMap, + proof_counter: BTreeMap, current_commitment: Option, - active_compute_units: BTreeSet, - // Cores of pending units also run CCs but for other CUs - pending_compute_units: BTreeSet, + cc_compute_units: BTreeMap, active_deals: BTreeMap, /// Resets every epoch last_submitted_proof_id: ProofIdx, - + pending_proof_txs: Vec<(String, CUID)>, persisted_proof_id_dir: PathBuf, unit_activated: Option>, @@ -85,11 +87,11 @@ pub struct ChainListener { async fn poll_subscription(s: &mut Option>) -> Option> where - T: DeserializeOwned, + T: DeserializeOwned + Send, { match s { Some(ref mut s) => s.next().await, - None => None, + None => pending().await, } } @@ -114,17 +116,20 @@ impl ChainListener { config: chain_config, host_id, difficulty: Difficulty::default(), - init_timestamp: U256::zero(), - global_nonce: GlobalNonce::new([0; 32]).into(), - current_epoch: U256::zero(), - epoch_duration: U256::zero(), + init_timestamp: U256::ZERO, + global_nonce: GlobalNonce::new([0; 32]), + current_epoch: U256::ZERO, + epoch_duration: U256::ZERO, + min_proofs_per_epoch: U256::ZERO, + max_proofs_per_epoch: U256::ZERO, + proof_counter: BTreeMap::new(), current_commitment: None, - active_compute_units: BTreeSet::new(), - pending_compute_units: BTreeSet::new(), + cc_compute_units: BTreeMap::new(), core_manager, timer_resolution: listener_config.proof_poll_period, ccp_client, last_submitted_proof_id: ProofIdx::zero(), + pending_proof_txs: vec![], persisted_proof_id_dir, unit_activated: None, unit_deactivated: None, @@ -174,8 +179,10 @@ impl ChainListener { self.global_nonce = init_params.global_nonce; self.epoch_duration = init_params.epoch_duration; self.current_epoch = init_params.current_epoch; + self.min_proofs_per_epoch = init_params.min_proofs_per_epoch; + self.max_proofs_per_epoch = init_params.max_proofs_per_epoch; - tracing::info!(target: "chain-listener","Commitment initial params: difficulty {}, global nonce {}, init_timestamp {}, epoch_duration {}, current_epoch {}", init_params.difficulty, init_params.global_nonce, init_params.init_timestamp, init_params.epoch_duration, init_params.current_epoch); + tracing::info!(target: "chain-listener","Commitment initial params: difficulty {}, global nonce {}, init_timestamp {}, epoch_duration {}, current_epoch {}, min_proofs_per_epoch {}, max_proofs_per_epoch {}", init_params.difficulty, init_params.global_nonce, init_params.init_timestamp, init_params.epoch_duration, init_params.current_epoch, init_params.min_proofs_per_epoch, init_params.max_proofs_per_epoch); Ok(()) } @@ -194,35 +201,33 @@ impl ChainListener { tracing::info!(target: "chain-listener", "Successfully subscribed to unit events"); } else { tracing::info!(target: "chain-listener", "Compute peer has no commitment"); - self.stop_commitment().await? + self.reset_commitment().await? } if let Some(status) = self.get_commitment_status().await? { - tracing::info!(target: "chain-listener", "Current commitment status: {status}"); + tracing::info!(target: "chain-listener", "Current commitment status: {status:?}"); match status { - CommitmentStatus::Active => { + CCStatus::Active => { self.refresh_commitment().await?; } - CommitmentStatus::Inactive - | CommitmentStatus::Failed - | CommitmentStatus::Removed => { + CCStatus::Inactive | CCStatus::Failed | CCStatus::Removed => { self.reset_commitment().await?; } - CommitmentStatus::WaitDelegation | CommitmentStatus::WaitStart => { + CCStatus::WaitDelegation | CCStatus::WaitStart => { tracing::info!(target: "chain-listener", "Waiting for commitment to be activated; Stopping current one"); self.stop_commitment().await? } + _ => {} } } self.load_proof_id().await?; - () }; if let Err(e) = result { - tracing::error!(target: "chain-listener", "Failed to refresh compute units: {e}"); + tracing::warn!(target: "chain-listener", "Failed to refresh compute units: {e}"); tracing::info!(target: "chain-listener", "Retrying in 5 seconds"); tokio::time::sleep(Duration::from_secs(5)).await; } else { @@ -238,8 +243,10 @@ impl ChainListener { } pub async fn set_proof_id(&mut self, proof_id: ProofIdx) -> eyre::Result<()> { - let mut backoff = ExponentialBackoff::default(); - backoff.max_elapsed_time = Some(Duration::from_secs(3)); + let backoff = ExponentialBackoff { + max_elapsed_time: Some(Duration::from_secs(3)), + ..ExponentialBackoff::default() + }; let write = retry(backoff, || async { persistence::persist_proof_id( @@ -247,14 +254,14 @@ impl ChainListener { self.last_submitted_proof_id, self.current_epoch, ).await.map_err(|err|{ - tracing::error!(target: "chain-listener", "Failed to persist proof id: {err}; Retrying..."); + tracing::warn!(target: "chain-listener", "Failed to persist proof id: {err}; Retrying..."); eyre!(err) })?; Ok(()) }).await; if let Err(err) = write { - tracing::error!(target: "chain-listener", "Failed to persist proof id: {err}; Ignoring.."); + tracing::warn!(target: "chain-listener", "Failed to persist proof id: {err}; Ignoring.."); } self.last_submitted_proof_id = proof_id; @@ -296,7 +303,7 @@ impl ChainListener { .realloc_utility_cores(vec![utility_core]) .await .map_err(|err| { - tracing::error!(target: "chain-listener", "Error reallocating utility core {utility_core} to CCP, error: {err}. Retrying..."); + tracing::warn!(target: "chain-listener", "Error reallocating utility core {utility_core} to CCP, error: {err}. Retrying..."); eyre::eyre!("Error reallocating utility core {utility_core} to CCP, error: {err}") })?; Ok(()) @@ -311,6 +318,7 @@ impl ChainListener { let result = tokio::task::Builder::new() .name("ChainListener") .spawn(async move { + if let Err(err) = self.set_utility_core().await { tracing::error!(target: "chain-listener", "Failed to set utility core: {err}; Stopping..."); exit(1); @@ -327,28 +335,26 @@ impl ChainListener { exit(1); } - if let Err(err) = self.subscribe_deal_matched().await { + if let Err(err) = self.subscribe_unit_matched().await { tracing::error!(target: "chain-listener", "Failed to subscribe to deal matched: {err}; Stopping..."); exit(1); } tracing::info!(target: "chain-listener", "Subscribed successfully"); - let setup: eyre::Result<()> = try { - self.refresh_state().await?; - }; - if let Err(err) = setup { - tracing::error!(target: "chain-listener", "ChainListener: compute units refresh error: {err}"); - panic!("ChainListener startup error: {err}"); + if let Err(err) = self.refresh_state().await { + tracing::error!(target: "chain-listener", "Failed to refresh state: {err}; Stopping..."); + exit(1); } + tracing::info!(target: "chain-listener", "State successfully refreshed, starting main loop"); let mut timer = IntervalStream::new(interval(self.timer_resolution)); loop { tokio::select! { event = poll_subscription(&mut self.heads) => { if let Err(err) = self.process_new_header(event).await { - tracing::error!(target: "chain-listener", "newHeads event processing error: {err}"); + tracing::warn!(target: "chain-listener", "newHeads event processing error: {err}"); let result: eyre::Result<()> = try { self.refresh_state().await?; @@ -363,7 +369,7 @@ impl ChainListener { }, event = poll_subscription(&mut self.commitment_activated) => { if let Err(err) = self.process_commitment_activated(event).await { - tracing::error!(target: "chain-listener", "CommitmentActivated event processing error: {err}"); + tracing::warn!(target: "chain-listener", "CommitmentActivated event processing error: {err}"); let result: eyre::Result<()> = try { self.refresh_state().await?; @@ -378,7 +384,7 @@ impl ChainListener { event = poll_subscription(&mut self.unit_activated) => { if self.unit_activated.is_some() { if let Err(err) = self.process_unit_activated(event).await { - tracing::error!(target: "chain-listener", "UnitActivated event processing error: {err}"); + tracing::warn!(target: "chain-listener", "UnitActivated event processing error: {err}"); let result: eyre::Result<()> = try { self.refresh_state().await?; @@ -394,7 +400,7 @@ impl ChainListener { event = poll_subscription(&mut self.unit_deactivated) => { if self.unit_deactivated.is_some() { if let Err(err) = self.process_unit_deactivated(event).await { - tracing::error!(target: "chain-listener", "UnitDeactivated event processing error: {err}"); + tracing::warn!(target: "chain-listener", "UnitDeactivated event processing error: {err}"); let result: eyre::Result<()> = try { self.refresh_state().await?; @@ -408,12 +414,12 @@ impl ChainListener { } }, event = poll_subscription(&mut self.unit_matched) => { - if let Err(err) = self.process_deal_matched(event) { - tracing::error!(target: "chain-listener", "DealMatched event processing error: {err}"); + if let Err(err) = self.process_unit_matched(event) { + tracing::warn!(target: "chain-listener", "DealMatched event processing error: {err}"); let result: eyre::Result<()> = try { self.refresh_state().await?; - self.subscribe_deal_matched().await?; + self.subscribe_unit_matched().await?; }; if let Err(err) = result { tracing::error!(target: "chain-listener", "Failed to resubscribe to DealMatched: {err}; Stopping..."); @@ -424,16 +430,19 @@ impl ChainListener { _ = timer.next() => { if self.ccp_client.is_some() { if let Err(err) = self.poll_proofs().await { - tracing::error!(target: "chain-listener", "Failed to poll/submit proofs: {err}"); + tracing::warn!(target: "chain-listener", "Failed to poll/submit proofs: {err}"); } - } else{ - if let Err(err) = self.submit_mocked_proofs().await { - tracing::error!(target: "chain-listener", "Failed to submit mocked proofs: {err}"); - } - } + } else if let Err(err) = self.submit_mocked_proofs().await { + tracing::warn!(target: "chain-listener", "Failed to submit mocked proofs: {err}"); + } + if let Err(err) = self.poll_deal_statuses().await { - tracing::error!(target: "chain-listener", "Failed to poll deal statuses: {err}"); + tracing::warn!(target: "chain-listener", "Failed to poll deal statuses: {err}"); + } + + if let Err(err) = self.poll_pending_proof_txs().await { + tracing::warn!(target: "chain-listener", "Failed to poll pending proof txs: {err}"); } } } @@ -444,7 +453,7 @@ impl ChainListener { result } - async fn get_commitment_status(&self) -> eyre::Result> { + async fn get_commitment_status(&self) -> eyre::Result> { if let Some(commitment_id) = self.current_commitment.clone() { let status = self .chain_connector @@ -460,39 +469,40 @@ impl ChainListener { async fn refresh_compute_units(&mut self) -> eyre::Result<()> { let mut units = self.chain_connector.get_compute_units().await?; - let in_deal: Vec<_> = units.extract_if(|cu| cu.deal.is_some()).collect(); + let in_deal: Vec<_> = units.extract_if(|cu| !cu.deal.is_zero()).collect(); - let (active, pending): (Vec, Vec) = units - .into_iter() - .partition(|unit| unit.start_epoch <= self.current_epoch); - - self.active_compute_units - .extend(active.into_iter().map(|unit| unit.id)); - self.pending_compute_units - .extend(pending.into_iter().map(PendingUnit::from)); + self.cc_compute_units + .extend(units.into_iter().map(|unit| (CUID::new(unit.id.0), unit))); for cu in in_deal { - if let Some(deal) = cu.deal { - self.active_deals.insert(deal, cu.id); + if !cu.deal.is_zero() { + self.active_deals + .insert(cu.deal.to_string().into(), CUID::new(cu.id.0)); } } + let active = self + .cc_compute_units + .values() + .filter(|unit| unit.startEpoch <= self.current_epoch); + let pending = self + .cc_compute_units + .values() + .filter(|unit| unit.startEpoch > self.current_epoch); tracing::info!(target: "chain-listener", - "Compute units mapping: active {}, pending {}, in deal {}", - self.active_compute_units.len(), - self.pending_compute_units.len(), + "Compute units mapping: in cc {}/[{} pending], in deal {}", + self.cc_compute_units.len(), + pending.clone().count(), self.active_deals.len() ); - // TODO: log compute units pretty tracing::info!(target: "chain-listener", "Active compute units: {:?}", - self.active_compute_units.iter().map(CUID::to_string).collect::>() + active.map(|cu| cu.id.to_string()).collect::>() ); tracing::info!(target: "chain-listener", "Pending compute units: {:?}", - self.pending_compute_units - .iter() + pending .map(|cu| cu.id.to_string()) .collect::>() ); @@ -511,7 +521,7 @@ impl ChainListener { let subs = self .ws_client .subscribe("eth_subscribe", rpc_params!["newHeads"], "eth_unsubscribe").await.map_err(|err| { - tracing::error!(target: "chain-listener", "Failed to subscribe to newHeads: {err}; Retrying..."); + tracing::warn!(target: "chain-listener", "Failed to subscribe to newHeads: {err}; Retrying..."); eyre!(err) })?; @@ -525,8 +535,9 @@ impl ChainListener { async fn subscribe_cc_activated(&mut self) -> eyre::Result<()> { let sub = retry(ExponentialBackoff::default(), || async { + let topic = CommitmentActivated::SIGNATURE_HASH.to_string(); let topics = vec![ - CommitmentActivatedData::topic(), + topic, peer_id_to_hex(self.host_id), ]; let params = rpc_params![ @@ -537,7 +548,7 @@ impl ChainListener { .ws_client .subscribe("eth_subscribe", params, "eth_unsubscribe") .await.map_err(|err| { - tracing::error!(target: "chain-listener", "Failed to subscribe to cc events: {err}; Retrying..."); + tracing::warn!(target: "chain-listener", "Failed to subscribe to cc events: {err}; Retrying..."); eyre!(err) })?; @@ -552,13 +563,14 @@ impl ChainListener { async fn subscribe_unit_activated(&mut self) -> eyre::Result<()> { if let Some(c_id) = self.current_commitment.as_ref() { let sub = retry(ExponentialBackoff::default(), || async { + let topic = UnitActivated::SIGNATURE_HASH.to_string(); let params = rpc_params!["logs", - json!({"address": self.config.cc_contract_address, "topics": vec![UnitActivatedData::topic(), hex::encode(&c_id.0)]})]; + json!({"address": self.config.cc_contract_address, "topics": vec![topic, hex::encode(c_id.0)]})]; let subs = self .ws_client .subscribe("eth_subscribe", params, "eth_unsubscribe") .await.map_err(|err| { - tracing::error!(target: "chain-listener", "Failed to subscribe to unit activated: {err}; Retrying..."); + tracing::warn!(target: "chain-listener", "Failed to subscribe to unit activated: {err}; Retrying..."); eyre!(err) })?; @@ -573,33 +585,39 @@ impl ChainListener { } async fn subscribe_unit_deactivated(&mut self) -> eyre::Result<()> { - if let Some(c_id) = self.current_commitment.as_ref() { - let sub = retry(ExponentialBackoff::default(), || async { - let params = rpc_params![ - "logs", - json!({"address": self.config.cc_contract_address, "topics": vec![UnitDeactivatedData::topic(), hex::encode(&c_id.0)]}) - ]; - let subs = self - .ws_client - .subscribe("eth_subscribe", params, "eth_unsubscribe") - .await.map_err(|err| { - tracing::error!(target: "chain-listener", "Failed to subscribe to unit deactivated: {err}; Retrying..."); - eyre!(err) - })?; + let commitment_id = match &self.current_commitment { + Some(c_id) => c_id, + None => { + return Ok(()); + } + }; - Ok(subs) - }).await?; + let sub = retry(ExponentialBackoff::default(), || async { + let topic = UnitDeactivated::SIGNATURE_HASH.to_string(); + let params = rpc_params![ + "logs", + json!({"address": self.config.cc_contract_address, "topics": vec![topic, commitment_id.to_string()]}) + ]; + let subs = self + .ws_client + .subscribe("eth_subscribe", params, "eth_unsubscribe") + .await.map_err(|err| { + tracing::warn!(target: "chain-listener", "Failed to subscribe to unit deactivated: {err}; Retrying..."); + eyre!(err) + })?; - self.unit_deactivated = Some(sub); - } + Ok(subs) + }).await?; + + self.unit_deactivated = Some(sub); Ok(()) } - async fn subscribe_deal_matched(&mut self) -> eyre::Result<()> { + async fn subscribe_unit_matched(&mut self) -> eyre::Result<()> { let sub = retry(ExponentialBackoff::default(), || async { let topics = vec![ - DealMatchedData::topic(), + ComputeUnitMatched::SIGNATURE_HASH.to_string(), peer_id_to_hex(self.host_id), ]; let params = rpc_params![ @@ -610,7 +628,7 @@ impl ChainListener { .ws_client .subscribe("eth_subscribe", params, "eth_unsubscribe") .await.map_err(|err| { - tracing::error!(target: "chain-listener", "Failed to subscribe to deal matched: {err}; Retrying..."); + tracing::warn!(target: "chain-listener", "Failed to subscribe to deal matched: {err}; Retrying..."); eyre!(err) })?; @@ -639,9 +657,6 @@ impl ChainListener { // TODO: add epoch_number to metrics tracing::info!(target: "chain-listener", "Epoch changed, new epoch number: {epoch_number}"); - tracing::info!(target: "chain-listener", "Resetting proof id counter"); - self.reset_proof_id().await?; - // nonce changes every epoch self.global_nonce = self.chain_connector.get_global_nonce().await?; tracing::info!(target: "chain-listener", @@ -649,24 +664,24 @@ impl ChainListener { self.global_nonce ); + self.current_epoch = epoch_number; + tracing::info!(target: "chain-listener", "Resetting proof id counter"); + self.reset_proof_id().await?; + self.proof_counter.clear(); + if let Some(status) = self.get_commitment_status().await? { - tracing::info!(target: "chain-listener", "Current commitment status: {status}"); + tracing::info!(target: "chain-listener", "Current commitment status: {status:?}"); match status { - CommitmentStatus::Active => { - self.activate_pending_units(epoch_number).await?; + CCStatus::Active => { + self.refresh_commitment().await?; } - CommitmentStatus::Inactive - | CommitmentStatus::Failed - | CommitmentStatus::Removed => { + CCStatus::Inactive | CCStatus::Failed | CCStatus::Removed => { self.reset_commitment().await?; } - CommitmentStatus::WaitDelegation => {} - CommitmentStatus::WaitStart => {} + _ => {} } } - - self.current_epoch = epoch_number; } Ok(()) @@ -684,35 +699,38 @@ impl ChainListener { err })?; - let cc_event = parse_log::(log)?; - let unit_ids = cc_event.info.unit_ids; + let cc_event = parse_log::(log)?; + let unit_ids = cc_event.unitIds; tracing::info!(target: "chain-listener", "Received CommitmentActivated event for commitment: {}, startEpoch: {}, unitIds: {:?}", - cc_event.info.commitment_id, - cc_event.info.start_epoch, + cc_event.commitmentId.to_string(), + cc_event.startEpoch, unit_ids .iter() - .map(CUID::to_string) + .map(FixedBytes::to_string) .collect::>() ); - self.current_commitment = Some(cc_event.info.commitment_id); + self.current_commitment = Some(CommitmentId(cc_event.commitmentId.0)); self.subscribe_unit_activated().await?; self.subscribe_unit_deactivated().await?; - let is_cc_active = cc_event.info.start_epoch <= self.current_epoch; + self.cc_compute_units = unit_ids + .into_iter() + .map(|id| { + ( + CUID::new(id.0), + ComputeUnit { + id, + deal: Address::ZERO, + startEpoch: cc_event.startEpoch, + }, + ) + }) + .collect(); - if is_cc_active { - self.active_compute_units = unit_ids.into_iter().collect(); - self.refresh_commitment().await?; - } else { - self.pending_compute_units = unit_ids - .into_iter() - .map(|id| PendingUnit::new(id, cc_event.info.start_epoch)) - .collect(); - self.stop_commitment().await?; - } + self.refresh_commitment().await?; Ok(()) } @@ -728,19 +746,21 @@ impl ChainListener { err })?; - let unit_event = parse_log::(log)?; + let unit_event = parse_log::(log)?; tracing::info!(target: "chain-listener", "Received UnitActivated event for unit: {}, startEpoch: {}", - unit_event.info.unit_id, - unit_event.info.start_epoch + unit_event.unitId, + unit_event.startEpoch ); - if self.current_epoch >= unit_event.info.start_epoch { - self.active_compute_units.insert(unit_event.info.unit_id); - } else { - // Will be activated on the `start_epoch` - self.pending_compute_units.insert(unit_event.info.into()); - } + self.cc_compute_units.insert( + CUID::new(unit_event.unitId.0), + ComputeUnit { + id: unit_event.unitId, + deal: Address::ZERO, + startEpoch: unit_event.startEpoch, + }, + ); self.refresh_commitment().await?; Ok(()) @@ -756,21 +776,19 @@ impl ChainListener { tracing::error!(target: "chain-listener", "Failed to parse UnitDeactivated event: {err}, data: {event}"); err })?; - let unit_event = parse_log::(log)?; - + let unit_event = parse_log::(log)?; + let unit_id = CUID::new(unit_event.unitId.0); tracing::info!(target: "chain-listener", "Received UnitDeactivated event for unit: {}", - unit_event.info.unit_id + unit_event.unitId.to_string() ); - self.active_compute_units.remove(&unit_event.info.unit_id); - self.pending_compute_units - .retain(|cu| cu.id != unit_event.info.unit_id); + self.cc_compute_units.remove(&unit_id); self.refresh_commitment().await?; - self.acquire_deal_core(unit_event.info.unit_id)?; + self.acquire_core_for_deal(unit_id)?; Ok(()) } - pub fn process_deal_matched( + pub fn process_unit_matched( &mut self, event: Option>, ) -> eyre::Result<()> { @@ -779,88 +797,157 @@ impl ChainListener { tracing::error!(target: "chain-listener", "Failed to parse DealMatched event: {err}, data: {event}"); err })?; - let deal_event = parse_log::(log)?; + let deal_event = parse_log::(log)?; tracing::info!(target: "chain-listener", "Received DealMatched event for deal: {}", - deal_event.info.deal_id + deal_event.deal ); - self.active_deals - .insert(deal_event.info.deal_id, deal_event.info.unit_id); + self.active_deals.insert( + deal_event.deal.to_string().into(), + CUID::new(deal_event.unitId.0), + ); Ok(()) } + /// Return already started units involved in CC and not having less than MIN_PROOFS_PER_EPOCH proofs in the current epoch + fn get_priority_units(&self) -> Vec { + self.cc_compute_units + .iter() + .filter(|(_, cu)| cu.startEpoch <= self.current_epoch) // CU is already started + .filter(|(cuid, _)| { + self.proof_counter + .get(cuid) + .map(|count| *count < self.min_proofs_per_epoch) + .unwrap_or(true) + }) + .map(|(cuid, _)| *cuid) + .collect() + } + + /// Return already started units involved in CC and found at least MIN_PROOFS_PER_EPOCH proofs, + /// but less that MAX_PROOFS_PER_EPOCH proofs in the current epoch + fn get_non_priority_units(&self) -> Vec { + self.cc_compute_units + .iter() + .filter(|(_, cu)| cu.startEpoch <= self.current_epoch) // CU is already started + .filter(|(cuid, _)| { + self.proof_counter + .get(cuid) + .map(|count| { + *count >= self.min_proofs_per_epoch && *count < self.max_proofs_per_epoch + }) + .unwrap_or(true) + }) + .map(|(cuid, _)| *cuid) + .collect() + } + + /// Return units in CC that is not active yet and can't produce proofs in the current epoch + fn get_pending_units(&self) -> Vec { + self.cc_compute_units + .values() + .filter(|cu| cu.startEpoch > self.current_epoch) // CU hasn't yet started + .map(|cu| CUID::new(cu.id.0)) + .collect() + } + /// Send GlobalNonce, Difficulty and Core<>CUID mapping (full commitment info) to CCP async fn refresh_commitment(&self) -> eyre::Result<()> { - if self.active_compute_units.is_empty() { + if self.cc_compute_units.is_empty() || self.current_commitment.is_none() { self.stop_commitment().await?; return Ok(()); } tracing::info!(target: "chain-listener", "Refreshing commitment, active compute units: {}", - self.active_compute_units - .iter() + self.cc_compute_units + .keys() .map(CUID::to_string) .collect::>() .join(", ") ); tracing::info!(target: "chain-listener", "Global nonce: {}", self.global_nonce); tracing::info!(target: "chain-listener", "Difficulty: {}", self.difficulty); - if let Some(ref ccp_client) = self.ccp_client { - let mut cores = self.acquire_active_units()?; - // All pending units not involved in deals will help to solve CCs for other units - tracing::info!(target: "chain-listener", - "Pending compute units: {:?}", - self.pending_compute_units - .iter() - .map(|cu| cu.id.to_string()) - .collect::>() - ); - let mut available_cores = self.get_available_cores()?; - tracing::info!( - target: "chain-listener", - "{} cores of pending units will be allocated to other units", - available_cores.len() - ); + let ccp_client = match &self.ccp_client { + Some(ccp_client) => ccp_client, + None => return Ok(()), + }; + + let priority_units = self.get_priority_units(); + let non_priority_units = self.get_non_priority_units(); + let pending_units = self.get_pending_units(); + + let mut cu_allocation = HashMap::new(); + let priority_cores = self.acquire_cores_for_cc(&priority_units)?; + let non_priority_cores = self.acquire_cores_for_cc(&non_priority_units)?; + let pending_cores = self.acquire_cores_for_cc(&pending_units)?; + + if all_min_proofs_found(&priority_units) { + tracing::info!(target: "chain-listener", "All CUs found minimal number of proofs {} in current epoch {}", self.min_proofs_per_epoch, self.current_epoch); + if all_max_proofs_found(&non_priority_units) { + tracing::info!(target: "chain-listener", "All CUs found max number of proofs {} in current epoch {}", self.max_proofs_per_epoch ,self.current_epoch); + self.stop_commitment().await?; + return Ok(()); + } else { + // All CUs were proven, now let's work on submitting proofs for every CU until MAX_PROOF_COUNT is reached + cu_allocation.extend(non_priority_cores.iter().zip(non_priority_units.iter())); + + let mut units = non_priority_units.iter().cycle(); + // Assign "pending cores" to help generate proofs for "non priority units" + pending_cores.iter().for_each(|core| { + if let Some(unit) = units.next() { + cu_allocation.insert(*core, *unit); + } + }); + } + } else { + // Use assigned cores to calculate proofs for CUs who haven't reached MIN_PROOF_COUNT yet + cu_allocation.extend(priority_cores.iter().zip(priority_units.iter())); - for unit in self.active_compute_units.iter().cycle() { - if let Some(core) = available_cores.pop_first() { - cores.insert(core, *unit); - } else { - break; + // Use all spare cores to help CUs to reach MIN_PROOF_COUNT + let spare_cores: BTreeSet<_> = non_priority_cores + .into_iter() + .chain(pending_cores.into_iter()) + .collect(); + + let mut units = priority_units.iter().cycle(); + spare_cores.iter().for_each(|core| { + if let Some(unit) = units.next() { + cu_allocation.insert(*core, *unit); } - } + }); + } - tracing::info!(target: "chain-listener", - "Sending commitment to CCP: global_nonce: {}, difficulty: {}, cores: {:?}", + tracing::info!(target: "chain-listener", + "Sending commitment to CCP: global_nonce: {}, difficulty: {}, cores: {:?}", + self.global_nonce, + self.difficulty, + cu_allocation.iter().map(|(core, unit)| format!("{}: {}", core, unit)) + .collect::>() + ); + + ccp_client + .on_active_commitment( self.global_nonce, self.difficulty, - cores.iter().map(|(core, unit)| format!("{}: {}", core, unit.to_string())) - .collect::>() - ); + cu_allocation, + ) + .await + .map_err(|err| { + tracing::error!(target: "chain-listener", "Failed to send commitment to CCP: {err}"); + eyre::eyre!("Failed to send commitment to CCP: {err}") + })?; - ccp_client - .on_active_commitment( - self.global_nonce, - self.difficulty, - cores, - ) - .await - .map_err(|err| { - tracing::error!(target: "chain-listener", "Failed to send commitment to CCP: {err}"); - eyre::eyre!("Failed to send commitment to CCP: {err}") - })?; - } Ok(()) } - fn acquire_active_units(&self) -> eyre::Result> { + fn acquire_cores_for_cc(&self, units: &[CUID]) -> eyre::Result> { let cores = self .core_manager .acquire_worker_core(AcquireRequest::new( - self.active_compute_units.clone().into_iter().collect(), + units.to_vec(), WorkType::CapacityCommitment, )) .map_err(|err| { @@ -868,14 +955,10 @@ impl ChainListener { eyre::eyre!("Failed to acquire cores for active units: {err}") })?; - Ok(cores - .physical_core_ids - .into_iter() - .zip(self.active_compute_units.clone().into_iter()) - .collect()) + Ok(cores.physical_core_ids) } - fn acquire_deal_core(&self, unit_id: CUID) -> eyre::Result<()> { + fn acquire_core_for_deal(&self, unit_id: CUID) -> eyre::Result<()> { self.core_manager .acquire_worker_core(AcquireRequest::new(vec![unit_id], WorkType::Deal))?; Ok(()) @@ -883,8 +966,7 @@ impl ChainListener { /// Should be called only if Commitment is Inactive, Failed, Removed or not exists async fn reset_commitment(&mut self) -> eyre::Result<()> { - self.active_compute_units.clear(); - self.pending_compute_units.clear(); + self.cc_compute_units.clear(); self.active_deals.clear(); self.current_commitment = None; self.stop_commitment().await?; @@ -902,27 +984,6 @@ impl ChainListener { Ok(()) } - async fn activate_pending_units(&mut self, new_epoch: U256) -> eyre::Result<()> { - let to_activate: Vec<_> = self - .pending_compute_units - .extract_if(|unit| unit.start_epoch <= new_epoch) - .map(|cu| cu.id) - .collect(); - - tracing::info!(target: "chain-listener", - "Activating pending compute units: [{}]", - to_activate - .iter() - .map(CUID::to_string) - .collect::>() - .join(", ") - ); - - self.active_compute_units.extend(to_activate); - self.refresh_commitment().await?; - Ok(()) - } - /// Submit Mocked Proofs for all active compute units. /// Mocked Proof has result_hash == difficulty and random local_nonce async fn submit_mocked_proofs(&mut self) -> eyre::Result<()> { @@ -934,7 +995,8 @@ impl ChainListener { // proof_id is used only by CCP and is not sent to chain let proof_id = CCProofId::new(self.global_nonce, self.difficulty, ProofIdx::zero()); - for unit in self.active_compute_units.clone().into_iter() { + let units = self.cc_compute_units.keys().cloned().collect::>(); + for unit in units { let local_nonce = LocalNonce::random(); self.submit_proof(CCProof::new(proof_id, local_nonce, unit, result_hash)) .await?; @@ -944,12 +1006,12 @@ impl ChainListener { } async fn poll_proofs(&mut self) -> eyre::Result<()> { - if self.current_commitment.is_none() || self.active_compute_units.is_empty() { + if self.current_commitment.is_none() || self.cc_compute_units.is_empty() { return Ok(()); } if let Some(ref ccp_client) = self.ccp_client { - tracing::info!(target: "chain-listener", "Polling proofs after: {}", self.last_submitted_proof_id); + tracing::trace!(target: "chain-listener", "Polling proofs after: {}", self.last_submitted_proof_id); let proofs = ccp_client .get_proofs_after(self.last_submitted_proof_id, PROOF_POLL_LIMIT) @@ -963,9 +1025,12 @@ impl ChainListener { .filter(|p| p.id.global_nonce == self.global_nonce) .collect(); - tracing::info!(target: "chain-listener", "Found {} proofs from polling", proofs.len()); + if proofs.len() > 0 { + tracing::info!(target: "chain-listener", "Found {} proofs from polling", proofs.len()); + } + for proof in proofs.into_iter() { - let id = proof.id.idx.clone(); + let id = proof.id.idx; tracing::info!(target: "chain-listener", "Submitting proof: {id}"); self.submit_proof(proof).await?; self.set_proof_id(proof.id.idx).await?; @@ -975,7 +1040,7 @@ impl ChainListener { } async fn submit_proof(&mut self, proof: CCProof) -> eyre::Result<()> { - if !self.active_compute_units.contains(&proof.cu_id) { + if !self.cc_compute_units.contains_key(&proof.cu_id) { return Ok(()); } @@ -984,7 +1049,7 @@ impl ChainListener { match err { ConnectorError::RpcCallError { .. } => { Permanent(err) } _ => { - tracing::error!(target: "chain-listener", "Failed to submit proof: {err}. Retrying.."); + tracing::warn!(target: "chain-listener", "Failed to submit proof: {err}. Retrying.."); backoff::Error::transient(err) } } @@ -997,20 +1062,19 @@ impl ChainListener { match err { ConnectorError::RpcCallError { ref data, .. } => { // TODO: track proofs count per epoch and stop at maxProofsPerEpoch - if data.contains(TOO_MANY_PROOFS) { - tracing::info!(target: "chain-listener", "Too many proofs found for compute unit {}, stopping until next epoch", proof.cu_id); + if is_too_many_proofs(data) { + tracing::info!(target: "chain-listener", "Too many proofs found for compute unit {}", proof.cu_id); - self.active_compute_units.remove(&proof.cu_id); - self.pending_compute_units - .insert(PendingUnit::new(proof.cu_id, self.current_epoch + 1)); + self.proof_counter + .insert(proof.cu_id, self.max_proofs_per_epoch); self.refresh_commitment().await?; Ok(()) - } else if data.contains(COMMITMENT_IS_NOT_ACTIVE) { + } else if is_commitment_not_active(data) { tracing::info!(target: "chain-listener", "Submit proof returned commitment is not active error"); let status = self.get_commitment_status().await?; if let Some(status) = status { - tracing::info!(target: "chain-listener", "Current commitment status: {status}"); + tracing::info!(target: "chain-listener", "Current commitment status: {status:?}"); } self.reset_commitment().await?; @@ -1033,6 +1097,7 @@ impl ChainListener { } Ok(tx_id) => { tracing::info!(target: "chain-listener", "Submitted proof {}, txHash: {tx_id}", proof.id.idx); + self.pending_proof_txs.push((tx_id, proof.cu_id)); Ok(()) } } @@ -1059,11 +1124,9 @@ impl ChainListener { ))? .to_string(); - Ok(( - U256::from_str_radix(×tamp, 16)?, - U256::from_str_radix(&block_number, 16)?, - )) + Ok((U256::from_str(×tamp)?, U256::from_str(&block_number)?)) } + async fn poll_deal_statuses(&mut self) -> eyre::Result<()> { if self.active_deals.is_empty() { return Ok(()); @@ -1071,7 +1134,7 @@ impl ChainListener { let statuses = retry(ExponentialBackoff::default(), || async { let s = self.chain_connector.get_deal_statuses(self.active_deals.keys()).await.map_err(|err| { - tracing::error!(target: "chain-listener", "Failed to poll deal statuses: {err}"); + tracing::warn!(target: "chain-listener", "Failed to poll deal statuses: {err}; Retrying..."); eyre!("Failed to poll deal statuses: {err}; Retrying...") })?; @@ -1085,14 +1148,12 @@ impl ChainListener { { match status { Ok(status) => match status { - DealStatus::InsufficientFunds | DealStatus::Ended => { - tracing::info!(target: "chain-listener", "Deal {deal_id} status: {status}; Exiting..."); + Deal::Status::INSUFFICIENT_FUNDS | Deal::Status::ENDED => { + tracing::info!(target: "chain-listener", "Deal {deal_id} status: {status:?}; Exiting..."); self.exit_deal(&deal_id, cu_id).await?; tracing::info!(target: "chain-listener", "Exited deal {deal_id} successfully"); } - DealStatus::Active - | DealStatus::NotEnoughWorkers - | DealStatus::SmallBalance => {} + _ => {} }, Err(err) => { tracing::error!(target: "chain-listener", "Failed to get deal status for {deal_id}: {err}"); @@ -1102,13 +1163,16 @@ impl ChainListener { Ok(()) } + async fn exit_deal(&mut self, deal_id: &DealId, cu_id: CUID) -> eyre::Result<()> { - let mut backoff = ExponentialBackoff::default(); - backoff.max_elapsed_time = Some(Duration::from_secs(3)); + let backoff = ExponentialBackoff { + max_elapsed_time: Some(Duration::from_secs(3)), + ..ExponentialBackoff::default() + }; retry(backoff, || async { self.chain_connector.exit_deal(&cu_id).await.map_err(|err| { - tracing::error!(target: "chain-listener", "Failed to exit deal {deal_id}: {err}"); + tracing::warn!(target: "chain-listener", "Failed to exit deal {deal_id}: {err}"); eyre!("Failed to exit deal {deal_id}: {err}; Retrying...") })?; Ok(()) @@ -1118,13 +1182,76 @@ impl ChainListener { self.active_deals.remove(deal_id); Ok(()) } - fn get_available_cores(&self) -> eyre::Result> { - let available_units = self.pending_compute_units.iter().map(|cu| cu.id).collect(); - self.core_manager.acquire_worker_core(AcquireRequest::new(available_units, WorkType::CapacityCommitment)) - .map(|acquired| acquired.physical_core_ids) - .map_err(|err| { - tracing::error!(target: "chain-listener", "Failed to acquire cores for active units: {err}"); - eyre::eyre!("Failed to acquire cores for active units: {err}") - }) + + async fn poll_pending_proof_txs(&mut self) -> eyre::Result<()> { + if self.pending_proof_txs.is_empty() { + return Ok(()); + } + + let statuses = retry(ExponentialBackoff::default(), || async { + let s = self.chain_connector.get_tx_statuses(self.pending_proof_txs.iter().map(|(tx, _)| tx)).await.map_err(|err| { + tracing::warn!(target: "chain-listener", "Failed to poll pending proof txs statuses: {err}"); + eyre!("Failed to poll pending proof txs statuses: {err}; Retrying...") + })?; + + Ok(s) + }) + .await?; + + let mut refresh_neeeded = false; + let mut stats_updated = false; + for (status, (tx_hash, cu_id)) in statuses + .into_iter() + .zip(self.pending_proof_txs.clone().into_iter()) + { + match status { + Ok(Some(status)) => { + if status { + tracing::info!(target: "chain-listener", "Proof tx {tx_hash} confirmed"); + stats_updated = true; + let counter = self + .proof_counter + .entry(cu_id) + .and_modify(|c| { + *c = c.add(Uint::from(1)); + }) + .or_insert(Uint::from(1)); + + if *counter >= self.min_proofs_per_epoch { + tracing::info!(target: "chain-listener", "Compute unit {cu_id} submitted enough proofs"); + refresh_neeeded = true; + } + } else { + tracing::warn!(target: "chain-listener", "Proof tx {tx_hash} not confirmed"); + } + + self.pending_proof_txs.retain(|(tx, _)| tx != &tx_hash); + } + Ok(None) => { + tracing::debug!(target: "chain-listener", "Proof tx {tx_hash} not found"); + } + Err(err) => { + tracing::debug!(target: "chain-listener", "Failed to get tx receipt for {tx_hash}: {err}"); + } + } + } + + if refresh_neeeded { + self.refresh_commitment().await?; + } + + if stats_updated { + tracing::info!(target: "chain-listener", "Confirmed proofs count: {:?}", self.proof_counter.iter().map(|(cu, count)| format!("{}: {}", cu, count.to_string())).collect::>()); + } + + Ok(()) } } + +fn all_min_proofs_found(priority_units: &[CUID]) -> bool { + priority_units.is_empty() +} + +fn all_max_proofs_found(non_priority_units: &[CUID]) -> bool { + non_priority_units.is_empty() +} diff --git a/crates/chain-listener/src/persistence.rs b/crates/chain-listener/src/persistence.rs index 8ae19d10cd..e0fb0b93d9 100644 --- a/crates/chain-listener/src/persistence.rs +++ b/crates/chain-listener/src/persistence.rs @@ -13,16 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +use alloy_primitives::U256; use eyre::Context; use std::path::Path; +use alloy_serde_macro::{U256_as_String, U256_from_String}; use ccp_shared::proof::ProofIdx; -use ethabi::ethereum_types::U256; use serde::{Deserialize, Serialize}; - #[derive(Serialize, Deserialize)] pub struct PersistedProofId { pub proof_id: ProofIdx, + #[serde( + serialize_with = "U256_as_String", + deserialize_with = "U256_from_String" + )] pub epoch: U256, } @@ -41,9 +45,9 @@ pub(crate) async fn persist_proof_id( epoch: current_epoch, }) .map_err(|err| eyre::eyre!("Proof id serialization failed {err}"))?; - Ok(tokio::fs::write(&path, bytes) + tokio::fs::write(&path, bytes) .await - .context(format!("error writing proof id to {}", path.display()))?) + .context(format!("error writing proof id to {}", path.display())) } pub(crate) async fn load_persisted_proof_id( diff --git a/crates/chain-types/Cargo.toml b/crates/chain-types/Cargo.toml deleted file mode 100644 index 28490bd5d6..0000000000 --- a/crates/chain-types/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "chain-types" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -serde = { workspace = true } -ethabi = { workspace = true } -eyre = { workspace = true } -chain-data = { workspace = true } -hex = { workspace = true } -ccp-shared = { workspace = true } -types = { workspace = true } - -[dev-dependencies] -tokio = { workspace = true, features = ["rt", "macros"] } diff --git a/crates/chain-types/src/commitment.rs b/crates/chain-types/src/commitment.rs deleted file mode 100644 index 3bd605e1bc..0000000000 --- a/crates/chain-types/src/commitment.rs +++ /dev/null @@ -1,72 +0,0 @@ -use crate::commitment_status::CommitmentStatus; -use chain_data::{next_opt, parse_chain_data, ChainDataError}; -use ethabi::ethereum_types::U256; -use ethabi::{ParamType, Token}; - -/// struct CommitmentView { -/// CCStatus status; -/// bytes32 peerId; -/// uint256 collateralPerUnit; -/// uint256 unitCount; -/// uint256 startEpoch; -/// uint256 endEpoch; -/// uint256 rewardDelegatorRate; -/// address delegator; -/// uint256 totalCUFailCount; -/// uint256 failedEpoch; -/// uint256 exitedUnitCount; -/// } -pub struct Commitment { - pub status: CommitmentStatus, - pub start_epoch: U256, - pub end_epoch: U256, -} - -impl Commitment { - pub fn signature() -> Vec { - vec![ - ParamType::Uint(8), // CCStatus status - ParamType::FixedBytes(32), // bytes32 peerId - ParamType::Uint(256), // uint256 collateralPerUnit - ParamType::Uint(256), // uint256 unitCount - ParamType::Uint(256), // uint256 startEpoch - ParamType::Uint(256), // uint256 endEpoch - ParamType::Uint(256), // uint256 rewardDelegatorRate - ParamType::Address, // address delegator - ParamType::Uint(256), // uint256 totalCUFailCount - ParamType::Uint(256), // uint256 failedEpoch - ParamType::Uint(256), // uint256 exitedUnitCount - ] - } - - pub fn from(data: &str) -> Result { - let mut tokens = parse_chain_data(data, &Self::signature())?.into_iter(); - let status = next_opt( - &mut tokens, - "commitment_status", - CommitmentStatus::from_token, - )?; - let mut tokens = tokens.skip(3); - let start_epoch = next_opt(&mut tokens, "start_epoch", Token::into_uint)?; - let end_epoch = next_opt(&mut tokens, "end_epoch", Token::into_uint)?; - Ok(Commitment { - status, - start_epoch, - end_epoch, - }) - } -} - -#[cfg(test)] -mod tests { - #[tokio::test] - async fn decode_commitment() { - let data = "0x00000000000000000000000000000000000000000000000000000000000000016497db93b32e4cdd979ada46a23249f444da1efb186cd74b9666bd03f710028b000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012c00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; - let commitment = super::Commitment::from(data); - assert!(commitment.is_ok()); - let commitment = commitment.unwrap(); - assert_eq!(commitment.status, super::CommitmentStatus::WaitDelegation); - assert_eq!(commitment.start_epoch, 0.into()); - assert_eq!(commitment.end_epoch, 300.into()); - } -} diff --git a/crates/chain-types/src/commitment_status.rs b/crates/chain-types/src/commitment_status.rs deleted file mode 100644 index 6b7b9a5fb1..0000000000 --- a/crates/chain-types/src/commitment_status.rs +++ /dev/null @@ -1,79 +0,0 @@ -use std::fmt::Display; - -use chain_data::{next_opt, parse_chain_data, ChainDataError}; - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum CommitmentStatus { - Active = 0, - // WaitDelegation - before collateral is deposited. - WaitDelegation, - // Status is WaitStart - means collateral deposited, and epoch should be proceed before Active. - WaitStart, - Inactive, - Failed, - Removed, -} - -impl CommitmentStatus { - pub fn signature() -> Vec { - vec![ethabi::ParamType::Uint(8)] - } - pub fn from_num(num: u8) -> Option { - match num { - 0 => Some(CommitmentStatus::Active), - 1 => Some(CommitmentStatus::WaitDelegation), - 2 => Some(CommitmentStatus::WaitStart), - 3 => Some(CommitmentStatus::Inactive), - 4 => Some(CommitmentStatus::Failed), - 5 => Some(CommitmentStatus::Removed), - _ => None, - } - } - - pub fn from_token(token: ethabi::Token) -> Option { - token - .into_uint() - .and_then(|u| Self::from_num(u.as_u64() as u8)) - } - - pub fn from(data: &str) -> Result { - let mut tokens = parse_chain_data(data, &Self::signature())?.into_iter(); - next_opt(&mut tokens, "commitment_status", Self::from_token) - } -} - -impl Display for CommitmentStatus { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let str = match self { - CommitmentStatus::Active => "Active", - CommitmentStatus::WaitDelegation => "WaitDelegation", - CommitmentStatus::WaitStart => "WaitStart", - CommitmentStatus::Inactive => "Inactive", - CommitmentStatus::Failed => "Failed", - CommitmentStatus::Removed => "Removed", - } - .to_string(); - write!(f, "{}", str) - } -} - -#[cfg(test)] -mod tests { - #[tokio::test] - async fn decode_commitment_status() { - let data = "0x0000000000000000000000000000000000000000000000000000000000000001"; - let status = super::CommitmentStatus::from(data); - assert!(status.is_ok()); - let status = status.unwrap(); - assert_eq!(status, super::CommitmentStatus::WaitDelegation); - } - - #[tokio::test] - async fn decode_commitment_status_removed() { - let data = "0x0000000000000000000000000000000000000000000000000000000000000005"; - let status = super::CommitmentStatus::from(data); - assert!(status.is_ok()); - let status = status.unwrap(); - assert_eq!(status, super::CommitmentStatus::Removed); - } -} diff --git a/crates/chain-types/src/compute_peer.rs b/crates/chain-types/src/compute_peer.rs deleted file mode 100644 index 18998671e8..0000000000 --- a/crates/chain-types/src/compute_peer.rs +++ /dev/null @@ -1,92 +0,0 @@ -use crate::types::CommitmentId; -use chain_data::{next_opt, parse_chain_data, ChainDataError}; -use ethabi::ethereum_types::U256; -use ethabi::Token; - -/// struct ComputePeer { -/// bytes32 offerId; -/// bytes32 commitmentId; -/// uint256 unitCount; -/// address owner; -/// } -pub struct ComputePeer { - pub offer_id: Vec, - pub commitment_id: Option, - pub unit_count: U256, - pub owner: String, -} - -impl ComputePeer { - pub fn signature() -> Vec { - vec![ - ethabi::ParamType::FixedBytes(32), - ethabi::ParamType::FixedBytes(32), - ethabi::ParamType::Uint(256), - ethabi::ParamType::Address, - ] - } - pub fn from(data: &str) -> Result { - let mut tokens = parse_chain_data(data, &Self::signature())?.into_iter(); - let offer_id = next_opt(&mut tokens, "offer_id", Token::into_fixed_bytes)?; - let commitment_id = next_opt(&mut tokens, "commitment_id", Token::into_fixed_bytes)?; - - let commitment_id = if commitment_id.iter().all(|&x| x == 0) { - None - } else { - Some(CommitmentId(commitment_id)) - }; - - let unit_count = next_opt(&mut tokens, "unit_count", Token::into_uint)?; - let owner = next_opt(&mut tokens, "owner", Token::into_address)?; - - Ok(Self { - offer_id, - commitment_id, - unit_count, - owner: format!("{owner:#x}"), - }) - } -} - -#[cfg(test)] -mod tests { - #[tokio::test] - async fn decode_compute_peer_no_commitment() { - let data = "0xaa3046a12a1aac6e840625e6329d70b427328fec36dc8d273e5e6454b85633d5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000005b73c5498c1e3b4dba84de0f1833c4a029d90519"; - let compute_peer = super::ComputePeer::from(data); - assert!(compute_peer.is_ok()); - let compute_peer = compute_peer.unwrap(); - assert_eq!( - hex::encode(compute_peer.offer_id), - "aa3046a12a1aac6e840625e6329d70b427328fec36dc8d273e5e6454b85633d5" - ); - assert_eq!(compute_peer.commitment_id, None); - assert_eq!(compute_peer.unit_count, 2.into()); - assert_eq!( - compute_peer.owner, - "0x5b73c5498c1e3b4dba84de0f1833c4a029d90519" - ); - } - - #[tokio::test] - async fn decode_compute_peer() { - let data = "0xaa3046a12a1aac6e840625e6329d70b427328fec36dc8d273e5e6454b85633d5aa3046a12a1aac6e840625e6329d70b427328feceedc8d273e5e6454b85633b5000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000005b73c5498c1e3b4dba84de0f1833c4a029d90519"; - let compute_peer = super::ComputePeer::from(data); - assert!(compute_peer.is_ok()); - let compute_peer = compute_peer.unwrap(); - assert_eq!( - hex::encode(compute_peer.offer_id), - "aa3046a12a1aac6e840625e6329d70b427328fec36dc8d273e5e6454b85633d5" - ); - assert!(compute_peer.commitment_id.is_some()); - assert_eq!( - hex::encode(compute_peer.commitment_id.unwrap().0), - "aa3046a12a1aac6e840625e6329d70b427328feceedc8d273e5e6454b85633b5" - ); - assert_eq!(compute_peer.unit_count, 10.into()); - assert_eq!( - compute_peer.owner, - "0x5b73c5498c1e3b4dba84de0f1833c4a029d90519" - ); - } -} diff --git a/crates/chain-types/src/compute_unit.rs b/crates/chain-types/src/compute_unit.rs deleted file mode 100644 index 663481cd21..0000000000 --- a/crates/chain-types/src/compute_unit.rs +++ /dev/null @@ -1,137 +0,0 @@ -use ccp_shared::types::CUID; -use chain_data::ChainDataError::InvalidParsedToken; -use chain_data::{next_opt, parse_chain_data, ChainDataError}; -use ethabi::ethereum_types::U256; -use ethabi::Token; -use types::DealId; - -/// struct ComputeUnitView { -/// bytes32 id; -/// address deal; -/// uint256 startEpoch; -/// } -#[derive(Debug, PartialEq, Eq, Hash)] -pub struct ComputeUnit { - pub id: CUID, - /// if deal is zero-address, it means the unit is not assigned to any deal - pub deal: Option, - pub start_epoch: U256, -} - -impl ComputeUnit { - pub fn new(id: CUID, start_epoch: U256) -> Self { - Self { - id, - deal: None, - start_epoch, - } - } - pub fn signature() -> Vec { - vec![ - ethabi::ParamType::FixedBytes(32), - ethabi::ParamType::Address, - ethabi::ParamType::Uint(256), - ] - } - - pub fn parse(data: &str) -> Result { - let mut tokens = parse_chain_data(data, &Self::signature())?.into_iter(); - Self::from_tokens(&mut tokens) - } - - pub fn from_token(token: Token) -> Result { - let mut tokens = next_opt( - &mut std::iter::once(token), - "compute_unit", - Token::into_tuple, - )? - .into_iter(); - Self::from_tokens(&mut tokens) - } - - pub fn from_tokens( - data_tokens: &mut impl Iterator, - ) -> Result { - let id = next_opt(data_tokens, "id", Token::into_fixed_bytes)?; - let deal = next_opt(data_tokens, "deal", Token::into_address)?; - - // if deal is zero-address, it means the unit is not assigned to any deal - let deal = if deal.is_zero() { - None - } else { - Some(format!("{deal:#x}").into()) - }; - - let start_epoch = next_opt(data_tokens, "start_epoch", Token::into_uint)?; - Ok(ComputeUnit { - id: CUID::new( - id.as_slice() - .try_into() - .map_err(|_| InvalidParsedToken("id"))?, - ), - deal, - start_epoch, - }) - } -} - -#[derive(Debug, PartialEq, Eq, Hash, Ord, PartialOrd)] -pub struct PendingUnit { - pub id: CUID, - pub start_epoch: U256, -} - -impl PendingUnit { - pub fn new(id: CUID, start_epoch: U256) -> Self { - Self { id, start_epoch } - } -} -impl From for PendingUnit { - fn from(unit: ComputeUnit) -> Self { - Self { - id: unit.id, - start_epoch: unit.start_epoch, - } - } -} - -#[cfg(test)] -mod tests { - use ccp_shared::types::CUID; - use hex::FromHex; - - #[tokio::test] - async fn decode_compute_unit() { - let data = "aa3046a12a1aac6e840625e6329d70b427328fec36dc8d273e5e6454b85633d50000000000000000000000005e3d0fde6f793b3115a9e7f5ebc195bbeed35d6c00000000000000000000000000000000000000000000000000000000000003e8"; - let compute_unit = super::ComputeUnit::parse(data); - assert!(compute_unit.is_ok()); - let compute_unit = compute_unit.unwrap(); - - assert_eq!( - compute_unit.id, - ::from_hex("aa3046a12a1aac6e840625e6329d70b427328fec36dc8d273e5e6454b85633d5") - .unwrap() - ); - assert!(compute_unit.deal.is_some()); - assert_eq!( - compute_unit.deal.unwrap(), - "0x5e3d0fde6f793b3115a9e7f5ebc195bbeed35d6c" - ); - assert_eq!(compute_unit.start_epoch, 1000.into()); - } - - #[tokio::test] - async fn decode_compute_unit_no_deal() { - let data = "aa3046a12a1aac6e840625e6329d70b427328fec36dc8d273e5e6454b85633d5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003e8"; - let compute_unit = super::ComputeUnit::parse(data); - assert!(compute_unit.is_ok()); - let compute_unit = compute_unit.unwrap(); - assert_eq!( - compute_unit.id, - ::from_hex("aa3046a12a1aac6e840625e6329d70b427328fec36dc8d273e5e6454b85633d5") - .unwrap() - ); - assert!(compute_unit.deal.is_none()); - assert_eq!(compute_unit.start_epoch, 1000.into()); - } -} diff --git a/crates/chain-types/src/deal_status.rs b/crates/chain-types/src/deal_status.rs deleted file mode 100644 index ce9d349199..0000000000 --- a/crates/chain-types/src/deal_status.rs +++ /dev/null @@ -1,57 +0,0 @@ -use std::fmt::Display; - -use chain_data::{next_opt, parse_chain_data, ChainDataError}; - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum DealStatus { - // Deal does have enough funds to pay for the workers - InsufficientFunds = 0, - Active, - // Deal is stopped - Ended, - // Deal has a balance and waiting for workers - NotEnoughWorkers, - // Deal has balance less than the minimal balance. Min balance: 2 * targetWorkers * pricePerWorkerEpoch - SmallBalance, -} - -impl DealStatus { - pub fn signature() -> Vec { - vec![ethabi::ParamType::Uint(8)] - } - pub fn from_num(num: u8) -> Option { - match num { - 0 => Some(DealStatus::InsufficientFunds), - 1 => Some(DealStatus::Active), - 2 => Some(DealStatus::Ended), - 3 => Some(DealStatus::NotEnoughWorkers), - 4 => Some(DealStatus::SmallBalance), - _ => None, - } - } - - pub fn from_token(token: ethabi::Token) -> Option { - token - .into_uint() - .and_then(|u| Self::from_num(u.as_u64() as u8)) - } - - pub fn from(data: &str) -> Result { - let mut tokens = parse_chain_data(data, &Self::signature())?.into_iter(); - next_opt(&mut tokens, "deal_status", Self::from_token) - } -} - -impl Display for DealStatus { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let str = match self { - DealStatus::Active => "Active", - DealStatus::Ended => "Ended", - DealStatus::NotEnoughWorkers => "NotEnoughWorkers", - DealStatus::SmallBalance => "SmallBalance", - DealStatus::InsufficientFunds => "InsufficientFunds", - } - .to_string(); - write!(f, "{}", str) - } -} diff --git a/crates/chain-types/src/errors.rs b/crates/chain-types/src/errors.rs deleted file mode 100644 index c52cc0037e..0000000000 --- a/crates/chain-types/src/errors.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub const COMMITMENT_IS_NOT_ACTIVE: &str = "0852c720"; -pub const TOO_MANY_PROOFS: &str = "e5d50da7"; - -/// "Peer doesn't exists" in Market.sol -pub const PEER_NOT_EXISTS: &str = "08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000125065657220646f65736e27742065786973740000000000000000000000000000"; diff --git a/crates/chain-types/src/lib.rs b/crates/chain-types/src/lib.rs deleted file mode 100644 index a4b958ffe7..0000000000 --- a/crates/chain-types/src/lib.rs +++ /dev/null @@ -1,15 +0,0 @@ -mod commitment; -mod commitment_status; -mod compute_peer; -mod compute_unit; -mod deal_status; -mod errors; -mod types; - -pub use commitment::Commitment; -pub use commitment_status::CommitmentStatus; -pub use compute_peer::ComputePeer; -pub use compute_unit::{ComputeUnit, PendingUnit}; -pub use deal_status::DealStatus; -pub use errors::*; -pub use types::*; diff --git a/crates/chain-types/src/types.rs b/crates/chain-types/src/types.rs deleted file mode 100644 index 23d057041c..0000000000 --- a/crates/chain-types/src/types.rs +++ /dev/null @@ -1,12 +0,0 @@ -use std::fmt::{Display, Formatter}; - -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)] -pub struct CommitmentId(pub Vec); - -impl Display for CommitmentId { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", hex::encode(&self.0)) - } -}