diff --git a/orchestrator/Cargo.lock b/orchestrator/Cargo.lock index 205341bfe..f7a96193c 100644 --- a/orchestrator/Cargo.lock +++ b/orchestrator/Cargo.lock @@ -2,6 +2,26 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static", + "regex", +] + +[[package]] +name = "abi_build" +version = "0.1.0" +dependencies = [ + "ethers", + "serde", + "serde_derive", + "serde_json", +] + [[package]] name = "abscissa_core" version = "0.6.0-beta.1" @@ -49,20 +69,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f38df9084daf34a7822ccc39e85598f23fd80e8451003ea41441ed5123fc012" dependencies = [ "abscissa_core", - "actix-rt 2.2.0", - "tokio 1.5.0", + "actix-rt 2.5.0", + "tokio 1.13.0", ] [[package]] name = "actix" -version = "0.11.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "543c47e7827f8fcc9d1445bd98ba402137bfce80ee2187429de49c52b5131bd3" +checksum = "3720d0064a0ce5c0de7bd93bdb0a6caebab2a9b5668746145d7b3b0c5da02914" dependencies = [ - "actix-rt 2.2.0", + "actix-rt 2.5.0", "actix_derive", "bitflags", - "bytes 1.0.1", + "bytes 1.1.0", "crossbeam-channel", "futures-core", "futures-sink", @@ -73,7 +93,7 @@ dependencies = [ "parking_lot", "pin-project-lite 0.2.6", "smallvec", - "tokio 1.5.0", + "tokio 1.13.0", "tokio-util 0.6.6", ] @@ -100,12 +120,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d5dbeb2d9e51344cb83ca7cc170f1217f9fe25bfc50160e6e200b5c31c1019a" dependencies = [ "bitflags", - "bytes 1.0.1", + "bytes 1.1.0", "futures-core", "futures-sink", "log", "pin-project-lite 0.2.6", - "tokio 1.5.0", + "tokio 1.13.0", "tokio-util 0.6.6", ] @@ -143,7 +163,7 @@ dependencies = [ "actix-threadpool", "actix-tls 2.0.0", "actix-utils 2.0.0", - "base64", + "base64 0.13.0", "bitflags", "brotli2", "bytes 0.5.6", @@ -167,7 +187,7 @@ dependencies = [ "log", "mime", "percent-encoding", - "pin-project 1.0.6", + "pin-project 1.0.8", "rand 0.7.3", "regex", "serde", @@ -185,14 +205,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd38a862fa7fead2b47ee55e550982aba583ebc7365ccf0155b49934ad6f16f9" dependencies = [ "actix-codec 0.4.0", - "actix-rt 2.2.0", + "actix-rt 2.5.0", "actix-service 2.0.0", "actix-tls 3.0.0-beta.5", "actix-utils 3.0.0", "ahash", - "base64", + "base64 0.13.0", "bitflags", - "bytes 1.0.1", + "bytes 1.1.0", "bytestring", "derive_more", "encoding_rs", @@ -209,15 +229,15 @@ dependencies = [ "mime", "once_cell", "percent-encoding", - "pin-project 1.0.6", + "pin-project 1.0.8", "pin-project-lite 0.2.6", - "rand 0.8.3", + "rand 0.8.4", "regex", "serde", "sha-1", "smallvec", "time 0.2.26", - "tokio 1.5.0", + "tokio 1.13.0", "zstd", ] @@ -233,9 +253,9 @@ dependencies = [ [[package]] name = "actix-macros" -version = "0.2.0" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbcb2b608f0accc2f5bcf3dd872194ce13d94ee45b571487035864cf966b04ef" +checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6" dependencies = [ "quote", "syn", @@ -271,13 +291,13 @@ dependencies = [ [[package]] name = "actix-rt" -version = "2.2.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc7d7cd957c9ed92288a7c3c96af81fa5291f65247a76a34dac7b6af74e52ba0" +checksum = "05c2f80ce8d0c990941c7a7a931f69fd0701b76d521f8d36298edf59cd3fbf1f" dependencies = [ - "actix-macros 0.2.0", + "actix-macros 0.2.3", "futures-core", - "tokio 1.5.0", + "tokio 1.13.0", ] [[package]] @@ -371,7 +391,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65b7bb60840962ef0332f7ea01a57d73a24d2cb663708511ff800250bbfef569" dependencies = [ "actix-codec 0.4.0", - "actix-rt 2.2.0", + "actix-rt 2.5.0", "actix-service 2.0.0", "actix-utils 3.0.0", "derive_more", @@ -442,7 +462,7 @@ dependencies = [ "log", "mime", "openssl", - "pin-project 1.0.6", + "pin-project 1.0.8", "regex", "serde", "serde_json", @@ -490,13 +510,25 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aes" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +dependencies = [ + "cfg-if 1.0.0", + "cipher", + "cpufeatures", + "opaque-debug 0.3.0", +] + [[package]] name = "ahash" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f200cbb1e856866d9eade941cf3aa0c5d7dd36f74311c4273b494f4ef036957" dependencies = [ - "getrandom 0.2.2", + "getrandom 0.2.3", "once_cell", "version_check", ] @@ -546,6 +578,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + [[package]] name = "async-stream" version = "0.3.0" @@ -569,15 +607,26 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.49" +version = "0.1.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589652ce7ccb335d1e7ecb3be145425702b290dbcb7029bbeaae263fc1d87b48" +checksum = "44318e776df68115a881de9a8fd1b9e53368d7a4a5ce4cc48517da3393233a5e" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "async_io_stream" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d7b9decdf35d8908a7e3ef02f64c5e9b1695e230154c0e8de3969142d9b94c" +dependencies = [ + "futures", + "pharos", + "rustc_version 0.4.0", +] + [[package]] name = "atty" version = "0.2.14" @@ -589,6 +638,18 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "auto_impl" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7862e21c893d65a1650125d157eaeec691439379a1cee17ee49031b79236ada4" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "autocfg" version = "1.0.1" @@ -605,7 +666,7 @@ dependencies = [ "actix-http 2.2.0", "actix-rt 1.1.1", "actix-service 1.0.6", - "base64", + "base64 0.13.0", "bytes 0.5.6", "cfg-if 1.0.0", "derive_more", @@ -628,10 +689,10 @@ checksum = "5b276021b5aa1df71969acc8adc03973e4fc7d00bba0cbb6338e6f8ad0d7a3c2" dependencies = [ "actix-codec 0.4.0", "actix-http 3.0.0-beta.10", - "actix-rt 2.2.0", + "actix-rt 2.5.0", "actix-service 2.0.0", - "base64", - "bytes 1.0.1", + "base64 0.13.0", + "bytes 1.1.0", "cfg-if 1.0.0", "derive_more", "futures-core", @@ -641,7 +702,7 @@ dependencies = [ "openssl", "percent-encoding", "pin-project-lite 0.2.6", - "rand 0.8.3", + "rand 0.8.4", "serde", "serde_json", "serde_urlencoded", @@ -654,17 +715,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24f143b3839608f1254ac928abeffea9d654d9732162dcb6f0dd92bc022672f1" dependencies = [ "async-trait", - "bytes 1.0.1", + "bytes 1.1.0", "futures-util", "http", "http-body", "hyper", - "pin-project 1.0.6", + "pin-project 1.0.8", "regex", "serde", "serde_json", "serde_urlencoded", - "tokio 1.5.0", + "tokio 1.13.0", "tokio-util 0.6.6", "tower", "tower-http", @@ -690,6 +751,28 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" +[[package]] +name = "base58" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5024ee8015f02155eee35c711107ddd9a9bf3cb689cf2a9089c97e79b6e1ae83" + +[[package]] +name = "base58check" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ee2fe4c9a0c84515f136aaae2466744a721af6d63339c18689d9e995d74d99b" +dependencies = [ + "base58", + "sha2 0.8.2", +] + +[[package]] +name = "base64" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" + [[package]] name = "base64" version = "0.13.0" @@ -714,6 +797,15 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c7f7096bc256f5e5cb960f60dfc4f4ef979ca65abe7fb9d5a4f77150d3783d4" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bip32" version = "0.2.2" @@ -725,7 +817,7 @@ dependencies = [ "hmac 0.11.0", "k256", "ripemd160", - "sha2", + "sha2 0.9.8", "subtle", "zeroize", ] @@ -736,14 +828,68 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "bitvec" +version = "0.17.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41262f11d771fd4a61aa3ce019fca363b4b6c282fca9da2a31186d3965a47a5c" +dependencies = [ + "either", + "radium 0.3.0", +] + +[[package]] +name = "bitvec" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7774144344a4faa177370406a7ff5f1da24303817368584c6206c8303eb07848" +dependencies = [ + "funty", + "radium 0.6.2", + "tap", + "wyz", +] + +[[package]] +name = "blake2" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a4e37d16930f5459780f5621038b6382b9bb37c19016f39fb6b5808d831f174" +dependencies = [ + "crypto-mac 0.8.0", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding 0.1.5", + "byte-tools", + "byteorder", + "generic-array 0.12.4", +] + [[package]] name = "block-buffer" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "block-padding", - "generic-array", + "block-padding 0.2.1", + "generic-array 0.14.4", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", ] [[package]] @@ -778,7 +924,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" dependencies = [ - "sha2", + "sha2 0.9.8", ] [[package]] @@ -787,6 +933,18 @@ version = "3.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe" +[[package]] +name = "byte-slice-cast" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d30c751592b77c499e7bce34d99d67c2c11bdc0574e9a488ddade14150a4698" + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + [[package]] name = "byteorder" version = "1.4.3" @@ -801,9 +959,12 @@ checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" [[package]] name = "bytes" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" +dependencies = [ + "serde", +] [[package]] name = "bytestring" @@ -811,7 +972,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90706ba19e97b90786e19dc0d5e2abd80008d99d4c0c5d1ad0b5e72cec7c494d" dependencies = [ - "bytes 1.0.1", + "bytes 1.1.0", +] + +[[package]] +name = "camino" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52d74260d9bf6944e2208aa46841b4b8f0d7ffc0849a06837b2f510337f86b2b" +dependencies = [ + "serde", ] [[package]] @@ -820,6 +990,28 @@ version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e9e01327e6c86e92ec72b1c798d4a94810f147209bbe3ffab6a86954937a6f" +[[package]] +name = "cargo-platform" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba2ae6de944143141f6155a473a6b02f66c7c3f9f47316f802f80204ebfe6e12" +dependencies = [ + "camino", + "cargo-platform", + "semver 1.0.4", + "serde", + "serde_json", +] + [[package]] name = "cc" version = "1.0.67" @@ -855,6 +1047,15 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "cipher" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +dependencies = [ + "generic-array 0.14.4", +] + [[package]] name = "clap" version = "3.0.0-beta.5" @@ -904,6 +1105,62 @@ dependencies = [ "sha3", ] +[[package]] +name = "coins-bip32" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01b669993c632e5fec4a297085ec57381f53e4646c123cb77a7ca754e005c921" +dependencies = [ + "bincode", + "bs58", + "coins-core", + "digest 0.9.0", + "hmac 0.11.0", + "k256", + "lazy_static", + "serde", + "sha2 0.9.8", + "thiserror", +] + +[[package]] +name = "coins-bip39" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38426029442f91bd49973d6f59f28e3dbb14e633e3019ac4ec6bce402c44f81c" +dependencies = [ + "bitvec 0.17.4", + "coins-bip32", + "getrandom 0.2.3", + "hex", + "hmac 0.11.0", + "pbkdf2 0.8.0", + "rand 0.8.4", + "sha2 0.9.8", + "thiserror", +] + +[[package]] +name = "coins-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d257d975731955ee86fa7f348000c3fea09c262e84c70c11e994a85aa4f467a7" +dependencies = [ + "base58check", + "base64 0.12.3", + "bech32 0.7.3", + "blake2", + "digest 0.9.0", + "generic-array 0.14.4", + "hex", + "ripemd160", + "serde", + "serde_derive", + "sha2 0.9.8", + "sha3", + "thiserror", +] + [[package]] name = "color-eyre" version = "0.5.11" @@ -917,12 +1174,29 @@ dependencies = [ "owo-colors", ] +[[package]] +name = "colored" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" +dependencies = [ + "atty", + "lazy_static", + "winapi 0.3.9", +] + [[package]] name = "const-oid" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d6f2aa4d0537bcc1c74df8755072bd31c1ef1a3a1b85a68e8404a8c353b7b8b" +[[package]] +name = "const-oid" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d355758f44afa81c21e66e1301d47cbffbbcde4b405cbe46b8b19f213abf9f60" + [[package]] name = "const_fn" version = "0.4.6" @@ -968,6 +1242,22 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2df960f5d869b2dd8532793fde43eb5427cceb126c929747a26823ab0eeb536" +[[package]] +name = "core-foundation" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + [[package]] name = "cosmos-sdk-proto" version = "0.6.3" @@ -985,26 +1275,35 @@ name = "cosmos_gravity" version = "0.1.0" dependencies = [ "actix", - "bytes 1.0.1", + "bytes 1.1.0", "clarity", "cosmos-sdk-proto", "deep_space 2.4.7", "env_logger", "ethereum_gravity", + "ethers", "gravity_proto", "gravity_utils", "log", - "num256", "prost", "prost-types", - "rand 0.8.3", + "rand 0.8.4", "serde", "sha3", - "tokio 1.5.0", + "tokio 1.13.0", "tonic", "web30", ] +[[package]] +name = "cpufeatures" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" +dependencies = [ + "libc", +] + [[package]] name = "cpuid-bool" version = "0.1.2" @@ -1041,25 +1340,53 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "crypto-bigint" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f83bd3bb4314701c568e340cd8cf78c975aa0ca79e03d3f6d1677d5b0c9c0c03" dependencies = [ - "generic-array", + "generic-array 0.14.4", + "rand_core 0.6.2", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-bigint" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" +dependencies = [ + "generic-array 0.14.4", "rand_core 0.6.2", "subtle", "zeroize", ] +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array 0.14.4", + "subtle", +] + [[package]] name = "crypto-mac" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4857fd85a0c34b3c3297875b747c1e02e06b6a0ea32dd892d8192b9ce0813ea6" dependencies = [ - "generic-array", + "generic-array 0.14.4", "subtle", ] @@ -1069,10 +1396,19 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25fab6889090c8133f3deb8f73ba3c65a7f456f66436fc012a1b1e272b1e103e" dependencies = [ - "generic-array", + "generic-array 0.14.4", "subtle", ] +[[package]] +name = "ctr" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a232f92a03f37dd7d7dd2adc67166c77e9cd88de5b019b9a9eecfaeaf7bfd481" +dependencies = [ + "cipher", +] + [[package]] name = "darling" version = "0.13.0" @@ -1114,7 +1450,7 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afd044e670beb23ce6a2007d139ed3787b99c85b5ff3982b8fdfda66c3682e53" dependencies = [ - "base64", + "base64 0.13.0", "bech32 0.7.3", "hmac 0.10.1", "num-bigint 0.3.2", @@ -1127,7 +1463,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "sha2", + "sha2 0.9.8", "unicode-normalization", ] @@ -1136,9 +1472,9 @@ name = "deep_space" version = "2.4.7" source = "git+https://github.com/iqlusioninc/deep_space/?branch=master#c6b31f758a4d4542f1272815be130e0904e2854d" dependencies = [ - "base64", + "base64 0.13.0", "bech32 0.8.0", - "bytes 1.0.1", + "bytes 1.1.0", "cosmos-sdk-proto", "hmac 0.11.0", "log", @@ -1148,16 +1484,16 @@ dependencies = [ "pbkdf2 0.9.0", "prost", "prost-types", - "rand 0.8.3", + "rand 0.8.4", "ripemd160", "rust_decimal", "secp256k1 0.20.1", "serde", "serde_derive", "serde_json", - "sha2", + "sha2 0.9.8", "tendermint-proto", - "tokio 1.5.0", + "tokio 1.13.0", "tonic", "unicode-normalization", ] @@ -1168,7 +1504,16 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28e98c534e9c8a0483aa01d6f6913bc063de254311bd267c9cf535e9b70e15b2" dependencies = [ - "const-oid", + "const-oid 0.6.2", +] + +[[package]] +name = "der" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" +dependencies = [ + "const-oid 0.7.0", ] [[package]] @@ -1183,13 +1528,22 @@ dependencies = [ "syn", ] +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array 0.12.4", +] + [[package]] name = "digest" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array", + "generic-array 0.14.4", ] [[package]] @@ -1216,8 +1570,8 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43ee23aa5b4f68c7a092b5c3beb25f50c406adc75e2363634f242f28ab255372" dependencies = [ - "der", - "elliptic-curve", + "der 0.4.4", + "elliptic-curve 0.10.6", "hmac 0.11.0", "signature", ] @@ -1234,9 +1588,9 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "beca177dcb8eb540133e7680baff45e7cc4d93bf22002676cec549f82343721b" dependencies = [ - "crypto-bigint", + "crypto-bigint 0.2.11", "ff", - "generic-array", + "generic-array 0.14.4", "group", "pkcs8", "rand_core 0.6.2", @@ -1245,60 +1599,353 @@ dependencies = [ ] [[package]] -name = "encoding_rs" -version = "0.8.28" +name = "elliptic-curve" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f73d21281779c2c22cade2a4ab34eee34271869942546a364f7d552d30c62434" +dependencies = [ + "crypto-bigint 0.3.2", + "der 0.5.1", + "generic-array 0.14.4", + "rand_core 0.6.2", + "subtle", + "zeroize", +] + +[[package]] +name = "encoding_rs" +version = "0.8.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80df024fbc5ac80f87dfef0d9f5209a252f2a497f7f42944cff24d8253cac065" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "enum-as-inner" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c5f0096a91d210159eceb2ff5e1c4da18388a170e1e3ce948aac9c8fdbbf595" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "env_logger" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17392a012ea30ef05a610aa97dfb49496e71c9f676b27879922ea5bdf60d9d3f" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "error" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6e606f14042bb87cc02ef6a14db6c90ab92ed6f62d87e69377bc759fd7987cc" +dependencies = [ + "traitobject", + "typeable", +] + +[[package]] +name = "eth-keystore" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d47d900a7dea08593d398104f8288e37858b0ad714c8d08cd03fdb86563e6402" +dependencies = [ + "aes", + "ctr", + "digest 0.9.0", + "hex", + "hmac 0.11.0", + "pbkdf2 0.8.0", + "rand 0.8.4", + "scrypt", + "serde", + "serde_json", + "sha2 0.9.8", + "sha3", + "thiserror", + "uuid", +] + +[[package]] +name = "ethabi" +version = "15.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f76ef192b63e8a44b3d08832acebbb984c3fba154b5c26f70037c860202a0d4b" +dependencies = [ + "anyhow", + "ethereum-types", + "hex", + "serde", + "serde_json", + "sha3", + "thiserror", + "uint", +] + +[[package]] +name = "ethbloom" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb684ac8fa8f6c5759f788862bb22ec6fe3cb392f6bfd08e3c64b603661e3f8" +dependencies = [ + "crunchy", + "fixed-hash", + "impl-rlp", + "impl-serde", + "tiny-keccak", +] + +[[package]] +name = "ethereum-types" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05136f7057fe789f06e6d41d07b34e6f70d8c86e5693b60f97aaa6553553bdaf" +dependencies = [ + "ethbloom", + "fixed-hash", + "impl-rlp", + "impl-serde", + "primitive-types", + "uint", +] + +[[package]] +name = "ethereum_gravity" +version = "0.1.0" +dependencies = [ + "clarity", + "deep_space 2.4.7", + "ethers", + "gravity_abi", + "gravity_utils", + "log", + "sha3", + "tokio 1.13.0", + "web30", +] + +[[package]] +name = "ethers" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39d16b7455aa6e3de419d9e7d270605f402f87ffdbcfcd1c2d036d4cfceafdba" +dependencies = [ + "ethers-contract", + "ethers-core", + "ethers-etherscan", + "ethers-middleware", + "ethers-providers", + "ethers-signers", + "ethers-solc", +] + +[[package]] +name = "ethers-contract" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edb2b42ed54282b47225563f8f8ecb5356337ecb6342cf7a6129421413574947" +dependencies = [ + "ethers-contract-abigen", + "ethers-contract-derive", + "ethers-core", + "ethers-providers", + "futures-util", + "hex", + "once_cell", + "pin-project 1.0.8", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "ethers-contract-abigen" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e02185a49bdd3bc4cdbb59164ab5a2c6c99eb50d1162d051d40e3ec7fd9b77b9" +dependencies = [ + "Inflector", + "anyhow", + "cfg-if 1.0.0", + "ethers-core", + "getrandom 0.2.3", + "hex", + "once_cell", + "proc-macro2", + "quote", + "reqwest", + "serde", + "serde_json", + "syn", + "url", +] + +[[package]] +name = "ethers-contract-derive" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbfe93d0ab5f7eb3656f78a56d73e8da4cdef11b22ded7a2015afc5fddc54d8f" +dependencies = [ + "ethers-contract-abigen", + "ethers-core", + "hex", + "proc-macro2", + "quote", + "serde_json", + "syn", +] + +[[package]] +name = "ethers-core" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e101557a375366496ecdc53b69a1bfdebf393b6c0691f5d667659e4580164b68" +dependencies = [ + "arrayvec 0.7.2", + "bytes 1.1.0", + "cargo_metadata", + "convert_case", + "ecdsa", + "elliptic-curve 0.11.1", + "ethabi", + "generic-array 0.14.4", + "hex", + "k256", + "once_cell", + "proc-macro2", + "quote", + "rand 0.8.4", + "rlp", + "rlp-derive", + "serde", + "serde_json", + "syn", + "thiserror", + "tiny-keccak", +] + +[[package]] +name = "ethers-etherscan" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80df024fbc5ac80f87dfef0d9f5209a252f2a497f7f42944cff24d8253cac065" +checksum = "75615bf40e98ad5100b5854608238d7f6cda6ba2779141b6d7c7d9f27246dfc4" dependencies = [ - "cfg-if 1.0.0", + "ethers-core", + "reqwest", + "serde", + "serde-aux", + "serde_json", + "thiserror", ] [[package]] -name = "enum-as-inner" -version = "0.3.3" +name = "ethers-middleware" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c5f0096a91d210159eceb2ff5e1c4da18388a170e1e3ce948aac9c8fdbbf595" +checksum = "af5d80cc912cf090997b1bc1d01138cc1efed44445e64621cb7f8b29d2bb1bc6" dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", + "async-trait", + "ethers-contract", + "ethers-core", + "ethers-etherscan", + "ethers-providers", + "ethers-signers", + "futures-util", + "instant", + "reqwest", + "serde", + "serde_json", + "thiserror", + "tokio 1.13.0", + "tracing", + "tracing-futures", + "url", ] [[package]] -name = "env_logger" -version = "0.8.3" +name = "ethers-providers" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17392a012ea30ef05a610aa97dfb49496e71c9f676b27879922ea5bdf60d9d3f" +checksum = "254efa7740027e29145f4674324ad3745a569244d2f8932c3bb9847992324b78" dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", + "async-trait", + "auto_impl", + "ethers-core", + "futures-channel", + "futures-core", + "futures-timer", + "futures-util", + "hex", + "parking_lot", + "pin-project 1.0.8", + "reqwest", + "serde", + "serde_json", + "thiserror", + "tracing", + "tracing-futures", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-timer", + "web-sys", + "ws_stream_wasm", ] [[package]] -name = "error" -version = "0.1.9" +name = "ethers-signers" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6e606f14042bb87cc02ef6a14db6c90ab92ed6f62d87e69377bc759fd7987cc" +checksum = "2d1b5ef38b0159c99324abb1db1b131e80944968ae1227032d3a84d2368bd79c" dependencies = [ - "traitobject", - "typeable", + "async-trait", + "coins-bip32", + "coins-bip39", + "elliptic-curve 0.11.1", + "eth-keystore", + "ethers-core", + "futures-executor", + "futures-util", + "hex", + "rand 0.8.4", + "semver 1.0.4", + "sha2 0.9.8", + "thiserror", ] [[package]] -name = "ethereum_gravity" -version = "0.1.0" +name = "ethers-solc" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca81cc5875d2194ba552e7b0c9262f7b6dd8268c69d0db42ddc15deb04dc2348" dependencies = [ - "clarity", - "deep_space 2.4.7", - "gravity_utils", - "log", - "num256", - "sha3", - "web30", + "colored", + "ethers-core", + "glob", + "hex", + "home", + "md-5", + "once_cell", + "regex", + "semver 1.0.4", + "serde", + "serde_json", + "sha2 0.9.8", + "thiserror", + "tracing", + "walkdir", ] [[package]] @@ -1311,6 +1958,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + [[package]] name = "ff" version = "0.10.1" @@ -1321,6 +1974,18 @@ dependencies = [ "subtle", ] +[[package]] +name = "fixed-hash" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" +dependencies = [ + "byteorder", + "rand 0.8.4", + "rustc-hex", + "static_assertions", +] + [[package]] name = "fixedbitset" version = "0.2.0" @@ -1398,11 +2063,17 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +[[package]] +name = "funty" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" + [[package]] name = "futures" -version = "0.3.14" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d5813545e459ad3ca1bff9915e9ad7f1a47dc6a91b627ce321d5863b7dd253" +checksum = "8cd0210d8c325c245ff06fd95a3b13689a1a276ac8cfa8e8720cb840bfb84b9e" dependencies = [ "futures-channel", "futures-core", @@ -1415,9 +2086,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.14" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce79c6a52a299137a6013061e0cf0e688fce5d7f1bc60125f520912fdb29ec25" +checksum = "7fc8cd39e3dbf865f7340dce6a2d401d24fd37c6fe6c4f0ee0de8bfca2252d27" dependencies = [ "futures-core", "futures-sink", @@ -1425,15 +2096,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.14" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "098cd1c6dda6ca01650f1a37a794245eb73181d0d4d4e955e2f3c37db7af1815" +checksum = "629316e42fe7c2a0b9a65b47d159ceaa5453ab14e8f0a3c5eedbb8cd55b4a445" [[package]] name = "futures-executor" -version = "0.3.14" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f6cb7042eda00f0049b1d2080aa4b93442997ee507eb3828e8bd7577f94c9d" +checksum = "7b808bf53348a36cab739d7e04755909b9fcaaa69b7d7e588b37b6ec62704c97" dependencies = [ "futures-core", "futures-task", @@ -1442,17 +2113,16 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.14" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "365a1a1fb30ea1c03a830fdb2158f5236833ac81fa0ad12fe35b29cddc35cb04" +checksum = "e481354db6b5c353246ccf6a728b0c5511d752c08da7260546fc0933869daa11" [[package]] name = "futures-macro" -version = "0.3.14" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668c6733a182cd7deb4f1de7ba3bf2120823835b3bcfbeacf7d2c4a773c1bb8b" +checksum = "a89f17b21645bc4ed773c69af9c9a0effd4a3f1a3876eadd453469f8854e7fdd" dependencies = [ - "proc-macro-hack", "proc-macro2", "quote", "syn", @@ -1460,21 +2130,27 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.14" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5629433c555de3d82861a7a4e3794a4c40040390907cfbfd7143a92a426c23" +checksum = "996c6442437b62d21a32cd9906f9c41e7dc1e19a9579843fad948696769305af" [[package]] name = "futures-task" -version = "0.3.14" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba7aa51095076f3ba6d9a1f702f74bd05ec65f555d70d2033d55ba8d69f581bc" +checksum = "dabf1872aaab32c886832f2276d2f5399887e2bd613698a02359e4ea83f8de12" + +[[package]] +name = "futures-timer" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" [[package]] name = "futures-util" -version = "0.3.14" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c144ad54d60f23927f0a6b6d816e4271278b64f005ad65e4e35291d2de9c025" +checksum = "41d22213122356472061ac0f1ab2cee28d2bac8491410fd68c2af53d1cedb83e" dependencies = [ "futures-channel", "futures-core", @@ -1485,8 +2161,6 @@ dependencies = [ "memchr", "pin-project-lite 0.2.6", "pin-utils", - "proc-macro-hack", - "proc-macro-nested", "slab", ] @@ -1499,6 +2173,15 @@ dependencies = [ "byteorder", ] +[[package]] +name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum", +] + [[package]] name = "generic-array" version = "0.14.4" @@ -1522,13 +2205,15 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" dependencies = [ "cfg-if 1.0.0", + "js-sys", "libc", "wasi 0.10.2+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] @@ -1537,21 +2222,28 @@ version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce" +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + [[package]] name = "gorc" version = "0.2.23" dependencies = [ "abscissa_core", "abscissa_tokio", - "actix-rt 2.2.0", + "actix-rt 2.5.0", "bip32", - "bytes 1.0.1", + "bytes 1.1.0", "clap", "clarity", "cosmos_gravity", "deep_space 2.4.7", "env_logger", "ethereum_gravity", + "ethers", "gravity_proto", "gravity_utils", "k256", @@ -1569,19 +2261,31 @@ dependencies = [ "serde", "signatory", "thiserror", - "tokio 1.5.0", + "tokio 1.13.0", "toml", "tonic", "web30", ] +[[package]] +name = "gravity_abi" +version = "0.1.0" +dependencies = [ + "ethers", + "serde", + "serde_derive", + "serde_json", +] + [[package]] name = "gravity_bridge" version = "0.1.0" dependencies = [ + "abi_build", "cosmos_gravity", "ethereum_gravity", "gorc", + "gravity_abi", "gravity_proto", "gravity_utils", "orchestrator", @@ -1595,7 +2299,7 @@ dependencies = [ name = "gravity_proto" version = "0.1.0" dependencies = [ - "bytes 1.0.1", + "bytes 1.1.0", "cosmos-sdk-proto", "prost", "prost-types", @@ -1610,15 +2314,19 @@ dependencies = [ "clarity", "cosmos-sdk-proto", "deep_space 2.4.7", + "ethers", + "gravity_abi", "gravity_proto", "log", "num-bigint 0.4.0", "num256", - "rand 0.8.3", + "rand 0.8.4", + "rustc-hex", "serde", "serde_derive", + "serde_json", "sha3", - "tokio 1.5.0", + "tokio 1.13.0", "tonic", "url", "web30", @@ -1661,7 +2369,7 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "825343c4eef0b63f541f8903f395dc5beb362a979b5799a84062527ef1e37726" dependencies = [ - "bytes 1.0.1", + "bytes 1.1.0", "fnv", "futures-core", "futures-sink", @@ -1669,7 +2377,7 @@ dependencies = [ "http", "indexmap", "slab", - "tokio 1.5.0", + "tokio 1.13.0", "tokio-util 0.6.6", "tracing", ] @@ -1698,6 +2406,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "hkd32" version = "0.6.0" @@ -1708,7 +2422,7 @@ dependencies = [ "once_cell", "pbkdf2 0.8.0", "rand_core 0.6.2", - "sha2", + "sha2 0.9.8", "zeroize", ] @@ -1719,7 +2433,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" dependencies = [ "crypto-mac 0.10.0", - "digest", + "digest 0.9.0", ] [[package]] @@ -1729,7 +2443,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" dependencies = [ "crypto-mac 0.11.0", - "digest", + "digest 0.9.0", +] + +[[package]] +name = "home" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2456aef2e6b6a9784192ae780c0f15bc57df0e918585282325e8c8ac27737654" +dependencies = [ + "winapi 0.3.9", ] [[package]] @@ -1749,7 +2472,7 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "527e8c9ac747e28542699a951517aa9a6945af506cd1f2e1b53a576c17b6cc11" dependencies = [ - "bytes 1.0.1", + "bytes 1.1.0", "fnv", "itoa", ] @@ -1760,7 +2483,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dfb77c123b4e2f72a2069aeae0b4b4949cc7e966df277813fc16347e7549737" dependencies = [ - "bytes 1.0.1", + "bytes 1.1.0", "http", "pin-project-lite 0.2.6", ] @@ -1789,7 +2512,7 @@ version = "0.14.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b61cf2d1aebcf6e6352c97b81dc2244ca29194be1b276f5d8ad5c6330fffb11" dependencies = [ - "bytes 1.0.1", + "bytes 1.1.0", "futures-channel", "futures-core", "futures-util", @@ -1801,12 +2524,40 @@ dependencies = [ "itoa", "pin-project-lite 0.2.6", "socket2 0.4.0", - "tokio 1.5.0", + "tokio 1.13.0", "tower-service", "tracing", "want", ] +[[package]] +name = "hyper-rustls" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f9f7a97316d44c0af9b0301e65010573a853a9fc97046d7331d7f6bc0fd5a64" +dependencies = [ + "futures-util", + "hyper", + "log", + "rustls", + "tokio 1.13.0", + "tokio-rustls", + "webpki", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes 1.1.0", + "hyper", + "native-tls", + "tokio 1.13.0", + "tokio-native-tls", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -1824,6 +2575,44 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "impl-codec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "161ebdfec3c8e3b52bf61c4f3550a1eea4f9579d10dc1b936f3171ebdcd6c443" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-rlp" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" +dependencies = [ + "rlp", +] + +[[package]] +name = "impl-serde" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b47ca4d2b6931707a55fce5cf66aff80e2178c8b63bbb4ecb5695cbc870ddf6f" +dependencies = [ + "serde", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5dacb10c5b3bb92d46ba347505a9041e676bb20ad220101326bffb0c93031ee" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "indenter" version = "0.3.3" @@ -1842,11 +2631,14 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.9" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if 1.0.0", + "js-sys", + "wasm-bindgen", + "web-sys", ] [[package]] @@ -1867,9 +2659,15 @@ dependencies = [ "socket2 0.3.19", "widestring", "winapi 0.3.9", - "winreg", + "winreg 0.6.2", ] +[[package]] +name = "ipnet" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" + [[package]] name = "itertools" version = "0.9.0" @@ -1894,6 +2692,15 @@ dependencies = [ "libc", ] +[[package]] +name = "js-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "k256" version = "0.9.6" @@ -1902,8 +2709,8 @@ checksum = "903ae2481bcdfdb7b68e0a9baa4b7c9aff600b9ae2e8e5bb5833b8c91ab851ea" dependencies = [ "cfg-if 1.0.0", "ecdsa", - "elliptic-curve", - "sha2", + "elliptic-curve 0.10.6", + "sha2 0.9.8", "sha3", ] @@ -1943,9 +2750,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.93" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41" +checksum = "a60553f9a9e039a333b4e9b20573b9e9b9c0bb3a11e201ccc48ef4283456d673" [[package]] name = "linked-hash-map" @@ -2019,6 +2826,17 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" +[[package]] +name = "md-5" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15" +dependencies = [ + "block-buffer 0.9.0", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + [[package]] name = "memchr" version = "2.4.0" @@ -2111,6 +2929,24 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" +[[package]] +name = "native-tls" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "net2" version = "0.2.37" @@ -2309,9 +3145,15 @@ checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4" [[package]] name = "once_cell" -version = "1.7.2" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" + +[[package]] +name = "opaque-debug" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" [[package]] name = "opaque-debug" @@ -2366,7 +3208,7 @@ dependencies = [ name = "orchestrator" version = "0.4.1" dependencies = [ - "actix-rt 2.2.0", + "actix-rt 2.5.0", "axum", "clarity", "cosmos_gravity", @@ -2374,22 +3216,23 @@ dependencies = [ "docopt", "env_logger", "ethereum_gravity", + "ethers", "futures", + "gravity_abi", "gravity_proto", "gravity_utils", "hyper", "lazy_static", "log", - "num256", "openssl", "openssl-probe", "prometheus", - "rand 0.8.3", + "rand 0.8.4", "relayer", "serde", "serde_derive", "serde_json", - "tokio 1.5.0", + "tokio 1.13.0", "tonic", "web30", ] @@ -2409,6 +3252,32 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2386b4ebe91c2f7f51082d4cefa145d030e33a1842a96b12e4885cc3c01f7a55" +[[package]] +name = "parity-scale-codec" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373b1a4c1338d9cd3d1fa53b3a11bdab5ab6bd80a20f7f7becd76953ae2be909" +dependencies = [ + "arrayvec 0.7.2", + "bitvec 0.20.4", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1557010476e0595c9b568d16dcfb81b93cdeb157612726f5170d31aa707bed27" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "parking_lot" version = "0.11.1" @@ -2434,6 +3303,17 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "password-hash" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77e0b28ace46c5a396546bcf443bf422b57049617433d8854227352a4a9b24e7" +dependencies = [ + "base64ct", + "rand_core 0.6.2", + "subtle", +] + [[package]] name = "password-hash" version = "0.3.2" @@ -2457,12 +3337,12 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3b8c0d71734018084da0c0354193a5edfb81b20d2d57a92c5b154aefc554a4a" dependencies = [ - "base64", + "base64 0.13.0", "crypto-mac 0.10.0", "hmac 0.10.1", "rand 0.7.3", "rand_core 0.5.1", - "sha2", + "sha2 0.9.8", "subtle", ] @@ -2472,7 +3352,11 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" dependencies = [ + "base64ct", "crypto-mac 0.11.0", + "hmac 0.11.0", + "password-hash 0.2.3", + "sha2 0.9.8", ] [[package]] @@ -2483,8 +3367,8 @@ checksum = "f05894bce6a1ba4be299d0c5f29563e08af2bc18bb7d48313113bed71e904739" dependencies = [ "crypto-mac 0.11.0", "hmac 0.11.0", - "password-hash", - "sha2", + "password-hash 0.3.2", + "sha2 0.9.8", ] [[package]] @@ -2512,6 +3396,16 @@ dependencies = [ "indexmap", ] +[[package]] +name = "pharos" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9567389417feee6ce15dd6527a8a1ecac205ef62c2932bcf3d9f6fc5b78b414" +dependencies = [ + "futures", + "rustc_version 0.4.0", +] + [[package]] name = "pin-project" version = "0.4.28" @@ -2523,11 +3417,11 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc174859768806e91ae575187ada95c91a29e96a98dc5d2cd9a1fed039501ba6" +checksum = "576bc800220cc65dac09e99e97b08b358cfab6e17078de8dc5fee223bd2d0c08" dependencies = [ - "pin-project-internal 1.0.6", + "pin-project-internal 1.0.8", ] [[package]] @@ -2543,9 +3437,9 @@ dependencies = [ [[package]] name = "pin-project-internal" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a490329918e856ed1b083f244e3bfe2d8c4f336407e4ea9e1a9f479ff09049e5" +checksum = "6e8fe8163d14ce7f0cdac2e040116f22eac817edabff0be91e8aff7e9accf389" dependencies = [ "proc-macro2", "quote", @@ -2576,7 +3470,7 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee3ef9b64d26bad0536099c816c6734379e45bbd5f14798def6809e5cc350447" dependencies = [ - "der", + "der 0.4.4", "pem-rfc7468", "spki", "zeroize", @@ -2586,13 +3480,36 @@ dependencies = [ name = "pkg-config" version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" + +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + +[[package]] +name = "primitive-types" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05e4722c697a58a99d5d06a08c30821d7c082a4632198de1eaa5a6c22ef42373" +dependencies = [ + "fixed-hash", + "impl-codec", + "impl-rlp", + "impl-serde", + "uint", +] [[package]] -name = "ppv-lite86" -version = "0.2.10" +name = "proc-macro-crate" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" +checksum = "1ebace6889caf889b4d3f76becee12e90353f2b8c7d875534a71e5742f8f6f83" +dependencies = [ + "thiserror", + "toml", +] [[package]] name = "proc-macro-error" @@ -2624,17 +3541,11 @@ version = "0.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" -[[package]] -name = "proc-macro-nested" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" - [[package]] name = "proc-macro2" -version = "1.0.29" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d" +checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" dependencies = [ "unicode-xid", ] @@ -2660,7 +3571,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e6984d2f1a23009bd270b8bb56d0926810a3d483f59c987d77969e9d8e840b2" dependencies = [ - "bytes 1.0.1", + "bytes 1.1.0", "prost-derive", ] @@ -2670,7 +3581,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32d3ebd75ac2679c2af3a92246639f9fcc8a442ee420719cc4fe195b98dd5fa3" dependencies = [ - "bytes 1.0.1", + "bytes 1.1.0", "heck", "itertools", "log", @@ -2701,7 +3612,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b518d7cdd93dab1d1122cf07fa9a60771836c668dde9d9e2a139f957f0d9f1bb" dependencies = [ - "bytes 1.0.1", + "bytes 1.1.0", "prost", ] @@ -2739,6 +3650,18 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "def50a86306165861203e7f84ecffbbdfdea79f0e51039b33de1e952358c47ac" + +[[package]] +name = "radium" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643f8f41a8ebc4c5dc4515c82bb8abd397b527fc20fd681b7c011c2aee5d44fb" + [[package]] name = "rand" version = "0.4.6" @@ -2767,9 +3690,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" dependencies = [ "libc", "rand_chacha 0.3.0", @@ -2827,7 +3750,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" dependencies = [ - "getrandom 0.2.2", + "getrandom 0.2.3", ] [[package]] @@ -2896,7 +3819,7 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" name = "register_delegate_keys" version = "0.4.1" dependencies = [ - "actix-rt 2.2.0", + "actix-rt 2.5.0", "clarity", "contact", "cosmos_gravity", @@ -2904,12 +3827,13 @@ dependencies = [ "docopt", "env_logger", "ethereum_gravity", + "ethers", "gravity_proto", "gravity_utils", "lazy_static", "log", "openssl-probe", - "rand 0.8.3", + "rand 0.8.4", "relayer", "serde", "serde_derive", @@ -2921,22 +3845,23 @@ name = "relayer" version = "0.4.1" dependencies = [ "actix", - "actix-rt 2.2.0", + "actix-rt 2.5.0", "clarity", "cosmos_gravity", "deep_space 2.4.7", "docopt", "env_logger", "ethereum_gravity", + "ethers", + "gravity_abi", "gravity_proto", "gravity_utils", "lazy_static", "log", - "num256", "openssl-probe", "serde", "serde_derive", - "tokio 1.5.0", + "tokio 1.13.0", "tonic", "web30", ] @@ -2950,6 +3875,45 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "reqwest" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d2927ca2f685faf0fc620ac4834690d29e7abb153add10f5812eef20b5e280" +dependencies = [ + "base64 0.13.0", + "bytes 1.1.0", + "encoding_rs", + "futures-core", + "futures-util", + "http", + "http-body", + "hyper", + "hyper-rustls", + "hyper-tls", + "ipnet", + "js-sys", + "lazy_static", + "log", + "mime", + "native-tls", + "percent-encoding", + "pin-project-lite 0.2.6", + "rustls", + "serde", + "serde_json", + "serde_urlencoded", + "tokio 1.13.0", + "tokio-native-tls", + "tokio-rustls", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", + "winreg 0.7.0", +] + [[package]] name = "resolv-conf" version = "0.7.0" @@ -2960,15 +3924,51 @@ dependencies = [ "quick-error", ] +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi 0.3.9", +] + [[package]] name = "ripemd160" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eca4ecc81b7f313189bf73ce724400a07da2a6dac19588b03c8bd76a2dcc251" dependencies = [ - "block-buffer", - "digest", - "opaque-debug", + "block-buffer 0.9.0", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + +[[package]] +name = "rlp" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "999508abb0ae792aabed2460c45b89106d97fe4adac593bdaef433c2605847b5" +dependencies = [ + "bytes 1.1.0", + "rustc-hex", +] + +[[package]] +name = "rlp-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -2987,7 +3987,7 @@ version = "1.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc7f5b8840fb1f83869a3e1dfd06d93db79ea05311ac5b42b8337d3371caa4f1" dependencies = [ - "arrayvec", + "arrayvec 0.5.2", "num-traits", "serde", ] @@ -2998,6 +3998,12 @@ version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232" +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + [[package]] name = "rustc_version" version = "0.2.3" @@ -3007,12 +4013,43 @@ dependencies = [ "semver 0.9.0", ] +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver 1.0.4", +] + +[[package]] +name = "rustls" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" +dependencies = [ + "base64 0.13.0", + "log", + "ring", + "sct", + "webpki", +] + [[package]] name = "ryu" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +[[package]] +name = "salsa20" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecbd2eb639fd7cab5804a0837fe373cc2172d15437e804c054a9fb885cb923b0" +dependencies = [ + "cipher", +] + [[package]] name = "same-file" version = "1.0.6" @@ -3022,12 +4059,46 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +dependencies = [ + "lazy_static", + "winapi 0.3.9", +] + [[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "scrypt" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879588d8f90906e73302547e20fffefdd240eb3e0e744e142321f5d49dea0518" +dependencies = [ + "base64ct", + "hmac 0.11.0", + "password-hash 0.2.3", + "pbkdf2 0.8.0", + "salsa20", + "sha2 0.9.8", +] + +[[package]] +name = "sct" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "secp256k1" version = "0.19.0" @@ -3074,6 +4145,29 @@ dependencies = [ "zeroize", ] +[[package]] +name = "security-framework" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23a2ac85147a3a11d77ecf1bc7166ec0b92febfa4461c37944e180f319ece467" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9dd14d83160b528b7bfd66439110573efcfbe281b17fc2ca9f39f550d619c7e" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" version = "0.9.0" @@ -3098,15 +4192,31 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +[[package]] +name = "send_wrapper" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "930c0acf610d3fdb5e2ab6213019aaa04e227ebe9547b0649ba599b16d788bd7" + [[package]] name = "serde" -version = "1.0.125" +version = "1.0.130" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" +checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" dependencies = [ "serde_derive", ] +[[package]] +name = "serde-aux" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93abf9799c576f004252b2a05168d58527fb7c54de12e94b4d12fe3475ffad24" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "serde-rlp" version = "0.1.4" @@ -3130,9 +4240,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.125" +version = "1.0.130" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d" +checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" dependencies = [ "proc-macro2", "quote", @@ -3141,9 +4251,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.64" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" +checksum = "e466864e431129c7e0d3476b92f20458e5879919a0596c6472738d9fa2d342f8" dependencies = [ "itoa", "ryu", @@ -3168,11 +4278,11 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfebf75d25bd900fd1e7d11501efab59bc846dbc76196839663e6637bba9f25f" dependencies = [ - "block-buffer", + "block-buffer 0.9.0", "cfg-if 1.0.0", "cpuid-bool", - "digest", - "opaque-debug", + "digest 0.9.0", + "opaque-debug 0.3.0", ] [[package]] @@ -3183,15 +4293,37 @@ checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" [[package]] name = "sha2" -version = "0.9.3" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" +dependencies = [ + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", +] + +[[package]] +name = "sha2" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa827a14b29ab7f44778d14a88d3cb76e949c45083f7dbfa507d0cb699dc12de" +checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" dependencies = [ - "block-buffer", + "block-buffer 0.9.0", "cfg-if 1.0.0", - "cpuid-bool", - "digest", - "opaque-debug", + "cpufeatures", + "digest 0.9.0", + "opaque-debug 0.3.0", + "sha2-asm", +] + +[[package]] +name = "sha2-asm" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf27176fb5d15398e3a479c652c20459d9dac830dedd1fa55b42a77dbcdbfcea" +dependencies = [ + "cc", ] [[package]] @@ -3200,10 +4332,10 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" dependencies = [ - "block-buffer", - "digest", + "block-buffer 0.9.0", + "digest 0.9.0", "keccak", - "opaque-debug", + "opaque-debug 0.3.0", ] [[package]] @@ -3242,7 +4374,7 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2807892cfa58e081aa1f1111391c7a0649d4fa127a4ffbe34bcbfb35a1171a4" dependencies = [ - "digest", + "digest 0.9.0", "rand_core 0.6.2", ] @@ -3279,13 +4411,19 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "spki" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c01a0c15da1b0b0e1494112e7af814a678fec9bd157881b49beac661e9b6f32" dependencies = [ - "der", + "der 0.4.4", ] [[package]] @@ -3297,6 +4435,12 @@ dependencies = [ "version_check", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "stdweb" version = "0.4.20" @@ -3304,7 +4448,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" dependencies = [ "discard", - "rustc_version", + "rustc_version 0.2.3", "stdweb-derive", "stdweb-internal-macros", "stdweb-internal-runtime", @@ -3369,9 +4513,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.80" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194" +checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59" dependencies = [ "proc-macro2", "quote", @@ -3390,6 +4534,12 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "tempdir" version = "0.3.7" @@ -3408,7 +4558,7 @@ checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" dependencies = [ "cfg-if 1.0.0", "libc", - "rand 0.8.3", + "rand 0.8.4", "redox_syscall", "remove_dir_all", "winapi 0.3.9", @@ -3421,7 +4571,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfd0371c6b0c7fc4f6b053b8a1bc868a2a47c49a1408c4cd6f595d9f3bd3c238" dependencies = [ "anomaly", - "bytes 1.0.1", + "bytes 1.1.0", "chrono", "num-derive", "num-traits", @@ -3447,7 +4597,7 @@ name = "test_runner" version = "0.1.0" dependencies = [ "actix", - "actix-rt 2.2.0", + "actix-rt 2.5.0", "actix-web", "clarity", "cosmos_gravity", @@ -3455,17 +4605,19 @@ dependencies = [ "docopt", "env_logger", "ethereum_gravity", + "ethers", "futures", + "gravity_abi", "gravity_proto", "gravity_utils", + "hex", "lazy_static", "log", - "num256", "orchestrator", - "rand 0.8.3", + "rand 0.8.4", "serde", "serde_derive", - "tokio 1.5.0", + "tokio 1.13.0", "tonic", "url", "web30", @@ -3482,18 +4634,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.24" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.24" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" dependencies = [ "proc-macro2", "quote", @@ -3566,6 +4718,15 @@ dependencies = [ "syn", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tinyvec" version = "1.2.0" @@ -3603,12 +4764,12 @@ dependencies = [ [[package]] name = "tokio" -version = "1.5.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83f0c8e7c0addab50b663055baf787d0af7f413a46e6e7fb9559a4e4db7137a5" +checksum = "588b2d10a336da58d877567cd8fb8a14b463e2104910f8132cd054b4b96e29ee" dependencies = [ "autocfg", - "bytes 1.0.1", + "bytes 1.1.0", "libc", "memchr", "mio 0.7.11", @@ -3632,6 +4793,16 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +dependencies = [ + "native-tls", + "tokio 1.13.0", +] + [[package]] name = "tokio-openssl" version = "0.4.0" @@ -3650,8 +4821,19 @@ checksum = "ac1bec5c0a4aa71e3459802c7a12e8912c2091ce2151004f9ce95cc5d1c6124e" dependencies = [ "futures", "openssl", - "pin-project 1.0.6", - "tokio 1.5.0", + "pin-project 1.0.8", + "tokio 1.13.0", +] + +[[package]] +name = "tokio-rustls" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" +dependencies = [ + "rustls", + "tokio 1.13.0", + "webpki", ] [[package]] @@ -3662,7 +4844,7 @@ checksum = "e177a5d8c3bf36de9ebe6d58537d8879e964332f93fb3339e43f618c81361af0" dependencies = [ "futures-core", "pin-project-lite 0.2.6", - "tokio 1.5.0", + "tokio 1.13.0", ] [[package]] @@ -3685,12 +4867,12 @@ version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "940a12c99365c31ea8dd9ba04ec1be183ffe4920102bb7122c2f515437601e8e" dependencies = [ - "bytes 1.0.1", + "bytes 1.1.0", "futures-core", "futures-sink", "log", "pin-project-lite 0.2.6", - "tokio 1.5.0", + "tokio 1.13.0", ] [[package]] @@ -3710,8 +4892,8 @@ checksum = "556dc31b450f45d18279cfc3d2519280273f460d5387e6b7b24503e65d206f8b" dependencies = [ "async-stream", "async-trait", - "base64", - "bytes 1.0.1", + "base64 0.13.0", + "bytes 1.1.0", "futures-core", "futures-util", "h2 0.3.3", @@ -3719,10 +4901,10 @@ dependencies = [ "http-body", "hyper", "percent-encoding", - "pin-project 1.0.6", + "pin-project 1.0.8", "prost", "prost-derive", - "tokio 1.5.0", + "tokio 1.13.0", "tokio-stream", "tokio-util 0.6.6", "tower", @@ -3752,10 +4934,10 @@ dependencies = [ "futures-core", "futures-util", "indexmap", - "pin-project 1.0.6", - "rand 0.8.3", + "pin-project 1.0.8", + "rand 0.8.4", "slab", - "tokio 1.5.0", + "tokio 1.13.0", "tokio-stream", "tokio-util 0.6.6", "tower-layer", @@ -3769,12 +4951,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b56efe69aa0ad2b5da6b942e57ea9f6fe683b7a314d4ff48662e2c8838de1" dependencies = [ - "bytes 1.0.1", + "bytes 1.1.0", "futures-core", "futures-util", "http", "http-body", - "pin-project 1.0.6", + "pin-project 1.0.8", "tower-layer", "tower-service", ] @@ -3793,9 +4975,9 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" [[package]] name = "tracing" -version = "0.1.25" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01ebdc2bb4498ab1ab5f5b73c5803825e60199229ccba0698170e3be0e7f959f" +checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" dependencies = [ "cfg-if 1.0.0", "log", @@ -3806,9 +4988,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.15" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42e6fa53307c8a17e4ccd4dc81cf5ec38db9209f59b222210375b54ee40d1e2" +checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e" dependencies = [ "proc-macro2", "quote", @@ -3817,9 +4999,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.17" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f" +checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4" dependencies = [ "lazy_static", ] @@ -3830,7 +5012,7 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" dependencies = [ - "pin-project 1.0.6", + "pin-project 1.0.8", "tracing", ] @@ -3926,6 +5108,18 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" +[[package]] +name = "uint" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6470ab50f482bde894a037a57064480a246dbfdd5960bd65a44824693f08da5f" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + [[package]] name = "unicase" version = "2.6.0" @@ -3971,11 +5165,17 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "url" -version = "2.2.1" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ccd964113622c8e9322cfac19eb1004a07e636c545f325da085d5cdde6f1f8b" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" dependencies = [ "form_urlencoded", "idna", @@ -3983,6 +5183,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "uuid" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +dependencies = [ + "getrandom 0.2.3", + "serde", +] + [[package]] name = "vcpkg" version = "0.2.11" @@ -4039,9 +5249,9 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] name = "wasm-bindgen" -version = "0.2.73" +version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83240549659d187488f91f33c0f8547cbfef0b2088bc470c116d1d260ef623d9" +checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -4049,9 +5259,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.73" +version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae70622411ca953215ca6d06d3ebeb1e915f0f6613e3b495122878d7ebec7dae" +checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" dependencies = [ "bumpalo", "lazy_static", @@ -4062,11 +5272,23 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" -version = "0.2.73" +version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e734d91443f177bfdb41969de821e15c516931c3c3db3d318fa1b68975d0f6f" +checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4074,9 +5296,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.73" +version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53739ff08c8a68b0fdbcd54c372b8ab800b1449ab3c9d706503bc7dd1621b2c" +checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" dependencies = [ "proc-macro2", "quote", @@ -4087,9 +5309,34 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.73" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" + +[[package]] +name = "wasm-timer" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" +dependencies = [ + "futures", + "js-sys", + "parking_lot", + "pin-utils", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9a543ae66aa233d14bb765ed9af4a33e81b8b58d1584cf1b47ff8cd0b9e4489" +checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] [[package]] name = "web30" @@ -4107,7 +5354,26 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "tokio 1.5.0", + "tokio 1.13.0", +] + +[[package]] +name = "webpki" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" +dependencies = [ + "webpki", ] [[package]] @@ -4178,6 +5444,15 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "winreg" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "ws2_32-sys" version = "0.2.1" @@ -4188,6 +5463,30 @@ dependencies = [ "winapi-build", ] +[[package]] +name = "ws_stream_wasm" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47ca1ab42f5afed7fc332b22b6e932ca5414b209465412c8cdf0ad23bc0de645" +dependencies = [ + "async_io_stream", + "futures", + "js-sys", + "pharos", + "rustc_version 0.4.0", + "send_wrapper", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wyz" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" + [[package]] name = "zeroize" version = "1.4.2" diff --git a/orchestrator/Cargo.toml b/orchestrator/Cargo.toml index e1918c182..20a32ff5f 100644 --- a/orchestrator/Cargo.toml +++ b/orchestrator/Cargo.toml @@ -24,6 +24,8 @@ members = [ "relayer", "register_delegate_keys", "gorc", + "abi_build", + "gravity_abi", ] [dependencies] @@ -36,4 +38,6 @@ test_runner = { path = "./test_runner" } gravity_proto = { path = "./gravity_proto" } register_delegate_keys = { path = "./register_delegate_keys" } gorc = { path = "./gorc" } -relayer = { path = "./relayer" } \ No newline at end of file +relayer = { path = "./relayer" } +abi_build = { path = "./abi_build" } +gravity_abi = { path = "./gravity_abi" } diff --git a/orchestrator/abi_build/Cargo.toml b/orchestrator/abi_build/Cargo.toml new file mode 100644 index 000000000..2cbe5b304 --- /dev/null +++ b/orchestrator/abi_build/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "abi_build" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +ethers = { version = "0.6.1", features = ["abigen"] } +serde_derive = "1.0" +serde_json = "1.0.69" +serde = "1.0" diff --git a/orchestrator/abi_build/src/main.rs b/orchestrator/abi_build/src/main.rs new file mode 100644 index 000000000..a0c4edd25 --- /dev/null +++ b/orchestrator/abi_build/src/main.rs @@ -0,0 +1,58 @@ +use ethers::contract::Abigen; +use std::process; + +fn main() { + // Gravity contract + + let abigen = match Abigen::new("Gravity", "../gravity_abi/Gravity.json") { + Ok(abigen) => abigen, + Err(e) => { + println!("Could not open Gravity.json: {}", e); + process::exit(1); + } + }; + + let abi = match abigen + .add_event_derive("serde::Deserialize") + .add_event_derive("serde::Serialize") + .generate() + { + Ok(abi) => abi, + Err(e) => { + println!("Could not generate abi from Gravity.json: {}", e); + process::exit(1); + } + }; + + match abi.write_to_file("../gravity_abi/src/gravity.rs") { + Ok(_) => (), + Err(e) => println!("Error writing gravity.rs: {}", e), + } + + // OpenZeppelin ERC20 contract + + let abigen = match Abigen::new("ERC20", "../gravity_abi/ERC20.json") { + Ok(abigen) => abigen, + Err(e) => { + println!("Could not open ERC20.json: {}", e); + process::exit(1); + } + }; + + let abi = match abigen + .add_event_derive("serde::Deserialize") + .add_event_derive("serde::Serialize") + .generate() + { + Ok(abi) => abi, + Err(e) => { + println!("Could not generate abi from ERC20.json: {}", e); + process::exit(1); + } + }; + + match abi.write_to_file("../gravity_abi/src/erc20.rs") { + Ok(_) => (), + Err(e) => println!("Error writing erc20.rs: {}", e), + } +} diff --git a/orchestrator/cosmos_gravity/Cargo.toml b/orchestrator/cosmos_gravity/Cargo.toml index d54b6c294..94dff5019 100644 --- a/orchestrator/cosmos_gravity/Cargo.toml +++ b/orchestrator/cosmos_gravity/Cargo.toml @@ -12,9 +12,9 @@ ethereum_gravity = {path = "../ethereum_gravity"} gravity_proto = {path = "../gravity_proto/"} deep_space ={git="https://github.com/iqlusioninc/deep_space/", branch="master"} +ethers = "0.6.1" clarity = "0.4.11" serde = "1.0" -num256 = "0.3" log = "0.4" sha3 = "0.9" tokio = "1.4" @@ -28,4 +28,4 @@ bytes = "1" [dev-dependencies] env_logger = "0.8" rand = "0.8" -actix = "0.11" \ No newline at end of file +actix = "0.12" diff --git a/orchestrator/cosmos_gravity/src/build.rs b/orchestrator/cosmos_gravity/src/build.rs index 14310f27d..ad7eb798b 100644 --- a/orchestrator/cosmos_gravity/src/build.rs +++ b/orchestrator/cosmos_gravity/src/build.rs @@ -1,36 +1,38 @@ -use std::collections::BTreeMap; - -use clarity::PrivateKey as EthPrivateKey; use deep_space::private_key::PrivateKey as CosmosPrivateKey; -use deep_space::utils::bytes_to_hex_str; use deep_space::Contact; use deep_space::Msg; -use ethereum_gravity::utils::downcast_uint256; +use ethereum_gravity::types::EthClient; +use ethers::prelude::*; +use ethers::utils::keccak256; use gravity_proto::gravity as proto; use gravity_proto::ToAny; +use gravity_utils::ethereum::{downcast_to_u64, format_eth_address}; use gravity_utils::message_signatures::{ encode_logic_call_confirm, encode_tx_batch_confirm, encode_valset_confirm, }; use gravity_utils::types::*; +use std::collections::BTreeMap; -pub fn signer_set_tx_confirmation_messages( +pub async fn signer_set_tx_confirmation_messages( contact: &Contact, - ethereum_key: EthPrivateKey, + eth_client: EthClient, valsets: Vec, cosmos_key: CosmosPrivateKey, gravity_id: String, ) -> Vec { let cosmos_address = cosmos_key.to_address(&contact.get_prefix()).unwrap(); - let ethereum_address = ethereum_key.to_public_key().unwrap(); + let ethereum_address = eth_client.address(); let mut msgs = Vec::new(); for valset in valsets { - let data = encode_valset_confirm(gravity_id.clone(), valset.clone()); - let signature = ethereum_key.sign_ethereum_msg(&data); + let data = keccak256(encode_valset_confirm(gravity_id.clone(), valset.clone()).as_slice()); + // Signer trait responds with a Result, but we use a LocalWallet and it + // will never throw an error + let signature = eth_client.signer().sign_message(data).await.unwrap(); let confirmation = proto::SignerSetTxConfirmation { - ethereum_signer: ethereum_address.to_string(), + ethereum_signer: format_eth_address(ethereum_address), signer_set_nonce: valset.nonce, - signature: signature.to_bytes().to_vec(), + signature: signature.into(), }; let msg = proto::MsgSubmitEthereumTxConfirmation { signer: cosmos_address.to_string(), @@ -42,25 +44,27 @@ pub fn signer_set_tx_confirmation_messages( msgs } -pub fn batch_tx_confirmation_messages( +pub async fn batch_tx_confirmation_messages( contact: &Contact, - ethereum_key: EthPrivateKey, + eth_client: EthClient, batches: Vec, cosmos_key: CosmosPrivateKey, gravity_id: String, ) -> Vec { let cosmos_address = cosmos_key.to_address(&contact.get_prefix()).unwrap(); - let ethereum_address = ethereum_key.to_public_key().unwrap(); + let ethereum_address = eth_client.address(); let mut msgs = Vec::new(); for batch in batches { - let data = encode_tx_batch_confirm(gravity_id.clone(), batch.clone()); - let signature = ethereum_key.sign_ethereum_msg(&data); + let data = keccak256(encode_tx_batch_confirm(gravity_id.clone(), batch.clone()).as_slice()); + // Signer trait responds with a Result, but we use a LocalWallet and it + // will never throw an error + let signature = eth_client.signer().sign_message(data).await.unwrap(); let confirmation = proto::BatchTxConfirmation { - token_contract: batch.token_contract.to_string(), + token_contract: format_eth_address(batch.token_contract), batch_nonce: batch.nonce, - ethereum_signer: ethereum_address.to_string(), - signature: signature.to_bytes().to_vec(), + ethereum_signer: format_eth_address(ethereum_address), + signature: signature.into(), }; let msg = proto::MsgSubmitEthereumEvent { signer: cosmos_address.to_string(), @@ -72,26 +76,27 @@ pub fn batch_tx_confirmation_messages( msgs } -pub fn contract_call_tx_confirmation_messages( +pub async fn contract_call_tx_confirmation_messages( contact: &Contact, - ethereum_key: EthPrivateKey, + eth_client: EthClient, logic_calls: Vec, cosmos_key: CosmosPrivateKey, gravity_id: String, ) -> Vec { let cosmos_address = cosmos_key.to_address(&contact.get_prefix()).unwrap(); - let ethereum_address = ethereum_key.to_public_key().unwrap(); + let ethereum_address = eth_client.address(); let mut msgs = Vec::new(); for logic_call in logic_calls { - let data = encode_logic_call_confirm(gravity_id.clone(), logic_call.clone()); - let signature = ethereum_key.sign_ethereum_msg(&data); + let data = + keccak256(encode_logic_call_confirm(gravity_id.clone(), logic_call.clone()).as_slice()); + // Signer trait responds with a Result, but we use a LocalWallet and it + // will never throw an error + let signature = eth_client.signer().sign_message(data).await.unwrap(); let confirmation = proto::ContractCallTxConfirmation { - ethereum_signer: ethereum_address.to_string(), - signature: signature.to_bytes().to_vec(), - invalidation_scope: bytes_to_hex_str(&logic_call.invalidation_id) - .as_bytes() - .to_vec(), + ethereum_signer: format_eth_address(ethereum_address), + signature: signature.into(), + invalidation_scope: logic_call.invalidation_id, invalidation_nonce: logic_call.invalidation_nonce, }; let msg = proto::MsgSubmitEthereumTxConfirmation { @@ -126,12 +131,12 @@ pub fn ethereum_event_messages( let mut unordered_msgs = BTreeMap::new(); for deposit in deposits { let event = proto::SendToCosmosEvent { - event_nonce: downcast_uint256(deposit.event_nonce.clone()).unwrap(), - ethereum_height: downcast_uint256(deposit.block_height).unwrap(), - token_contract: deposit.erc20.to_string(), + event_nonce: downcast_to_u64(deposit.event_nonce.clone()).unwrap(), + ethereum_height: downcast_to_u64(deposit.block_height).unwrap(), + token_contract: format_eth_address(deposit.erc20), amount: deposit.amount.to_string(), cosmos_receiver: deposit.destination.to_string(), - ethereum_sender: deposit.sender.to_string(), + ethereum_sender: format_eth_address(deposit.sender), }; let msg = proto::MsgSubmitEthereumEvent { signer: cosmos_address.to_string(), @@ -142,10 +147,10 @@ pub fn ethereum_event_messages( } for batch in batches { let event = proto::BatchExecutedEvent { - event_nonce: downcast_uint256(batch.event_nonce.clone()).unwrap(), - batch_nonce: downcast_uint256(batch.batch_nonce.clone()).unwrap(), - ethereum_height: downcast_uint256(batch.block_height).unwrap(), - token_contract: batch.erc20.to_string(), + event_nonce: downcast_to_u64(batch.event_nonce.clone()).unwrap(), + batch_nonce: downcast_to_u64(batch.batch_nonce.clone()).unwrap(), + ethereum_height: downcast_to_u64(batch.block_height).unwrap(), + token_contract: format_eth_address(batch.erc20), }; let msg = proto::MsgSubmitEthereumEvent { signer: cosmos_address.to_string(), @@ -156,10 +161,10 @@ pub fn ethereum_event_messages( } for deploy in erc20_deploys { let event = proto::Erc20DeployedEvent { - event_nonce: downcast_uint256(deploy.event_nonce.clone()).unwrap(), - ethereum_height: downcast_uint256(deploy.block_height).unwrap(), + event_nonce: downcast_to_u64(deploy.event_nonce.clone()).unwrap(), + ethereum_height: downcast_to_u64(deploy.block_height).unwrap(), cosmos_denom: deploy.cosmos_denom, - token_contract: deploy.erc20_address.to_string(), + token_contract: format_eth_address(deploy.erc20_address), erc20_name: deploy.name, erc20_symbol: deploy.symbol, erc20_decimals: deploy.decimals as u64, @@ -173,10 +178,10 @@ pub fn ethereum_event_messages( } for logic_call in logic_calls { let event = proto::ContractCallExecutedEvent { - event_nonce: downcast_uint256(logic_call.event_nonce.clone()).unwrap(), - ethereum_height: downcast_uint256(logic_call.block_height).unwrap(), + event_nonce: downcast_to_u64(logic_call.event_nonce.clone()).unwrap(), + ethereum_height: downcast_to_u64(logic_call.block_height).unwrap(), invalidation_id: logic_call.invalidation_id, - invalidation_nonce: downcast_uint256(logic_call.invalidation_nonce).unwrap(), + invalidation_nonce: downcast_to_u64(logic_call.invalidation_nonce).unwrap(), }; let msg = proto::MsgSubmitEthereumEvent { signer: cosmos_address.to_string(), @@ -187,9 +192,9 @@ pub fn ethereum_event_messages( } for valset in valsets { let event = proto::SignerSetTxExecutedEvent { - event_nonce: downcast_uint256(valset.event_nonce.clone()).unwrap(), - signer_set_tx_nonce: downcast_uint256(valset.valset_nonce.clone()).unwrap(), - ethereum_height: downcast_uint256(valset.block_height).unwrap(), + event_nonce: downcast_to_u64(valset.event_nonce.clone()).unwrap(), + signer_set_tx_nonce: downcast_to_u64(valset.valset_nonce.clone()).unwrap(), + ethereum_height: downcast_to_u64(valset.block_height).unwrap(), members: valset.members.iter().map(|v| v.into()).collect(), }; let msg = proto::MsgSubmitEthereumEvent { diff --git a/orchestrator/cosmos_gravity/src/query.rs b/orchestrator/cosmos_gravity/src/query.rs index aaac3832c..783ba83b9 100644 --- a/orchestrator/cosmos_gravity/src/query.rs +++ b/orchestrator/cosmos_gravity/src/query.rs @@ -1,8 +1,9 @@ -use clarity::Address as EthAddress; use deep_space::address::Address; +use ethers::types::Address as EthAddress; use gravity_proto::gravity::query_client::QueryClient as GravityQueryClient; use gravity_proto::gravity::*; use gravity_utils::error::GravityError; +use gravity_utils::ethereum::format_eth_address; use gravity_utils::types::*; use tonic::transport::Channel; @@ -109,7 +110,7 @@ pub async fn get_transaction_batch_signatures( let request = client .batch_tx_confirmations(BatchTxConfirmationsRequest { batch_nonce: nonce, - token_contract: contract_address.to_string(), + token_contract: format_eth_address(contract_address), }) .await?; let batch_confirms = request.into_inner().signatures; diff --git a/orchestrator/cosmos_gravity/src/send.rs b/orchestrator/cosmos_gravity/src/send.rs index d8a297ad2..09a830dc1 100644 --- a/orchestrator/cosmos_gravity/src/send.rs +++ b/orchestrator/cosmos_gravity/src/send.rs @@ -1,6 +1,4 @@ use bytes::BytesMut; -use clarity::Address as EthAddress; -use clarity::PrivateKey as EthPrivateKey; use deep_space::address::Address; use deep_space::coin::Coin; use deep_space::error::CosmosGrpcError; @@ -8,15 +6,20 @@ use deep_space::private_key::PrivateKey as CosmosPrivateKey; use deep_space::Contact; use deep_space::Fee; use deep_space::Msg; +use ethers::prelude::*; +use ethers::types::Address as EthAddress; +use ethers::utils::keccak256; use gravity_proto::cosmos_sdk_proto::cosmos::base::abci::v1beta1::TxResponse; use gravity_proto::cosmos_sdk_proto::cosmos::tx::v1beta1::BroadcastMode; use gravity_proto::gravity as proto; +use gravity_utils::error::GravityError; +use gravity_utils::ethereum::format_eth_address; use prost::Message; use std::cmp; use std::collections::HashSet; -use std::time::Duration; +use std::{result::Result, time::Duration}; -pub const MEMO: &str = "Sent using Althea Orchestrator"; +pub const MEMO: &str = "Sent using Gravity Bridge Orchestrator"; pub const TIMEOUT: Duration = Duration::from_secs(60); /// Send a transaction updating the eth address for the sending @@ -26,10 +29,10 @@ pub async fn update_gravity_delegate_addresses( delegate_eth_address: EthAddress, delegate_cosmos_address: Address, cosmos_key: CosmosPrivateKey, - etheruem_key: EthPrivateKey, + ethereum_wallet: LocalWallet, gas_price: (f64, String), gas_adjustment: f64, -) -> Result { +) -> Result { let our_valoper_address = cosmos_key .to_address(&contact.get_prefix()) .unwrap() @@ -53,16 +56,17 @@ pub async fn update_gravity_delegate_addresses( let mut data = BytesMut::with_capacity(eth_sign_msg.encoded_len()); Message::encode(ð_sign_msg, &mut data).expect("encoding failed"); - let eth_signature = etheruem_key.sign_ethereum_msg(&data).to_bytes().to_vec(); + let data_hash = keccak256(data); + let eth_signature = ethereum_wallet.sign_message(data_hash).await?; let msg = proto::MsgDelegateKeys { validator_address: our_valoper_address.to_string(), orchestrator_address: delegate_cosmos_address.to_string(), - ethereum_address: delegate_eth_address.to_string(), - eth_signature, + ethereum_address: format_eth_address(delegate_eth_address), + eth_signature: eth_signature.to_vec(), }; let msg = Msg::new("/gravity.v1.MsgDelegateKeys", msg); - __send_messages(contact, cosmos_key, gas_price, vec![msg], gas_adjustment).await + send_messages(contact, cosmos_key, gas_price, vec![msg], gas_adjustment).await } /// Sends tokens from Cosmos to Ethereum. These tokens will not be sent immediately instead @@ -75,11 +79,13 @@ pub async fn send_to_eth( gas_price: (f64, String), contact: &Contact, gas_adjustment: f64, -) -> Result { +) -> Result { if amount.denom != bridge_fee.denom { - return Err(CosmosGrpcError::BadInput(format!( - "The amount ({}) and bridge_fee ({}) denominations do not match.", - amount.denom, bridge_fee.denom, + return Err(GravityError::CosmosGrpcError(CosmosGrpcError::BadInput( + format!( + "The amount ({}) and bridge_fee ({}) denominations do not match.", + amount.denom, bridge_fee.denom, + ), ))); } @@ -87,12 +93,12 @@ pub async fn send_to_eth( let msg = proto::MsgSendToEthereum { sender: cosmos_address.to_string(), - ethereum_recipient: destination.to_string(), + ethereum_recipient: format_eth_address(destination), amount: Some(amount.into()), bridge_fee: Some(bridge_fee.clone().into()), }; let msg = Msg::new("/gravity.v1.MsgSendToEthereum", msg); - __send_messages(contact, cosmos_key, gas_price, vec![msg], gas_adjustment).await + send_messages(contact, cosmos_key, gas_price, vec![msg], gas_adjustment).await } pub async fn send_request_batch_tx( @@ -101,63 +107,14 @@ pub async fn send_request_batch_tx( gas_price: (f64, String), contact: &Contact, gas_adjustment: f64, -) -> Result { +) -> Result { let cosmos_address = cosmos_key.to_address(&contact.get_prefix()).unwrap(); let msg_request_batch = proto::MsgRequestBatchTx { signer: cosmos_address.to_string(), denom, }; let msg = Msg::new("/gravity.v1.MsgRequestBatchTx", msg_request_batch); - __send_messages(contact, cosmos_key, gas_price, vec![msg], gas_adjustment).await -} - -// TODO(Levi) teach this branch to accept gas_prices -async fn __send_messages( - contact: &Contact, - cosmos_key: CosmosPrivateKey, - gas_price: (f64, String), - messages: Vec, - gas_adjustment: f64, -) -> Result { - let cosmos_address = cosmos_key.to_address(&contact.get_prefix()).unwrap(); - - let fee_amount = Coin { - denom: gas_price.1.clone(), - amount: 0u32.into(), - }; - - let fee = Fee { - amount: vec![fee_amount], - gas_limit: 0, - granter: None, - payer: None, - }; - - let mut args = contact.get_message_args(cosmos_address, fee).await?; - - let tx_parts = cosmos_key.build_tx(&messages, args.clone(), MEMO)?; - let gas = contact.simulate_tx(tx_parts).await?; - - // multiply the estimated gas by the configured gas adjustment - let gas_limit: f64 = (gas.gas_used as f64) * gas_adjustment; - args.fee.gas_limit = cmp::max(gas_limit as u64, 500000 * messages.len() as u64); - - // compute the fee as fee=ceil(gas_limit * gas_price) - - let fee_amount: f64 = args.fee.gas_limit as f64 * gas_price.0; - let fee_amount: u64 = fee_amount.abs().ceil() as u64; - let fee_amount = Coin { - denom: gas_price.1, - amount: fee_amount.into(), - }; - args.fee.amount = vec![fee_amount]; - - let msg_bytes = cosmos_key.sign_std_msg(&messages, args, MEMO)?; - let response = contact - .send_transaction(msg_bytes, BroadcastMode::Sync) - .await?; - - contact.wait_for_tx(response, TIMEOUT).await + send_messages(contact, cosmos_key, gas_price, vec![msg], gas_adjustment).await } pub async fn send_messages( @@ -166,7 +123,7 @@ pub async fn send_messages( gas_price: (f64, String), messages: Vec, gas_adjustment: f64, -) -> Result { +) -> Result { let cosmos_address = cosmos_key.to_address(&contact.get_prefix()).unwrap(); let fee_amount = Coin { @@ -204,7 +161,7 @@ pub async fn send_messages( .send_transaction(msg_bytes, BroadcastMode::Sync) .await?; - contact.wait_for_tx(response, TIMEOUT).await + Ok(contact.wait_for_tx(response, TIMEOUT).await?) } pub async fn send_main_loop( diff --git a/orchestrator/ethereum_gravity/Cargo.toml b/orchestrator/ethereum_gravity/Cargo.toml index e9fb8c03c..bf26eec0f 100644 --- a/orchestrator/ethereum_gravity/Cargo.toml +++ b/orchestrator/ethereum_gravity/Cargo.toml @@ -7,11 +7,13 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -gravity_utils = {path = "../gravity_utils"} +gravity_abi = { path = "../gravity_abi" } +gravity_utils = { path = "../gravity_utils" } -deep_space ={git="https://github.com/iqlusioninc/deep_space/", branch="master"} +deep_space = { git="https://github.com/iqlusioninc/deep_space/", branch="master" } +ethers = "0.6.1" clarity = "0.4.11" web30 = "0.15.4" -num256 = "0.3" log = "0.4" sha3 = "0.9" +tokio = "1.13.0" diff --git a/orchestrator/ethereum_gravity/src/deploy_erc20.rs b/orchestrator/ethereum_gravity/src/deploy_erc20.rs index a32b5943a..96fa032ac 100644 --- a/orchestrator/ethereum_gravity/src/deploy_erc20.rs +++ b/orchestrator/ethereum_gravity/src/deploy_erc20.rs @@ -2,14 +2,11 @@ //! the event for this deployment is then ferried over to Cosmos where the validators will accept the ERC20 contract address //! as the representation of this asset on Ethereum -use clarity::{ - abi::{encode_call, Token}, - Uint256, -}; -use clarity::{Address, PrivateKey}; +use crate::{types::EthClient, utils::get_gas_price}; +use ethers::prelude::*; +use gravity_abi::gravity::*; use gravity_utils::error::GravityError; -use std::time::Duration; -use web30::{client::Web3, types::SendTxOption}; +use std::{result::Result, time::Duration}; /// Calls the Gravity ethereum contract to deploy the ERC20 representation of the given Cosmos asset /// denom. If an existing contract is already deployed representing this asset this call will cost @@ -21,35 +18,38 @@ pub async fn deploy_erc20( erc20_symbol: String, decimals: u8, gravity_contract: Address, - web3: &Web3, wait_timeout: Option, - sender_secret: PrivateKey, - options: Vec, -) -> Result { - let sender_address = sender_secret.to_public_key().unwrap(); - let tx_hash = web3 - .send_transaction( - gravity_contract, - encode_call( - "deployERC20(string,string,string,uint8)", - &[ - Token::String(cosmos_denom), - Token::String(erc20_name), - Token::String(erc20_symbol), - decimals.into(), - ], - )?, - 0u32.into(), - sender_address, - sender_secret, - options, - ) - .await?; + eth_client: EthClient, +) -> Result { + let contract_call = Gravity::new(gravity_contract, eth_client.clone()).deploy_erc20( + cosmos_denom, + erc20_name, + erc20_symbol.clone(), + decimals, + ); + let gas_price = get_gas_price(eth_client.clone()).await?; + let contract_call = contract_call.gas_price(gas_price); + + let pending_tx = contract_call.send().await?; + let tx_hash = *pending_tx; + info!("Deploying ERC-20 with tx hash {}", tx_hash); + // TODO(bolten): ethers interval default is 7s, this mirrors what web30 was doing, should we adjust? + // additionally we are mirroring only waiting for 1 confirmation by leaving that as default + let pending_tx = pending_tx.interval(Duration::from_secs(1)); + let potential_error = GravityError::GravityContractError(format!( + "Did not receive transaction receipt when deploying ERC-20 {}: {}", + erc20_symbol, tx_hash + )); if let Some(timeout) = wait_timeout { - web3.wait_for_transaction(tx_hash.clone(), timeout, None) - .await?; + match tokio::time::timeout(timeout, pending_tx).await?? { + Some(receipt) => Ok(receipt.transaction_hash), + None => Err(potential_error), + } + } else { + match pending_tx.await? { + Some(receipt) => Ok(receipt.transaction_hash), + None => Err(potential_error), + } } - - Ok(tx_hash) } diff --git a/orchestrator/ethereum_gravity/src/erc20_utils.rs b/orchestrator/ethereum_gravity/src/erc20_utils.rs new file mode 100644 index 000000000..1b9485a81 --- /dev/null +++ b/orchestrator/ethereum_gravity/src/erc20_utils.rs @@ -0,0 +1,74 @@ +use crate::types::EthClient; +use ethers::prelude::*; +use gravity_abi::erc20::ERC20; +use gravity_utils::error::GravityError; +use std::{result::Result, time::Duration}; + +/// Checks if any given contract is approved to spend money from any given erc20 contract +/// using any given address. What exactly this does can be hard to grok, essentially when +/// you want contract A to be able to spend your erc20 contract funds you need to call 'approve' +/// on the ERC20 contract with your own address and A's address so that in the future when you call +/// contract A it can manipulate your ERC20 balances. This function checks if that has already been done +/// and that the allowed amount is greater than the provided allowance threshold. +pub async fn check_erc20_approved( + erc20: Address, + target_contract: Address, + address: Address, + allowance_threshold: U256, + eth_client: EthClient, +) -> Result { + let erc20_contract = ERC20::new(erc20, eth_client.clone()); + let contract_call = erc20_contract.allowance(address, target_contract); + let allowance = contract_call.call().await?; + + Ok(allowance > allowance_threshold) +} + +/// Approves a given contract to spend erc20 funds from the given address from the erc20 contract provided. +/// What exactly this does can be hard to grok, essentially when you want contract A to be able to spend +/// your erc20 contract funds you need to call 'approve' on the ERC20 contract with your own address and A's +/// address so that in the future when you call contract A it can manipulate your ERC20 balances. +/// This function performs that action and waits for it to complete for up to Timeout duration +pub async fn approve_erc20_transfers( + erc20: Address, + target_contract: Address, + timeout_option: Option, + eth_client: EthClient, +) -> Result { + let erc20_contract = ERC20::new(erc20, eth_client.clone()); + let contract_call = erc20_contract.approve(target_contract, U256::MAX); + + let pending_tx = contract_call.send().await?; + let tx_hash = *pending_tx; + info!("Approving ERC-20 {} with txid {}", erc20, tx_hash); + // TODO(bolten): ethers interval default is 7s, this mirrors what web30 was doing, should we adjust? + // additionally we are mirroring only waiting for 1 confirmation by leaving that as default + let pending_tx = pending_tx.interval(Duration::from_secs(1)); + let potential_error = GravityError::GravityContractError(format!( + "Did not receive transaction receipt when approving ERC-20 {}: {}", + erc20, tx_hash + )); + + if let Some(timeout) = timeout_option { + match tokio::time::timeout(timeout, pending_tx).await?? { + Some(receipt) => Ok(receipt.transaction_hash), + None => Err(potential_error), + } + } else { + match pending_tx.await? { + Some(receipt) => Ok(receipt.transaction_hash), + None => Err(potential_error), + } + } +} + +pub async fn get_erc20_balance( + erc20: Address, + address: Address, + eth_client: EthClient, +) -> Result { + let erc20_contract = ERC20::new(erc20, eth_client.clone()); + let contract_call = erc20_contract.balance_of(address); + + Ok(contract_call.call().await?) +} diff --git a/orchestrator/ethereum_gravity/src/lib.rs b/orchestrator/ethereum_gravity/src/lib.rs index 44ded6091..da2d7c075 100644 --- a/orchestrator/ethereum_gravity/src/lib.rs +++ b/orchestrator/ethereum_gravity/src/lib.rs @@ -1,17 +1,23 @@ //! This crate contains various components and utilities for interacting with the Gravity Ethereum contract. -use clarity::Uint256; +use ethers::types::U256; #[macro_use] extern crate log; pub mod deploy_erc20; +pub mod erc20_utils; pub mod logic_call; pub mod send_to_cosmos; pub mod submit_batch; +pub mod types; pub mod utils; pub mod valset_update; -pub fn one_eth() -> Uint256 { +pub fn one_eth() -> U256 { 1000000000000000000u128.into() } + +pub fn one_eth_f32() -> f32 { + 1000000000000000000u128 as f32 +} diff --git a/orchestrator/ethereum_gravity/src/logic_call.rs b/orchestrator/ethereum_gravity/src/logic_call.rs index 3b396402d..79b46258f 100644 --- a/orchestrator/ethereum_gravity/src/logic_call.rs +++ b/orchestrator/ethereum_gravity/src/logic_call.rs @@ -1,11 +1,19 @@ -use crate::utils::{get_logic_call_nonce, GasCost}; -use clarity::{abi::Token, utils::bytes_to_hex_str, PrivateKey as EthPrivateKey}; -use clarity::{Address as EthAddress, Uint256}; +use crate::{ + types::{EthClient, EthSignerMiddleware}, + utils::{ + get_logic_call_nonce, + get_gas_price, + GasCost, + }, +}; +use ethers::contract::builders::ContractCall; +use ethers::prelude::*; +use ethers::types::Address as EthAddress; +use gravity_abi::gravity::*; +use gravity_utils::ethereum::{bytes_to_hex_str, vec_u8_to_fixed_32}; use gravity_utils::types::*; use gravity_utils::{error::GravityError, message_signatures::encode_logic_call_confirm_hashed}; -use std::{cmp::min, time::Duration}; -use web30::types::SendTxOption; -use web30::{client::Web3, types::TransactionRequest}; +use std::{result::Result, time::Duration}; /// this function generates an appropriate Ethereum transaction /// to submit the provided logic call @@ -14,15 +22,13 @@ pub async fn send_eth_logic_call( current_valset: Valset, call: LogicCall, confirms: &[LogicCallConfirmResponse], - web3: &Web3, timeout: Duration, gravity_contract_address: EthAddress, gravity_id: String, - our_eth_key: EthPrivateKey, - options: Vec, + gas_cost: GasCost, + eth_client: EthClient, ) -> Result<(), GravityError> { let new_call_nonce = call.invalidation_nonce; - let eth_address = our_eth_key.to_public_key().unwrap(); info!( "Ordering signatures and submitting LogicCall {}:{} to Ethereum", bytes_to_hex_str(&call.invalidation_id), @@ -33,11 +39,11 @@ pub async fn send_eth_logic_call( let before_nonce = get_logic_call_nonce( gravity_contract_address, call.invalidation_id.clone(), - eth_address, - web3, + eth_client.clone(), ) .await?; - let current_block_height = web3.eth_block_number().await?; + + let current_block_height = eth_client.get_block_number().await?; if before_nonce >= new_call_nonce { info!( "Someone else updated the LogicCall to {}, exiting early", @@ -52,29 +58,41 @@ pub async fn send_eth_logic_call( return Ok(()); } - let payload = encode_logic_call_payload(current_valset, &call, confirms, gravity_id)?; - - let tx = web3 - .send_transaction( - gravity_contract_address, - payload, - 0u32.into(), - eth_address, - our_eth_key, - options, - ) - .await?; - info!("Sent batch update with txid {:#066x}", tx); - - web3.wait_for_transaction(tx.clone(), timeout, None).await?; + let contract_call = build_send_logic_call_contract_call( + current_valset, + &call, + confirms, + gravity_contract_address, + gravity_id, + eth_client.clone(), + )?; + + let contract_call = contract_call + .gas(gas_cost.gas) + .gas_price(gas_cost.gas_price); + + let pending_tx = contract_call.send().await?; + let tx_hash = *pending_tx; + info!("Sent logic call with txid {}", tx_hash); + // TODO(bolten): ethers interval default is 7s, this mirrors what web30 was doing, should we adjust? + // additionally we are mirroring only waiting for 1 confirmation by leaving that as default + let pending_tx = pending_tx.interval(Duration::from_secs(1)); + + match tokio::time::timeout(timeout, pending_tx).await?? { + Some(_) => (), + None => error!( + "Did not receive transaction receipt when submitting batch: {}", + tx_hash + ), + } let last_nonce = get_logic_call_nonce( gravity_contract_address, call.invalidation_id, - eth_address, - web3, + eth_client.clone(), ) .await?; + if last_nonce != new_call_nonce { error!( "Current nonce is {} expected to update to nonce {}", @@ -94,208 +112,80 @@ pub async fn estimate_logic_call_cost( current_valset: Valset, call: LogicCall, confirms: &[LogicCallConfirmResponse], - web3: &Web3, gravity_contract_address: EthAddress, gravity_id: String, - our_eth_key: EthPrivateKey, + eth_client: EthClient, ) -> Result { - let our_eth_address = our_eth_key.to_public_key().unwrap(); - let our_balance = web3.eth_get_balance(our_eth_address).await?; - let our_nonce = web3.eth_get_transaction_count(our_eth_address).await?; - let gas_limit = min((u64::MAX - 1).into(), our_balance.clone()); - let gas_price = web3.eth_gas_price().await?; - let zero: Uint256 = 0u8.into(); - let val = web3 - .eth_estimate_gas(TransactionRequest { - from: Some(our_eth_address), - to: gravity_contract_address, - nonce: Some(our_nonce.clone().into()), - gas_price: Some(gas_price.clone().into()), - gas: Some(gas_limit.into()), - value: Some(zero.into()), - data: Some( - encode_logic_call_payload(current_valset, &call, confirms, gravity_id)?.into(), - ), - }) - .await?; + let contract_call = build_send_logic_call_contract_call( + current_valset, + &call, + confirms, + gravity_contract_address, + gravity_id, + eth_client.clone(), + )?; Ok(GasCost { - gas: val, - gas_price, + gas: contract_call.estimate_gas().await?, + gas_price: get_gas_price(eth_client.clone()).await?, }) } -/// Encodes the logic call payload for both cost estimation and submission to EThereum -fn encode_logic_call_payload( +pub fn build_send_logic_call_contract_call( current_valset: Valset, call: &LogicCall, confirms: &[LogicCallConfirmResponse], + gravity_contract_address: EthAddress, gravity_id: String, -) -> Result, GravityError> { + eth_client: EthClient, +) -> Result, GravityError> { let (current_addresses, current_powers) = current_valset.filter_empty_addresses(); + let current_powers: Vec = current_powers.iter().map(|power| (*power).into()).collect(); let current_valset_nonce = current_valset.nonce; let hash = encode_logic_call_confirm_hashed(gravity_id, call.clone()); let sig_data = current_valset.order_sigs(&hash, confirms)?; let sig_arrays = to_arrays(sig_data); - let mut transfer_amounts = Vec::new(); - let mut transfer_token_contracts = Vec::new(); - let mut fee_amounts = Vec::new(); - let mut fee_token_contracts = Vec::new(); - for item in call.transfers.iter() { - transfer_amounts.push(Token::Uint(item.amount.clone())); - transfer_token_contracts.push(item.token_contract_address); - } - for item in call.fees.iter() { - fee_amounts.push(Token::Uint(item.amount.clone())); - fee_token_contracts.push(item.token_contract_address); - } - - // Solidity function signature - // function submitBatch( - // // The validators that approve the batch and new valset - // address[] memory _currentValidators, - // uint256[] memory _currentPowers, - // uint256 _currentValsetNonce, - // // These are arrays of the parts of the validators signatures - // uint8[] memory _v, - // bytes32[] memory _r, - // bytes32[] memory _s, - // // The LogicCall arguments, encoded as a struct (see the Ethereum ABI encoding documentation for the handling of structs as arguments) - // uint256[] transferAmounts; - // address[] transferTokenContracts; - // // The fees (transferred to msg.sender) - // uint256[] feeAmounts; - // address[] feeTokenContracts; - // // The arbitrary logic call - // address logicContractAddress; - // bytes payload; - // // Invalidation metadata - // uint256 timeOut; - // bytes32 invalidationId; - // uint256 invalidationNonce; - let struct_tokens = &[ - Token::Dynamic(transfer_amounts), - transfer_token_contracts.into(), - Token::Dynamic(fee_amounts), - fee_token_contracts.into(), - call.logic_contract_address.into(), - Token::UnboundedBytes(call.payload.clone()), - call.timeout.into(), - Token::Bytes(call.invalidation_id.clone()), - call.invalidation_nonce.into(), - ]; - let tokens = &[ - current_addresses.into(), - current_powers.into(), - current_valset_nonce.into(), - sig_arrays.v, - sig_arrays.r, - sig_arrays.s, - Token::Struct(struct_tokens.to_vec()), - ]; - let payload = clarity::abi::encode_call( - "submitLogicCall(address[],uint256[],uint256,uint8[],bytes32[],bytes32[],(uint256[],address[],uint256[],address[],address,bytes,uint256,bytes32,uint256))", - tokens, - ) - .unwrap(); - trace!("Tokens {:?}", tokens); - - Ok(payload) -} - -#[cfg(test)] -mod tests { - use super::*; - use clarity::utils::hex_str_to_bytes; - use clarity::Signature; - - #[test] - /// This test encodes an abiV2 function call, specifically one - /// with a nontrivial struct in the header - fn encode_abiv2_function_header() { - // a golden master example encoding taken from Hardhat with all of it's parameters recreated - let encoded = "0x0c246c8200000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000c783df8a850f42e7f7e57013759c285caa701eb6000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000ffffffff0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000001b916bf9a6a908cbf3adee07b90c257ad68cd7006616e56db9de1b8138b83c6b600000000000000000000000000000000000000000000000000000000000000013d124e8782f054c80d07de84b5423a5f9a1d1cb005337a634066854b56b11da50000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000017c1736ccf692f653c433d7aa2ab45148c016f68000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000455e2bfa248696e76616c69646174696f6e49640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000c85759553aee2d4125afa8a9421aaf5397b96e6b000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000c85759553aee2d4125afa8a9421aaf5397b96e6b000000000000000000000000000000000000000000000000000000000000002074657374696e675061796c6f6164000000000000000000000000000000000000"; - let encoded = hex_str_to_bytes(encoded).unwrap(); - - let token_contract_address = "0xc85759553AEE2D4125aFa8a9421AAf5397b96E6b" - .parse() - .unwrap(); - let logic_contract_address = "0x17c1736CcF692F653c433d7aa2aB45148C016F68" - .parse() - .unwrap(); - let invalidation_scope = - hex_str_to_bytes("0x696e76616c69646174696f6e4964000000000000000000000000000000000000") - .unwrap(); - let invalidation_nonce = 1u8.into(); - let ethereum_signer = "0xc783df8a850f42e7F7e57013759C285caa701eB6" - .parse() - .unwrap(); - let token = vec![Erc20Token { - amount: 1u8.into(), - token_contract_address, - }]; - - let logic_call = LogicCall { - transfers: token.clone(), - fees: token, - logic_contract_address, - payload: hex_str_to_bytes( - "0x74657374696e675061796c6f6164000000000000000000000000000000000000", - ) - .unwrap(), - timeout: 4766922941000, - invalidation_id: invalidation_scope.clone(), - invalidation_nonce, - }; - - // a validator set - let valset = Valset { - nonce: 0, - members: vec![ValsetMember { - eth_address: Some(ethereum_signer), - power: 4294967295, - }], - }; - let confirm = LogicCallConfirmResponse { - invalidation_id: invalidation_scope, - invalidation_nonce, - ethereum_signer, - eth_signature: Signature { - v: 28u8.into(), - r: Uint256::from_bytes_be( - &hex_str_to_bytes( - "0xb916bf9a6a908cbf3adee07b90c257ad68cd7006616e56db9de1b8138b83c6b6", - ) - .unwrap(), - ), - s: Uint256::from_bytes_be( - &hex_str_to_bytes( - "0x3d124e8782f054c80d07de84b5423a5f9a1d1cb005337a634066854b56b11da5", - ) - .unwrap(), - ), + let transfer_amounts = call + .transfers + .iter() + .map(|transfer| transfer.amount) + .collect(); + let transfer_token_contracts = call + .transfers + .iter() + .map(|transfer| transfer.token_contract_address) + .collect(); + let fee_amounts = call.fees.iter().map(|fee| fee.amount).collect(); + let fee_token_contracts = call + .fees + .iter() + .map(|fee| fee.token_contract_address) + .collect(); + let invalidation_id = vec_u8_to_fixed_32(call.invalidation_id.clone())?; + + let contract_call = Gravity::new(gravity_contract_address, eth_client.clone()) + .submit_logic_call( + current_addresses, + current_powers, + current_valset_nonce.into(), + sig_arrays.v, + sig_arrays.r, + sig_arrays.s, + LogicCallArgs { + transfer_amounts, + transfer_token_contracts, + fee_amounts, + fee_token_contracts, + logic_contract_address: call.logic_contract_address, + payload: call.payload.clone().into(), + time_out: call.timeout.into(), + invalidation_id, + invalidation_nonce: call.invalidation_nonce.into(), }, - }; - - assert_eq!( - bytes_to_hex_str(&encoded), - bytes_to_hex_str( - &encode_logic_call_payload(valset, &logic_call, &[confirm], "foo".to_string()) - .unwrap() - ) - ); - } + ) + .from(eth_client.address()) + .value(U256::zero()); - /// prints a byte vec line by line as unint256 words - fn _print_bytes_as_uint256_words(input: &[u8]) { - for i in 0..(input.len() / 32) { - let start = i * 32; - let mut end = start + 32; - if end > input.len() { - end = input.len() - } - println!("{}", bytes_to_hex_str(&input[start..end])) - } - } + Ok(contract_call) } diff --git a/orchestrator/ethereum_gravity/src/send_to_cosmos.rs b/orchestrator/ethereum_gravity/src/send_to_cosmos.rs index 47bf4b18c..ce91e4955 100644 --- a/orchestrator/ethereum_gravity/src/send_to_cosmos.rs +++ b/orchestrator/ethereum_gravity/src/send_to_cosmos.rs @@ -1,14 +1,14 @@ //! Helper functions for sending tokens to Cosmos -use std::time::Duration; - -use clarity::abi::{encode_call, Token}; -use clarity::PrivateKey as EthPrivateKey; -use clarity::{Address, Uint256}; +use crate::{ + erc20_utils::{approve_erc20_transfers, check_erc20_approved}, + types::EthClient, +}; use deep_space::address::Address as CosmosAddress; +use ethers::prelude::*; +use gravity_abi::gravity::*; use gravity_utils::error::GravityError; -use web30::client::Web3; -use web30::types::SendTxOption; +use std::{result::Result, time::Duration}; const SEND_TO_COSMOS_GAS_LIMIT: u128 = 100_000; @@ -16,62 +16,28 @@ const SEND_TO_COSMOS_GAS_LIMIT: u128 = 100_000; pub async fn send_to_cosmos( erc20: Address, gravity_contract: Address, - amount: Uint256, + amount: U256, cosmos_destination: CosmosAddress, - sender_secret: EthPrivateKey, wait_timeout: Option, - web3: &Web3, - options: Vec, -) -> Result { - let sender_address = sender_secret.to_public_key()?; - let mut approve_nonce = None; - - for option in options.iter() { - if let SendTxOption::Nonce(_) = option { - return Err(GravityError::InvalidOptionsError( - "This call sends more than one tx! Can't specify".to_string(), - )); - } - } - - let approved = web3 - .check_erc20_approved(erc20, sender_address, gravity_contract) - .await?; + eth_client: EthClient, +) -> Result { + // TODO(bolten): this value is ported from web30, does it match our expectations? + // Check if the allowance remaining is greater than half of a U256 - it's as good + // a test as any. + let allowance_threshold = U256::MAX.div_mod(2u32.into()).0; + let approved = check_erc20_approved( + erc20, + gravity_contract, + eth_client.address(), + allowance_threshold, + eth_client.clone(), + ) + .await?; if !approved { - let mut options = options.clone(); - let nonce = web3.eth_get_transaction_count(sender_address).await?; - options.push(SendTxOption::Nonce(nonce.clone())); - approve_nonce = Some(nonce); - let txid = web3 - .approve_erc20_transfers(erc20, sender_secret, gravity_contract, None, options) - .await?; - trace!( - "We are not approved for ERC20 transfers, approving txid: {:#066x}", - txid - ); - if let Some(timeout) = wait_timeout { - web3.wait_for_transaction(txid, timeout, None).await?; - trace!("Approval finished!") - } - } - - // if the user sets a gas limit we should honor it, if they don't we - // should add the default - let mut has_gas_limit = false; - let mut options = options; - for option in options.iter() { - if let SendTxOption::GasLimit(_) = option { - has_gas_limit = true; - break; - } - } - if !has_gas_limit { - options.push(SendTxOption::GasLimit(SEND_TO_COSMOS_GAS_LIMIT.into())); - } - // if we have run an approval we should increment our nonce by one so that - // we can be sure our actual tx can go in immediately behind - if let Some(nonce) = approve_nonce { - options.push(SendTxOption::Nonce(nonce + 1u8.into())); + let txid = + approve_erc20_transfers(erc20, gravity_contract, wait_timeout, eth_client.clone()) + .await?; + trace!("ERC-20 approval for {} finished with txid {}", erc20, txid); } // This code deals with some specifics of Ethereum byte encoding, Ethereum is BigEndian @@ -82,30 +48,35 @@ pub async fn send_to_cosmos( while cosmos_dest_address_bytes.len() < 32 { cosmos_dest_address_bytes.insert(0, 0u8); } - let encoded_destination_address = Token::Bytes(cosmos_dest_address_bytes); + // TODO(bolten): have to convert back from what was done above for the contract call, + // there's probably a cleaner way to do this + let mut cosmos_dest_address_bytes_slice: [u8; 32] = Default::default(); + cosmos_dest_address_bytes_slice.copy_from_slice(&cosmos_dest_address_bytes[..]); + + let contract_call = Gravity::new(gravity_contract, eth_client.clone()) + .send_to_cosmos(erc20, cosmos_dest_address_bytes_slice, amount) + .gas(SEND_TO_COSMOS_GAS_LIMIT); - let tx_hash = web3 - .send_transaction( - gravity_contract, - encode_call( - "sendToCosmos(address,bytes32,uint256)", - &[ - erc20.into(), - encoded_destination_address, - amount.clone().into(), - ], - )?, - 0u32.into(), - sender_address, - sender_secret, - options, - ) - .await?; + let pending_tx = contract_call.send().await?; + let tx_hash = *pending_tx; + info!("Sending to Cosmos with txid {}", tx_hash); + // TODO(bolten): ethers interval default is 7s, this mirrors what web30 was doing, should we adjust? + // additionally we are mirroring only waiting for 1 confirmation by leaving that as default + let pending_tx = pending_tx.interval(Duration::from_secs(1)); + let potential_error = GravityError::GravityContractError(format!( + "Did not receive transaction receipt when sending to Cosmos: {}", + tx_hash + )); if let Some(timeout) = wait_timeout { - web3.wait_for_transaction(tx_hash.clone(), timeout, None) - .await?; + match tokio::time::timeout(timeout, pending_tx).await?? { + Some(receipt) => Ok(receipt.transaction_hash), + None => Err(potential_error), + } + } else { + match pending_tx.await? { + Some(receipt) => Ok(receipt.transaction_hash), + None => Err(potential_error), + } } - - Ok(tx_hash) } diff --git a/orchestrator/ethereum_gravity/src/submit_batch.rs b/orchestrator/ethereum_gravity/src/submit_batch.rs index 8b4b3a761..689235c08 100644 --- a/orchestrator/ethereum_gravity/src/submit_batch.rs +++ b/orchestrator/ethereum_gravity/src/submit_batch.rs @@ -1,12 +1,15 @@ -use crate::utils::{get_tx_batch_nonce, GasCost}; -use clarity::PrivateKey as EthPrivateKey; -use clarity::{Address as EthAddress, Uint256}; +use crate::{ + types::{EthClient, EthSignerMiddleware}, + utils::{get_gas_price, get_tx_batch_nonce, GasCost}, +}; +use ethers::contract::builders::ContractCall; +use ethers::prelude::*; +use ethers::types::Address as EthAddress; +use gravity_abi::gravity::*; use gravity_utils::error::GravityError; use gravity_utils::message_signatures::encode_tx_batch_confirm_hashed; use gravity_utils::types::*; -use std::{cmp::min, time::Duration}; -use web30::types::SendTxOption; -use web30::{client::Web3, types::TransactionRequest}; +use std::{result::Result, time::Duration}; /// this function generates an appropriate Ethereum transaction /// to submit the provided transaction batch @@ -15,15 +18,13 @@ pub async fn send_eth_transaction_batch( current_valset: Valset, batch: TransactionBatch, confirms: &[BatchConfirmResponse], - web3: &Web3, timeout: Duration, gravity_contract_address: EthAddress, gravity_id: String, - our_eth_key: EthPrivateKey, - options: Vec, + gas_cost: GasCost, + eth_client: EthClient, ) -> Result<(), GravityError> { let new_batch_nonce = batch.nonce; - let eth_address = our_eth_key.to_public_key().unwrap(); info!( "Ordering signatures and submitting TransactionBatch {}:{} to Ethereum", batch.token_contract, new_batch_nonce @@ -33,11 +34,11 @@ pub async fn send_eth_transaction_batch( let before_nonce = get_tx_batch_nonce( gravity_contract_address, batch.token_contract, - eth_address, - web3, + eth_client.clone(), ) .await?; - let current_block_height = web3.eth_block_number().await?; + + let current_block_height = eth_client.get_block_number().await?; if before_nonce >= new_batch_nonce { info!( "Someone else updated the batch to {}, exiting early", @@ -52,29 +53,41 @@ pub async fn send_eth_transaction_batch( return Ok(()); } - let payload = encode_batch_payload(current_valset, &batch, confirms, gravity_id)?; + let contract_call = build_submit_batch_contract_call( + current_valset, + &batch, + confirms, + gravity_contract_address, + gravity_id, + eth_client.clone(), + )?; - let tx = web3 - .send_transaction( - gravity_contract_address, - payload, - 0u32.into(), - eth_address, - our_eth_key, - options, - ) - .await?; - info!("Sent batch update with txid {:#066x}", tx); + let contract_call = contract_call + .gas(gas_cost.gas) + .gas_price(gas_cost.gas_price); - web3.wait_for_transaction(tx.clone(), timeout, None).await?; + let pending_tx = contract_call.send().await?; + let tx_hash = *pending_tx; + info!("Sent batch update with txid {}", tx_hash); + // TODO(bolten): ethers interval default is 7s, this mirrors what web30 was doing, should we adjust? + // additionally we are mirroring only waiting for 1 confirmation by leaving that as default + let pending_tx = pending_tx.interval(Duration::from_secs(1)); + + match tokio::time::timeout(timeout, pending_tx).await?? { + Some(_) => (), + None => error!( + "Did not receive transaction receipt when submitting batch: {}", + tx_hash + ), + } let last_nonce = get_tx_batch_nonce( gravity_contract_address, batch.token_contract, - eth_address, - web3, + eth_client.clone(), ) .await?; + if last_nonce != new_batch_nonce { error!( "Current nonce is {} expected to update to nonce {}", @@ -83,6 +96,7 @@ pub async fn send_eth_transaction_batch( } else { info!("Successfully updated Batch with new Nonce {:?}", last_nonce); } + Ok(()) } @@ -91,43 +105,35 @@ pub async fn estimate_tx_batch_cost( current_valset: Valset, batch: TransactionBatch, confirms: &[BatchConfirmResponse], - web3: &Web3, gravity_contract_address: EthAddress, gravity_id: String, - our_eth_key: EthPrivateKey, + eth_client: EthClient, ) -> Result { - let our_eth_address = our_eth_key.to_public_key().unwrap(); - let our_balance = web3.eth_get_balance(our_eth_address).await?; - let our_nonce = web3.eth_get_transaction_count(our_eth_address).await?; - let gas_limit = min((u64::MAX - 1).into(), our_balance.clone()); - let gas_price = web3.eth_gas_price().await?; - let zero: Uint256 = 0u8.into(); - let val = web3 - .eth_estimate_gas(TransactionRequest { - from: Some(our_eth_address), - to: gravity_contract_address, - nonce: Some(our_nonce.clone().into()), - gas_price: Some(gas_price.clone().into()), - gas: Some(gas_limit.into()), - value: Some(zero.into()), - data: Some(encode_batch_payload(current_valset, &batch, confirms, gravity_id)?.into()), - }) - .await?; + let contract_call = build_submit_batch_contract_call( + current_valset, + &batch, + confirms, + gravity_contract_address, + gravity_id, + eth_client.clone(), + )?; Ok(GasCost { - gas: val, - gas_price, + gas: contract_call.estimate_gas().await?, + gas_price: get_gas_price(eth_client.clone()).await?, }) } -/// Encodes the batch payload for both estimate_tx_batch_cost and send_eth_transaction_batch -fn encode_batch_payload( +pub fn build_submit_batch_contract_call( current_valset: Valset, batch: &TransactionBatch, confirms: &[BatchConfirmResponse], + gravity_contract_address: EthAddress, gravity_id: String, -) -> Result, GravityError> { + eth_client: EthClient, +) -> Result, GravityError> { let (current_addresses, current_powers) = current_valset.filter_empty_addresses(); + let current_powers: Vec = current_powers.iter().map(|power| (*power).into()).collect(); let current_valset_nonce = current_valset.nonce; let new_batch_nonce = batch.nonce; let hash = encode_tx_batch_confirm_hashed(gravity_id, batch.clone()); @@ -135,40 +141,23 @@ fn encode_batch_payload( let sig_arrays = to_arrays(sig_data); let (amounts, destinations, fees) = batch.get_checkpoint_values(); - // Solidity function signature - // function submitBatch( - // // The validators that approve the batch and new valset - // address[] memory _currentValidators, - // uint256[] memory _currentPowers, - // uint256 _currentValsetNonce, - // // These are arrays of the parts of the validators signatures - // uint8[] memory _v, - // bytes32[] memory _r, - // bytes32[] memory _s, - // // The batch of transactions - // uint256[] memory _amounts, - // address[] memory _destinations, - // uint256[] memory _fees, - // uint256 _batchNonce, - // address _tokenContract, - // uint256 _batchTimeout - let tokens = &[ - current_addresses.into(), - current_powers.into(), - current_valset_nonce.into(), - sig_arrays.v, - sig_arrays.r, - sig_arrays.s, - amounts, - destinations, - fees, - new_batch_nonce.into(), - batch.token_contract.into(), - batch.batch_timeout.into(), - ]; - let payload = clarity::abi::encode_call("submitBatch(address[],uint256[],uint256,uint8[],bytes32[],bytes32[],uint256[],address[],uint256[],uint256,address,uint256)", - tokens).unwrap(); - trace!("Tokens {:?}", tokens); + let contract_call = Gravity::new(gravity_contract_address, eth_client.clone()) + .submit_batch( + current_addresses, + current_powers, + current_valset_nonce.into(), + sig_arrays.v, + sig_arrays.r, + sig_arrays.s, + amounts, + destinations, + fees, + new_batch_nonce.into(), + batch.token_contract, + batch.batch_timeout.into(), + ) + .from(eth_client.address()) + .value(U256::zero()); - Ok(payload) + Ok(contract_call) } diff --git a/orchestrator/ethereum_gravity/src/types.rs b/orchestrator/ethereum_gravity/src/types.rs new file mode 100644 index 000000000..91cd74033 --- /dev/null +++ b/orchestrator/ethereum_gravity/src/types.rs @@ -0,0 +1,5 @@ +use ethers::prelude::*; +use std::sync::Arc; + +pub type EthSignerMiddleware = SignerMiddleware, LocalWallet>; +pub type EthClient = Arc; diff --git a/orchestrator/ethereum_gravity/src/utils.rs b/orchestrator/ethereum_gravity/src/utils.rs index 64feb3a9a..90b2b78dd 100644 --- a/orchestrator/ethereum_gravity/src/utils.rs +++ b/orchestrator/ethereum_gravity/src/utils.rs @@ -1,256 +1,164 @@ -use clarity::abi::{encode_call, Token}; -use clarity::Uint256; -use clarity::{abi::encode_tokens, Address as EthAddress}; +use crate::types::EthClient; +use ethers::middleware::gas_oracle::Etherscan; +use ethers::prelude::gas_oracle::GasOracle; +use ethers::prelude::*; +use ethers::types::Address as EthAddress; +use gravity_abi::gravity::*; use gravity_utils::error::GravityError; -use gravity_utils::types::*; -use sha3::{Digest, Keccak256}; -use std::u128::MAX as U128MAX; -use std::u64::MAX as U64MAX; -use web30::{client::Web3, jsonrpc::error::Web3Error}; - -// pub fn get_correct_sig_for_address( -// address: CosmosAddress, -// confirms: &[ValsetConfirmResponse], -// ) -> (Uint256, Uint256, Uint256) { -// for sig in confirms { -// if sig.eth_signer == address { -// return ( -// sig.eth_signature.v.clone(), -// sig.eth_signature.r.clone(), -// sig.eth_signature.s.clone(), -// ); -// } -// } -// panic!("Could not find that address!"); -// } - -pub fn get_checkpoint_abi_encode( - valset: &Valset, - gravity_id: &str, -) -> Result, GravityError> { - let (eth_addresses, powers) = valset.filter_empty_addresses(); - Ok(encode_tokens(&[ - Token::FixedString(gravity_id.to_string()), - Token::FixedString("checkpoint".to_string()), - valset.nonce.into(), - eth_addresses.into(), - powers.into(), - ])) -} - -pub fn get_checkpoint_hash(valset: &Valset, gravity_id: &str) -> Result, GravityError> { - let locally_computed_abi_encode = get_checkpoint_abi_encode(valset, gravity_id); - let locally_computed_digest = Keccak256::digest(&locally_computed_abi_encode?); - Ok(locally_computed_digest.to_vec()) -} - -pub fn downcast_uint256(input: Uint256) -> Option { - if input >= U64MAX.into() { - None - } else { - let mut val = input.to_bytes_be(); - // pad to 8 bytes - while val.len() < 8 { - val.insert(0, 0); - } - let mut lower_bytes: [u8; 8] = [0; 8]; - // get the 'lowest' 8 bytes from a 256 bit integer - lower_bytes.copy_from_slice(&val[0..val.len()]); - Some(u64::from_be_bytes(lower_bytes)) - } -} - -pub fn downcast_to_u128(input: Uint256) -> Option { - if input >= U128MAX.into() { - None - } else { - let mut val = input.to_bytes_be(); - // pad to 8 bytes - while val.len() < 16 { - val.insert(0, 0); - } - let mut lower_bytes: [u8; 16] = [0; 16]; - // get the 'lowest' 16 bytes from a 256 bit integer - lower_bytes.copy_from_slice(&val[0..val.len()]); - Some(u128::from_be_bytes(lower_bytes)) - } -} - -#[test] -fn test_downcast_nonce() { - let mut i = 0u64; - while i < 100_000 { - assert_eq!(i, downcast_uint256(i.into()).unwrap()); - i += 1 - } - let mut i: u64 = std::u32::MAX.into(); - i -= 100; - let end = i + 100_000; - while i < end { - assert_eq!(i, downcast_uint256(i.into()).unwrap()); - i += 1 - } -} - -#[test] -fn test_downcast_to_u128() { - let mut i = 0u128; - while i < 100_000 { - assert_eq!(i, downcast_to_u128(i.into()).unwrap()); - i += 1 - } - let mut i: u128 = std::u64::MAX.into(); - i -= 100; - let end = i + 100_000; - while i < end { - assert_eq!(i, downcast_to_u128(i.into()).unwrap()); - i += 1 - } -} +use gravity_utils::ethereum::{downcast_to_u64, vec_u8_to_fixed_32}; +use std::result::Result; /// Gets the latest validator set nonce pub async fn get_valset_nonce( - contract_address: EthAddress, - caller_address: EthAddress, - web3: &Web3, -) -> Result { - let payload = encode_call("state_lastValsetNonce()", &[]).unwrap(); - - let val = web3 - .simulate_transaction(contract_address, 0u8.into(), payload, caller_address, None) - .await?; + gravity_contract_address: EthAddress, + eth_client: EthClient, +) -> Result { + let contract_call = Gravity::new(gravity_contract_address, eth_client.clone()) + .state_last_valset_nonce() + .from(eth_client.address()) + .value(U256::zero()); + let gas_estimate = contract_call.estimate_gas().await?; + let contract_call = contract_call + .gas(gas_estimate) + .gas_price(get_gas_price(eth_client.clone()).await?); + + let valset_nonce = contract_call.call().await?; + + // TODO (bolten): do we actually want to halt the bridge as the original comment implies? // the go represents all nonces as u64, there's no // reason they should ever overflow without a user // submitting millions or tens of millions of dollars // worth of transactions. But we properly check and // handle that case here. - let real_num = Uint256::from_bytes_be(&val); - Ok(downcast_uint256(real_num).expect("Valset nonce overflow! Bridge Halt!")) + Ok(downcast_to_u64(valset_nonce).expect("Valset nonce overflow! Bridge Halt!")) } /// Gets the latest transaction batch nonce pub async fn get_tx_batch_nonce( gravity_contract_address: EthAddress, erc20_contract_address: EthAddress, - caller_address: EthAddress, - web3: &Web3, -) -> Result { - let payload = encode_call("lastBatchNonce(address)", &[erc20_contract_address.into()]).unwrap(); - let val = web3 - .simulate_transaction( - gravity_contract_address, - 0u8.into(), - payload, - caller_address, - None, - ) - .await?; + eth_client: EthClient, +) -> Result { + let contract_call = Gravity::new(gravity_contract_address, eth_client.clone()) + .last_batch_nonce(erc20_contract_address) + .from(eth_client.address()) + .value(U256::zero()); + let gas_estimate = contract_call.estimate_gas().await?; + let contract_call = contract_call + .gas(gas_estimate) + .gas_price(get_gas_price(eth_client.clone()).await?); + + let tx_batch_nonce = contract_call.call().await?; + + // TODO (bolten): do we actually want to halt the bridge as the original comment implies? // the go represents all nonces as u64, there's no // reason they should ever overflow without a user // submitting millions or tens of millions of dollars // worth of transactions. But we properly check and // handle that case here. - let real_num = Uint256::from_bytes_be(&val); - Ok(downcast_uint256(real_num).expect("TxBatch nonce overflow! Bridge Halt!")) + Ok(downcast_to_u64(tx_batch_nonce).expect("TxBatch nonce overflow! Bridge Halt!")) } /// Gets the latest transaction batch nonce pub async fn get_logic_call_nonce( gravity_contract_address: EthAddress, invalidation_id: Vec, - caller_address: EthAddress, - web3: &Web3, -) -> Result { - let payload = encode_call( - "lastLogicCallNonce(bytes32)", - &[Token::Bytes(invalidation_id)], - ) - .unwrap(); - let val = web3 - .simulate_transaction( - gravity_contract_address, - 0u8.into(), - payload, - caller_address, - None, - ) - .await?; + eth_client: EthClient, +) -> Result { + let invalidation_id = vec_u8_to_fixed_32(invalidation_id)?; + + let contract_call = Gravity::new(gravity_contract_address, eth_client.clone()) + .last_logic_call_nonce(invalidation_id) + .from(eth_client.address()) + .value(U256::zero()); + let gas_estimate = contract_call.estimate_gas().await?; + let contract_call = contract_call + .gas(gas_estimate) + .gas_price(get_gas_price(eth_client.clone()).await?); + + let logic_call_nonce = contract_call.call().await?; + + // TODO (bolten): do we actually want to halt the bridge as the original comment implies? // the go represents all nonces as u64, there's no // reason they should ever overflow without a user // submitting millions or tens of millions of dollars // worth of transactions. But we properly check and // handle that case here. - let real_num = Uint256::from_bytes_be(&val); - Ok(downcast_uint256(real_num).expect("LogicCall nonce overflow! Bridge Halt!")) + Ok(downcast_to_u64(logic_call_nonce).expect("LogicCall nonce overflow! Bridge Halt!")) } /// Gets the latest transaction batch nonce pub async fn get_event_nonce( gravity_contract_address: EthAddress, - caller_address: EthAddress, - web3: &Web3, -) -> Result { - let payload = encode_call("state_lastEventNonce()", &[]).unwrap(); - let val = web3 - .simulate_transaction( - gravity_contract_address, - 0u8.into(), - payload, - caller_address, - None, - ) - .await?; + eth_client: EthClient, +) -> Result { + let contract_call = Gravity::new(gravity_contract_address, eth_client.clone()) + .state_last_event_nonce() + .from(eth_client.address()) + .value(U256::zero()); + let gas_estimate = contract_call.estimate_gas().await?; + let contract_call = contract_call + .gas(gas_estimate) + .gas_price(get_gas_price(eth_client.clone()).await?); + + let event_nonce = contract_call.call().await?; + + // TODO (bolten): do we actually want to halt the bridge as the original comment implies? // the go represents all nonces as u64, there's no // reason they should ever overflow without a user // submitting millions or tens of millions of dollars // worth of transactions. But we properly check and // handle that case here. - let real_num = Uint256::from_bytes_be(&val); - Ok(downcast_uint256(real_num).expect("EventNonce nonce overflow! Bridge Halt!")) + Ok(downcast_to_u64(event_nonce).expect("EventNonce nonce overflow! Bridge Halt!")) } /// Gets the gravityID pub async fn get_gravity_id( - contract_address: EthAddress, - caller_address: EthAddress, - web3: &Web3, -) -> Result { - let payload = encode_call("state_gravityId()", &[]).unwrap(); - let val = web3 - .simulate_transaction(contract_address, 0u8.into(), payload, caller_address, None) - .await?; - let gravity_id = String::from_utf8(val); - match gravity_id { - Ok(val) => Ok(val), - Err(e) => Err(Web3Error::BadResponse(e.to_string())), + gravity_contract_address: EthAddress, + eth_client: EthClient, +) -> Result { + let contract_call = Gravity::new(gravity_contract_address, eth_client.clone()) + .state_gravity_id() + .from(eth_client.address()) + .value(U256::zero()); + let gas_estimate = contract_call.estimate_gas().await?; + let contract_call = contract_call + .gas(gas_estimate) + .gas_price(get_gas_price(eth_client.clone()).await?); + + let gravity_id = contract_call.call().await?; + let id_as_string = String::from_utf8(gravity_id.to_vec()); + + match id_as_string { + Ok(id) => Ok(id), + Err(err) => Err(GravityError::GravityContractError(format!( + "Received invalid utf8 when getting gravity id {:?}: {}", + &gravity_id, err + ))), } } -/// Gets the ERC20 symbol, should maybe be upstreamed -pub async fn get_erc20_symbol( - contract_address: EthAddress, - caller_address: EthAddress, - web3: &Web3, -) -> Result { - let payload = encode_call("symbol()", &[]).unwrap(); +/// If ETHERSCAN_API_KEY env var is set, we'll call out to Etherscan for a gas estimate. +/// Otherwise, just call eth_gasPrice. +pub async fn get_gas_price(eth_client: EthClient) -> Result { + if let Ok(_) = std::env::var("ETHERSCAN_API_KEY") { + let etherscan_client = Client::new_from_env(Chain::Mainnet)?; + let etherscan_oracle = Etherscan::new(etherscan_client); + return Ok(etherscan_oracle.fetch().await?); + } - let val_symbol = web3 - .simulate_transaction(contract_address, 0u8.into(), payload, caller_address, None) - .await?; - // Pardon the unwrap, but this is temporary code, intended only for the tests, to help them - // deal with a deprecated feature (the symbol), which will be removed soon - Ok(String::from_utf8(val_symbol).unwrap()) + Ok(eth_client.get_gas_price().await?) } /// Just a helper struct to represent the cost of actions on Ethereum #[derive(Debug, Default, Clone)] pub struct GasCost { - pub gas: Uint256, - pub gas_price: Uint256, + pub gas: U256, + pub gas_price: U256, } impl GasCost { - pub fn get_total(&self) -> Uint256 { - self.gas.clone() * self.gas_price.clone() + pub fn get_total(&self) -> U256 { + self.gas * self.gas_price } } diff --git a/orchestrator/ethereum_gravity/src/valset_update.rs b/orchestrator/ethereum_gravity/src/valset_update.rs index b99dc238a..1657f12f8 100644 --- a/orchestrator/ethereum_gravity/src/valset_update.rs +++ b/orchestrator/ethereum_gravity/src/valset_update.rs @@ -1,10 +1,15 @@ -use crate::utils::{get_valset_nonce, GasCost}; -use clarity::PrivateKey as EthPrivateKey; -use clarity::{Address as EthAddress, Uint256}; -use gravity_utils::types::*; -use gravity_utils::{error::GravityError, message_signatures::encode_valset_confirm_hashed}; -use std::{cmp::min, time::Duration}; -use web30::{client::Web3, types::TransactionRequest}; +use crate::{ + types::{EthClient, EthSignerMiddleware}, + utils::{get_gas_price, get_valset_nonce, GasCost}, +}; +use ethers::contract::builders::ContractCall; +use ethers::prelude::*; +use ethers::types::Address as EthAddress; +use gravity_abi::gravity::*; +use gravity_utils::{ + error::GravityError, message_signatures::encode_valset_confirm_hashed, types::*, +}; +use std::{result::Result, time::Duration}; /// this function generates an appropriate Ethereum transaction /// to submit the provided validator set and signatures. @@ -13,21 +18,21 @@ pub async fn send_eth_valset_update( new_valset: Valset, old_valset: Valset, confirms: &[ValsetConfirmResponse], - web3: &Web3, timeout: Duration, gravity_contract_address: EthAddress, gravity_id: String, - our_eth_key: EthPrivateKey, + gas_cost: GasCost, + eth_client: EthClient, ) -> Result<(), GravityError> { let old_nonce = old_valset.nonce; let new_nonce = new_valset.nonce; assert!(new_nonce > old_nonce); - let eth_address = our_eth_key.to_public_key().unwrap(); + info!( "Ordering signatures and submitting validator set {} -> {} update to Ethereum", old_nonce, new_nonce ); - let before_nonce = get_valset_nonce(gravity_contract_address, eth_address, web3).await?; + let before_nonce = get_valset_nonce(gravity_contract_address, eth_client.clone()).await?; if before_nonce != old_nonce { info!( "Someone else updated the valset to {}, exiting early", @@ -36,23 +41,34 @@ pub async fn send_eth_valset_update( return Ok(()); } - let payload = encode_valset_payload(new_valset, old_valset, confirms, gravity_id)?; + let contract_call = build_valset_update_contract_call( + &new_valset, + &old_valset, + confirms, + gravity_contract_address, + gravity_id, + eth_client.clone(), + )?; + let contract_call = contract_call + .gas(gas_cost.gas) + .gas_price(gas_cost.gas_price); - let tx = web3 - .send_transaction( - gravity_contract_address, - payload, - 0u32.into(), - eth_address, - our_eth_key, - vec![], - ) - .await?; - info!("Sent valset update with txid {:#066x}", tx); + let pending_tx = contract_call.send().await?; + let tx_hash = *pending_tx; + info!("Sent valset update with txid {}", tx_hash); + // TODO(bolten): ethers interval default is 7s, this mirrors what web30 was doing, should we adjust? + // additionally we are mirroring only waiting for 1 confirmation by leaving that as default + let pending_tx = pending_tx.interval(Duration::from_secs(1)); - web3.wait_for_transaction(tx, timeout, None).await?; + match tokio::time::timeout(timeout, pending_tx).await?? { + Some(_) => (), + None => error!( + "Did not receive transaction receipt when sending valset update: {}", + tx_hash + ), + } - let last_nonce = get_valset_nonce(gravity_contract_address, eth_address, web3).await?; + let last_nonce = get_valset_nonce(gravity_contract_address, eth_client.clone()).await?; if last_nonce != new_nonce { error!( "Current nonce is {} expected to update to nonce {}", @@ -64,6 +80,7 @@ pub async fn send_eth_valset_update( last_nonce ); } + Ok(()) } @@ -72,94 +89,59 @@ pub async fn estimate_valset_cost( new_valset: &Valset, old_valset: &Valset, confirms: &[ValsetConfirmResponse], - web3: &Web3, gravity_contract_address: EthAddress, gravity_id: String, - our_eth_key: EthPrivateKey, + eth_client: EthClient, ) -> Result { - let our_eth_address = our_eth_key.to_public_key().unwrap(); - let our_balance = web3.eth_get_balance(our_eth_address).await?; - let our_nonce = web3.eth_get_transaction_count(our_eth_address).await?; - let gas_limit = min((u64::MAX - 1).into(), our_balance.clone()); - let gas_price = web3.eth_gas_price().await?; - let zero: Uint256 = 0u8.into(); - let val = web3 - .eth_estimate_gas(TransactionRequest { - from: Some(our_eth_address), - to: gravity_contract_address, - nonce: Some(our_nonce.clone().into()), - gas_price: Some(gas_price.clone().into()), - gas: Some(gas_limit.into()), - value: Some(zero.into()), - data: Some( - encode_valset_payload( - new_valset.clone(), - old_valset.clone(), - confirms, - gravity_id, - )? - .into(), - ), - }) - .await?; + let contract_call = build_valset_update_contract_call( + new_valset, + old_valset, + confirms, + gravity_contract_address, + gravity_id, + eth_client.clone(), + )?; Ok(GasCost { - gas: val, - gas_price, + gas: contract_call.estimate_gas().await?, + gas_price: get_gas_price(eth_client.clone()).await?, }) } -/// Encodes the payload bytes for the validator set update call, useful for -/// estimating the cost of submitting a validator set -pub fn encode_valset_payload( - new_valset: Valset, - old_valset: Valset, +pub fn build_valset_update_contract_call( + new_valset: &Valset, + old_valset: &Valset, confirms: &[ValsetConfirmResponse], + gravity_contract_address: EthAddress, gravity_id: String, -) -> Result, GravityError> { + eth_client: EthClient, +) -> Result, GravityError> { let (old_addresses, old_powers) = old_valset.filter_empty_addresses(); let (new_addresses, new_powers) = new_valset.filter_empty_addresses(); - let old_nonce = old_valset.nonce; - let new_nonce = new_valset.nonce; + let old_powers: Vec = old_powers.iter().map(|power| (*power).into()).collect(); + let new_powers: Vec = new_powers.iter().map(|power| (*power).into()).collect(); // remember the signatures are over the new valset and therefore this is the value we must encode // the old valset exists only as a hash in the ethereum store - let hash = encode_valset_confirm_hashed(gravity_id, new_valset); + let hash = encode_valset_confirm_hashed(gravity_id, new_valset.clone()); // we need to use the old valset here because our signatures need to match the current // members of the validator set in the contract. let sig_data = old_valset.order_sigs(&hash, confirms)?; let sig_arrays = to_arrays(sig_data); - // Solidity function signature - // function updateValset( - // // The new version of the validator set - // address[] memory _newValidators, - // uint256[] memory _newPowers, - // uint256 _newValsetNonce, - // // The current validators that approve the change - // address[] memory _currentValidators, - // uint256[] memory _currentPowers, - // uint256 _currentValsetNonce, - // // These are arrays of the parts of the current validator's signatures - // uint8[] memory _v, - // bytes32[] memory _r, - // bytes32[] memory _s - let tokens = &[ - new_addresses.into(), - new_powers.into(), - new_nonce.into(), - old_addresses.into(), - old_powers.into(), - old_nonce.into(), - sig_arrays.v, - sig_arrays.r, - sig_arrays.s, - ]; - - let payload = clarity::abi::encode_call( - "updateValset(address[],uint256[],uint256,address[],uint256[],uint256,uint8[],bytes32[],bytes32[])", - tokens, - ).unwrap(); - - Ok(payload) + let contract = Gravity::new(gravity_contract_address, eth_client.clone()); + Ok(contract + .update_valset( + new_addresses, + new_powers, + new_valset.nonce.into(), + old_addresses, + old_powers, + old_valset.nonce.into(), + sig_arrays.v, + sig_arrays.r, + sig_arrays.s, + ) + .from(eth_client.address()) + .value(U256::zero())) } diff --git a/orchestrator/gorc/Cargo.toml b/orchestrator/gorc/Cargo.toml index 7db2a256b..15a963fb6 100644 --- a/orchestrator/gorc/Cargo.toml +++ b/orchestrator/gorc/Cargo.toml @@ -19,8 +19,9 @@ orchestrator = { path = "../orchestrator" } relayer = { path = "../relayer" } deep_space ={git="https://github.com/iqlusioninc/deep_space/", branch="master"} +ethers = "0.6.1" clarity = "0.4.12" -actix-rt = "2.2" +actix-rt = "2.5" rpassword = "5" bip32 = "0.2" k256 = { version = "0.9", features = ["pem"] } diff --git a/orchestrator/gorc/src/commands/cosmos_to_eth.rs b/orchestrator/gorc/src/commands/cosmos_to_eth.rs index d987f8ca9..bb53a525d 100644 --- a/orchestrator/gorc/src/commands/cosmos_to_eth.rs +++ b/orchestrator/gorc/src/commands/cosmos_to_eth.rs @@ -1,9 +1,9 @@ use crate::application::APP; use abscissa_core::{status_err, Application, Clap, Command, Runnable}; -use clarity::Address as EthAddress; use clarity::Uint256; use cosmos_gravity::send::{send_request_batch_tx, send_to_eth}; use deep_space::coin::Coin; +use ethers::types::Address as EthAddress; use gravity_proto::gravity::DenomToErc20Request; use gravity_utils::connection_prep::{check_for_fee_denom, create_rpc_connections}; use std::{process::exit, time::Duration}; @@ -27,6 +27,9 @@ pub fn one_atom() -> f64 { 1000000f64 } +// TODO(bolten): deep_space's Coin type relies internally on clarity's Uint256, +// and it would make this code super akward to try to get around that...replacing +// that here might be part of a broader deep_space replacement pub fn print_atom(input: Uint256) -> String { let float: f64 = input.to_string().parse().unwrap(); let res = float / one_atom(); @@ -54,7 +57,7 @@ impl Runnable for CosmosToEthCmd { let cosmos_prefix = config.cosmos.prefix.trim(); let cosmos_address = cosmos_key.to_address(cosmos_prefix).unwrap(); - let cosmos_grpc = config.cosmos.prefix.trim(); + let cosmos_grpc = config.cosmos.grpc.trim(); println!("Sending from Cosmos address {}", cosmos_address); abscissa_tokio::run_with_actix(&APP, async { let connections = create_rpc_connections( @@ -85,13 +88,14 @@ impl Runnable for CosmosToEthCmd { exit(1); } } + let amount = Coin { amount: amount.clone(), denom: gravity_denom.clone(), }; let bridge_fee = Coin { - denom: gravity_denom.clone(), amount: 1u64.into(), + denom: gravity_denom.clone(), }; let eth_dest = self.args.get(3).expect("ethereum destination is required"); diff --git a/orchestrator/gorc/src/commands/deploy/erc20.rs b/orchestrator/gorc/src/commands/deploy/erc20.rs index fda52afdf..702127387 100644 --- a/orchestrator/gorc/src/commands/deploy/erc20.rs +++ b/orchestrator/gorc/src/commands/deploy/erc20.rs @@ -1,11 +1,15 @@ use crate::{application::APP, prelude::*}; use abscissa_core::{Clap, Command, Runnable}; use ethereum_gravity::deploy_erc20::deploy_erc20; +use ethers::prelude::*; use gravity_proto::gravity::{DenomToErc20ParamsRequest, DenomToErc20Request}; -use gravity_utils::connection_prep::{check_for_eth, create_rpc_connections}; +use gravity_utils::{ + connection_prep::{check_for_eth, create_rpc_connections}, + ethereum::downcast_to_u64, +}; use std::convert::TryFrom; use std::process::exit; -use std::time::Duration; +use std::{sync::Arc, time::Duration}; use tokio::time::sleep as delay_for; /// Deploy Erc20 @@ -35,6 +39,7 @@ impl Erc20 { let config = APP.config(); + let ethereum_wallet = config.load_ethers_wallet(self.ethereum_key.clone()); let contract_address = config .gravity .contract @@ -50,12 +55,21 @@ impl Erc20 { ) .await; + let provider = connections.eth_provider.clone().unwrap(); + let chain_id = provider + .get_chainid() + .await + .expect("Could not retrieve chain ID"); + let chain_id = + downcast_to_u64(chain_id).expect("Chain ID overflowed when downcasting to u64"); + let eth_client = SignerMiddleware::new( + provider, + ethereum_wallet.clone().with_chain_id(chain_id), + ); + let eth_client = Arc::new(eth_client); let mut grpc = connections.grpc.clone().unwrap(); - let web3 = connections.web3.clone().unwrap(); - let ethereum_key = config.load_clarity_key(self.ethereum_key.clone()); - let ethereum_public_key = ethereum_key.to_public_key().unwrap(); - check_for_eth(ethereum_public_key, &web3).await; + check_for_eth(eth_client.address(), eth_client.clone()).await; let req = DenomToErc20ParamsRequest { denom: denom.clone(), @@ -75,15 +89,13 @@ impl Erc20 { res.erc20_symbol, u8::try_from(res.erc20_decimals).unwrap(), contract_address, - &web3, Some(timeout), - ethereum_key, - vec![], + eth_client.clone(), ) .await .expect("Could not deploy ERC20"); - println!("We have deployed ERC20 contract {:#066x}, waiting to see if the Cosmos chain choses to adopt it", res); + println!("We have deployed ERC20 contract {}, waiting to see if the Cosmos chain choses to adopt it", res); match tokio::time::timeout(Duration::from_secs(100), async { loop { diff --git a/orchestrator/gorc/src/commands/eth_to_cosmos.rs b/orchestrator/gorc/src/commands/eth_to_cosmos.rs index 4d7efe480..6c5079298 100644 --- a/orchestrator/gorc/src/commands/eth_to_cosmos.rs +++ b/orchestrator/gorc/src/commands/eth_to_cosmos.rs @@ -1,11 +1,15 @@ use crate::application::APP; use abscissa_core::{status_err, Application, Clap, Command, Runnable}; -use clarity::Address as EthAddress; -use clarity::Uint256; use deep_space::address::Address as CosmosAddress; +use ethereum_gravity::erc20_utils::get_erc20_balance; use ethereum_gravity::send_to_cosmos::send_to_cosmos; -use gravity_utils::connection_prep::{check_for_eth, create_rpc_connections}; -use std::time::Duration; +use ethers::prelude::*; +use ethers::types::Address as EthAddress; +use gravity_utils::{ + connection_prep::{check_for_eth, create_rpc_connections}, + ethereum::downcast_to_u64, +}; +use std::{sync::Arc, time::Duration}; const TIMEOUT: Duration = Duration::from_secs(60); @@ -24,7 +28,7 @@ impl Runnable for EthToCosmosCmd { .expect("Invalid ERC20 contract address!"); let ethereum_key = self.args.get(1).expect("key is required"); - let ethereum_key = config.load_clarity_key(ethereum_key.clone()); + let ethereum_wallet = config.load_ethers_wallet(ethereum_key.clone()); let contract_address = self.args.get(2).expect("contract address is required"); let contract_address: EthAddress = @@ -32,6 +36,7 @@ impl Runnable for EthToCosmosCmd { let cosmos_prefix = config.cosmos.prefix.trim(); let eth_rpc = config.ethereum.rpc.trim(); + abscissa_tokio::run_with_actix(&APP, async { let connections = create_rpc_connections( cosmos_prefix.to_string(), @@ -40,58 +45,69 @@ impl Runnable for EthToCosmosCmd { TIMEOUT, ) .await; - let web3 = connections.web3.unwrap(); + + let provider = connections.eth_provider.clone().unwrap(); + let chain_id = provider + .get_chainid() + .await + .expect("Could not retrieve chain ID"); + let chain_id = + downcast_to_u64(chain_id).expect("Chain ID overflowed when downcasting to u64"); + let eth_client = SignerMiddleware::new( + provider, + ethereum_wallet.clone().with_chain_id(chain_id), + ); + let eth_client = Arc::new(eth_client); let cosmos_dest = self.args.get(3).expect("cosmos destination is required"); let cosmos_dest: CosmosAddress = cosmos_dest.parse().unwrap(); - let ethereum_public_key = ethereum_key.to_public_key().unwrap(); - check_for_eth(ethereum_public_key, &web3).await; + let ethereum_address = eth_client.address(); + check_for_eth(ethereum_address, eth_client.clone()).await; let init_amount = self.args.get(4).expect("amount is required"); - let amount: Uint256 = init_amount.parse().unwrap(); + let amount: U256 = init_amount.parse().unwrap(); - let erc20_balance = web3 - .get_erc20_balance(erc20_address, ethereum_public_key) - .await - .expect("Failed to get balance, check ERC20 contract address"); + let erc20_balance = + get_erc20_balance(erc20_address, ethereum_address, eth_client.clone()) + .await + .expect("Failed to get balance, check ERC20 contract address"); let times = self.args.get(5).expect("times is required"); - let times = times.parse::().expect("cannot parse times"); + let times_usize = times.parse::().expect("cannot parse times"); + let times_u256 = U256::from_dec_str(times).expect("cannot parse times"); if erc20_balance == 0u8.into() { panic!( "You have zero {} tokens, please double check your sender and erc20 addresses!", contract_address ); - } else if amount.clone() * times.into() > erc20_balance { + } else if amount * times_u256 > erc20_balance { panic!( "Insufficient balance {} > {}", - amount * times.into(), + amount * times_u256, erc20_balance ); } - for _ in 0..times { + for _ in 0..times_usize { println!( "Sending {} / {} to Cosmos from {} to {}", init_amount.parse::().unwrap(), erc20_address, - ethereum_public_key, + ethereum_address, cosmos_dest ); // we send some erc20 tokens to the gravity contract to register a deposit let res = send_to_cosmos( erc20_address, contract_address, - amount.clone(), + amount, cosmos_dest, - ethereum_key, Some(TIMEOUT), - &web3, - vec![], + eth_client.clone(), ) .await; match res { - Ok(tx_id) => println!("Send to Cosmos txid: {:#066x}", tx_id), + Ok(tx_id) => println!("Send to Cosmos txid: {}", tx_id), Err(e) => println!("Failed to send tokens! {:?}", e), } } diff --git a/orchestrator/gorc/src/commands/keys/eth/show.rs b/orchestrator/gorc/src/commands/keys/eth/show.rs index c2ba159b1..0eadd9dd1 100644 --- a/orchestrator/gorc/src/commands/keys/eth/show.rs +++ b/orchestrator/gorc/src/commands/keys/eth/show.rs @@ -18,6 +18,9 @@ impl Runnable for ShowEthKeyCmd { fn run(&self) { let config = APP.config(); let name = self.args.get(0).expect("name is required"); + // TODO(bolten): is ethers wallet even capable of printing the public and + // private keys? for now, leaving load_clarity_key in config.rs and + // maintaining the functionality here let key = config.load_clarity_key(name.clone()); let pub_key = key.to_public_key().expect("Could not build public key"); diff --git a/orchestrator/gorc/src/commands/orchestrator/start.rs b/orchestrator/gorc/src/commands/orchestrator/start.rs index ec1c659f7..76b9cbd05 100644 --- a/orchestrator/gorc/src/commands/orchestrator/start.rs +++ b/orchestrator/gorc/src/commands/orchestrator/start.rs @@ -1,15 +1,18 @@ use crate::{application::APP, prelude::*}; use abscissa_core::{Clap, Command, Runnable}; -use clarity::address::Address as EthAddress; -use gravity_utils::connection_prep::{ - check_delegate_addresses, check_for_eth, check_for_fee_denom, create_rpc_connections, - wait_for_cosmos_node_ready, +use ethers::{prelude::*, types::Address as EthAddress}; +use gravity_utils::{ + connection_prep::{ + check_delegate_addresses, check_for_eth, check_for_fee_denom, create_rpc_connections, + wait_for_cosmos_node_ready, + }, + ethereum::{downcast_to_u64, format_eth_address}, }; use orchestrator::main_loop::{ orchestrator_main_loop, ETH_ORACLE_LOOP_SPEED, ETH_SIGNER_LOOP_SPEED, }; use relayer::main_loop::LOOP_SPEED as RELAYER_LOOP_SPEED; -use std::cmp::min; +use std::{cmp::min, sync::Arc}; /// Start the Orchestrator #[derive(Command, Debug, Clap)] @@ -34,8 +37,8 @@ impl Runnable for StartCommand { let cosmos_key = config.load_deep_space_key(self.cosmos_key.clone()); let cosmos_address = cosmos_key.to_address(&cosmos_prefix).unwrap(); - let ethereum_key = config.load_clarity_key(self.ethereum_key.clone()); - let ethereum_address = ethereum_key.to_public_key().unwrap(); + let ethereum_wallet = config.load_ethers_wallet(self.ethereum_key.clone()); + let ethereum_address = ethereum_wallet.address(); let contract_address: EthAddress = config .gravity @@ -61,10 +64,21 @@ impl Runnable for StartCommand { let mut grpc = connections.grpc.clone().unwrap(); let contact = connections.contact.clone().unwrap(); - let web3 = connections.web3.clone().unwrap(); + let provider = connections.eth_provider.clone().unwrap(); + let chain_id = provider + .get_chainid() + .await + .expect("Could not retrieve chain ID during orchestrator start"); + let chain_id = + downcast_to_u64(chain_id).expect("Chain ID overflowed when downcasting to u64"); + let eth_client = SignerMiddleware::new( + provider, + ethereum_wallet.clone().with_chain_id(chain_id), + ); + let eth_client = Arc::new(eth_client); info!("Starting Relayer + Oracle + Ethereum Signer"); - info!("Ethereum Address: {}", ethereum_address); + info!("Ethereum Address: {}", format_eth_address(ethereum_address)); info!("Cosmos Address {}", cosmos_address); // check if the cosmos node is syncing, if so wait for it @@ -83,21 +97,20 @@ impl Runnable for StartCommand { // check if we actually have the promised balance of tokens to pay fees check_for_fee_denom(&fees_denom, cosmos_address, &contact).await; - check_for_eth(ethereum_address, &web3).await; + check_for_eth(ethereum_address, eth_client.clone()).await; let gas_price = config.cosmos.gas_price.as_tuple(); orchestrator_main_loop( cosmos_key, - ethereum_key, - web3, contact, + eth_client, grpc, contract_address, gas_price, &config.metrics.listen_addr, config.ethereum.gas_price_multiplier, - config.ethereum.blocks_to_search as u128, + config.ethereum.blocks_to_search, config.cosmos.gas_adjustment, self.orchestrator_only, config.cosmos.msg_batch_size, diff --git a/orchestrator/gorc/src/commands/sign_delegate_keys.rs b/orchestrator/gorc/src/commands/sign_delegate_keys.rs index 9e0c769ee..fac2cf383 100644 --- a/orchestrator/gorc/src/commands/sign_delegate_keys.rs +++ b/orchestrator/gorc/src/commands/sign_delegate_keys.rs @@ -1,5 +1,6 @@ use crate::{application::APP, prelude::*}; use abscissa_core::{Application, Clap, Command, Runnable}; +use ethers::{prelude::Signer, utils::keccak256}; use gravity_proto::gravity as proto; use std::time::Duration; @@ -15,7 +16,7 @@ impl Runnable for SignDelegateKeysCmd { let config = APP.config(); abscissa_tokio::run_with_actix(&APP, async { let name = self.args.get(0).expect("ethereum-key-name is required"); - let key = config.load_clarity_key(name.clone()); + let ethereum_wallet = config.load_ethers_wallet(name.clone()); let val = self.args.get(1).expect("validator-address is required"); let address = val.parse().expect("Could not parse address"); @@ -46,7 +47,11 @@ impl Runnable for SignDelegateKeysCmd { let mut buf = bytes::BytesMut::with_capacity(size); prost::Message::encode(&msg, &mut buf).expect("Failed to encode DelegateKeysSignMsg!"); - let signature = key.sign_ethereum_msg(&buf); + let data = keccak256(buf); + let signature = ethereum_wallet + .sign_message(data) + .await + .expect("Could not sign DelegateKeysSignMsg"); println!("{}", signature); }) diff --git a/orchestrator/gorc/src/commands/tx/cosmos.rs b/orchestrator/gorc/src/commands/tx/cosmos.rs index b1eb5aa88..dda332e58 100644 --- a/orchestrator/gorc/src/commands/tx/cosmos.rs +++ b/orchestrator/gorc/src/commands/tx/cosmos.rs @@ -2,9 +2,10 @@ use crate::{application::APP, prelude::*, utils::*}; use abscissa_core::{Clap, Command, Runnable}; -use clarity::{Address as EthAddress, Uint256}; +use clarity::Uint256; use cosmos_gravity::send::send_to_eth; use deep_space::{coin::Coin, private_key::PrivateKey as CosmosPrivateKey}; +use ethers::types::Address as EthAddress; use gravity_proto::gravity::DenomToErc20Request; use gravity_utils::connection_prep::{check_for_fee_denom, create_rpc_connections}; use regex::Regex; @@ -62,6 +63,8 @@ impl Runnable for SendToEth { let cosmos_key = get_cosmos_key(&from_cosmos_key); + // TODO(bolten): I guess this command doesn't work yet? I hope no one is trying to + // call it let cosmos_address = cosmos_key.to_address("//TODO add to config file").unwrap(); println!("Sending from Cosmos address {}", cosmos_address); @@ -94,7 +97,7 @@ impl Runnable for SendToEth { exit(1); } } let amount = Coin { - amount, + amount: amount.clone(), denom: denom.clone(), }; let bridge_fee = Coin { diff --git a/orchestrator/gorc/src/commands/tx/eth.rs b/orchestrator/gorc/src/commands/tx/eth.rs index 155f78d57..948ec5c3f 100644 --- a/orchestrator/gorc/src/commands/tx/eth.rs +++ b/orchestrator/gorc/src/commands/tx/eth.rs @@ -2,11 +2,16 @@ use crate::{application::APP, prelude::*, utils::*}; use abscissa_core::{Clap, Command, Runnable}; -use clarity::Address as EthAddress; -use clarity::{PrivateKey as EthPrivateKey, Uint256}; use deep_space::address::Address as CosmosAddress; +use ethereum_gravity::erc20_utils::get_erc20_balance; use ethereum_gravity::send_to_cosmos::send_to_cosmos; -use gravity_utils::connection_prep::{check_for_eth, create_rpc_connections}; +use ethers::prelude::*; +use ethers::types::Address as EthAddress; +use gravity_utils::{ + connection_prep::{check_for_eth, create_rpc_connections}, + ethereum::{downcast_to_u64, format_eth_address}, +}; +use std::sync::Arc; /// Create transactions in Eth chain #[derive(Command, Debug, Clap)] @@ -28,7 +33,9 @@ pub struct SendToCosmos { help: bool, } -fn lookup_eth_key(_key: String) -> EthPrivateKey { +// TODO(bolten): I guess this command is also non-functional? +// are the commands under tx dead code? +fn lookup_eth_key(_key: String) -> LocalWallet { todo!() } @@ -45,11 +52,11 @@ impl Runnable for SendToCosmos { .parse() .expect("Expected a valid Eth Address"); let erc20_amount = self.free[3].clone(); - let eth_key = lookup_eth_key(from_eth_key); + let ethereum_wallet = lookup_eth_key(from_eth_key); println!( "Sending from Eth address {}", - eth_key.to_public_key().unwrap() + format_eth_address(ethereum_wallet.address()) ); let config = APP.config(); let cosmos_prefix = config.cosmos.prefix.clone(); @@ -66,18 +73,27 @@ impl Runnable for SendToCosmos { let connections = create_rpc_connections(cosmos_prefix, Some(cosmso_grpc), Some(eth_rpc), TIMEOUT) .await; - let web3 = connections.web3.unwrap(); - let ethereum_public_key = eth_key.to_public_key().unwrap(); - check_for_eth(ethereum_public_key, &web3).await; + let provider = connections.eth_provider.clone().unwrap(); + let chain_id = provider + .get_chainid() + .await + .expect("Could not retrieve chain ID"); + let chain_id = + downcast_to_u64(chain_id).expect("Chain ID overflowed when downcasting to u64"); + let eth_client = SignerMiddleware::new( + provider, + ethereum_wallet.clone().with_chain_id(chain_id), + ); + let eth_client = Arc::new(eth_client); + check_for_eth(eth_client.address(), eth_client.clone()).await; - let amount: Uint256 = erc20_amount - .parse() - .expect("Expected amount in xx.yy format"); + let amount = U256::from_dec_str(erc20_amount.as_str()) + .expect("Could not parse amount to U256"); - let erc20_balance = web3 - .get_erc20_balance(erc20_contract, ethereum_public_key) - .await - .expect("Failed to get balance, check ERC20 contract address"); + let erc20_balance = + get_erc20_balance(erc20_contract, eth_client.address(), eth_client.clone()) + .await + .expect("Failed to get balance, check ERC20 contract address"); if erc20_balance == 0u8.into() { panic!( @@ -87,7 +103,10 @@ impl Runnable for SendToCosmos { } println!( "Sending {} / {} to Cosmos from {} to {}", - amount, erc20_contract, ethereum_public_key, to_cosmos_addr + amount, + erc20_contract, + eth_client.address(), + to_cosmos_addr ); // we send some erc20 tokens to the gravity contract to register a deposit let res = send_to_cosmos( @@ -95,10 +114,8 @@ impl Runnable for SendToCosmos { contract_address, amount.clone(), to_cosmos_addr, - eth_key, Some(TIMEOUT), - &web3, - vec![], + eth_client.clone(), ) .await; match res { diff --git a/orchestrator/gorc/src/config.rs b/orchestrator/gorc/src/config.rs index 8cf93e66f..54d4010f5 100644 --- a/orchestrator/gorc/src/config.rs +++ b/orchestrator/gorc/src/config.rs @@ -1,3 +1,4 @@ +use ethers::signers::LocalWallet as EthWallet; use serde::{Deserialize, Serialize}; use signatory::FsKeyStore; use std::net::SocketAddr; @@ -27,6 +28,10 @@ impl GorcConfig { clarity::PrivateKey::from_slice(&key).expect("Could not convert key") } + pub fn load_ethers_wallet(&self, name: String) -> EthWallet { + EthWallet::from(self.load_secret_key(name)) + } + pub fn load_deep_space_key(&self, name: String) -> deep_space::private_key::PrivateKey { let key = self.load_secret_key(name).to_bytes(); let key = deep_space::utils::bytes_to_hex_str(&key); diff --git a/orchestrator/gravity_abi/Cargo.toml b/orchestrator/gravity_abi/Cargo.toml new file mode 100644 index 000000000..b76c5e281 --- /dev/null +++ b/orchestrator/gravity_abi/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "gravity_abi" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +ethers = "0.6.1" +serde = "1.0" +serde_derive = "1.0" +serde_json = "1.0.69" \ No newline at end of file diff --git a/orchestrator/gravity_abi/ERC20.json b/orchestrator/gravity_abi/ERC20.json new file mode 100644 index 000000000..5b22926bc --- /dev/null +++ b/orchestrator/gravity_abi/ERC20.json @@ -0,0 +1,288 @@ +[ + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "symbol", + "type": "string" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + } + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/orchestrator/gravity_abi/Gravity.json b/orchestrator/gravity_abi/Gravity.json new file mode 100644 index 000000000..e3def9e98 --- /dev/null +++ b/orchestrator/gravity_abi/Gravity.json @@ -0,0 +1,669 @@ +[ + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_gravityId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_powerThreshold", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "_validators", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "_powers", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "string", + "name": "_cosmosDenom", + "type": "string" + }, + { + "indexed": true, + "internalType": "address", + "name": "_tokenContract", + "type": "address" + }, + { + "indexed": false, + "internalType": "string", + "name": "_name", + "type": "string" + }, + { + "indexed": false, + "internalType": "string", + "name": "_symbol", + "type": "string" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "_decimals", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_eventNonce", + "type": "uint256" + } + ], + "name": "ERC20DeployedEvent", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "_invalidationId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_invalidationNonce", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "_returnData", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_eventNonce", + "type": "uint256" + } + ], + "name": "LogicCallEvent", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_tokenContract", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "_sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "_destination", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_eventNonce", + "type": "uint256" + } + ], + "name": "SendToCosmosEvent", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "_batchNonce", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_eventNonce", + "type": "uint256" + } + ], + "name": "TransactionBatchExecutedEvent", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "_newValsetNonce", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_eventNonce", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "_validators", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "_powers", + "type": "uint256[]" + } + ], + "name": "ValsetUpdatedEvent", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_cosmosDenom", + "type": "string" + }, + { + "internalType": "string", + "name": "_name", + "type": "string" + }, + { + "internalType": "string", + "name": "_symbol", + "type": "string" + }, + { + "internalType": "uint8", + "name": "_decimals", + "type": "uint8" + } + ], + "name": "deployERC20", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_erc20Address", + "type": "address" + } + ], + "name": "lastBatchNonce", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_invalidation_id", + "type": "bytes32" + } + ], + "name": "lastLogicCallNonce", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_tokenContract", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_destination", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "sendToCosmos", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "state_gravityId", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "state_invalidationMapping", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "state_lastBatchNonces", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "state_lastEventNonce", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "state_lastValsetCheckpoint", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "state_lastValsetNonce", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "state_powerThreshold", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "_currentValidators", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "_currentPowers", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "_currentValsetNonce", + "type": "uint256" + }, + { + "internalType": "uint8[]", + "name": "_v", + "type": "uint8[]" + }, + { + "internalType": "bytes32[]", + "name": "_r", + "type": "bytes32[]" + }, + { + "internalType": "bytes32[]", + "name": "_s", + "type": "bytes32[]" + }, + { + "internalType": "uint256[]", + "name": "_amounts", + "type": "uint256[]" + }, + { + "internalType": "address[]", + "name": "_destinations", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "_fees", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "_batchNonce", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_tokenContract", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_batchTimeout", + "type": "uint256" + } + ], + "name": "submitBatch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "_currentValidators", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "_currentPowers", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "_currentValsetNonce", + "type": "uint256" + }, + { + "internalType": "uint8[]", + "name": "_v", + "type": "uint8[]" + }, + { + "internalType": "bytes32[]", + "name": "_r", + "type": "bytes32[]" + }, + { + "internalType": "bytes32[]", + "name": "_s", + "type": "bytes32[]" + }, + { + "components": [ + { + "internalType": "uint256[]", + "name": "transferAmounts", + "type": "uint256[]" + }, + { + "internalType": "address[]", + "name": "transferTokenContracts", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "feeAmounts", + "type": "uint256[]" + }, + { + "internalType": "address[]", + "name": "feeTokenContracts", + "type": "address[]" + }, + { + "internalType": "address", + "name": "logicContractAddress", + "type": "address" + }, + { + "internalType": "bytes", + "name": "payload", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "timeOut", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "invalidationId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "invalidationNonce", + "type": "uint256" + } + ], + "internalType": "struct LogicCallArgs", + "name": "_args", + "type": "tuple" + } + ], + "name": "submitLogicCall", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "_currentValidators", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "_currentPowers", + "type": "uint256[]" + }, + { + "internalType": "uint8[]", + "name": "_v", + "type": "uint8[]" + }, + { + "internalType": "bytes32[]", + "name": "_r", + "type": "bytes32[]" + }, + { + "internalType": "bytes32[]", + "name": "_s", + "type": "bytes32[]" + }, + { + "internalType": "bytes32", + "name": "_theHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_powerThreshold", + "type": "uint256" + } + ], + "name": "testCheckValidatorSignatures", + "outputs": [], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "_validators", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "_powers", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "_valsetNonce", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "_gravityId", + "type": "bytes32" + } + ], + "name": "testMakeCheckpoint", + "outputs": [], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "_newValidators", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "_newPowers", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "_newValsetNonce", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "_currentValidators", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "_currentPowers", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "_currentValsetNonce", + "type": "uint256" + }, + { + "internalType": "uint8[]", + "name": "_v", + "type": "uint8[]" + }, + { + "internalType": "bytes32[]", + "name": "_r", + "type": "bytes32[]" + }, + { + "internalType": "bytes32[]", + "name": "_s", + "type": "bytes32[]" + } + ], + "name": "updateValset", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/orchestrator/gravity_abi/src/erc20.rs b/orchestrator/gravity_abi/src/erc20.rs new file mode 100644 index 000000000..08a6b45e2 --- /dev/null +++ b/orchestrator/gravity_abi/src/erc20.rs @@ -0,0 +1,559 @@ +pub use erc20_mod::*; +#[allow(clippy::too_many_arguments)] +mod erc20_mod { + #![allow(clippy::enum_variant_names)] + #![allow(dead_code)] + #![allow(clippy::type_complexity)] + #![allow(unused_imports)] + use ethers::contract::{ + builders::{ContractCall, Event}, + Contract, Lazy, + }; + use ethers::core::{ + abi::{Abi, Detokenize, InvalidOutputType, Token, Tokenizable}, + types::*, + }; + use ethers::providers::Middleware; + #[doc = "ERC20 was auto-generated with ethers-rs Abigen. More information at: https://github.com/gakonst/ethers-rs"] + use std::sync::Arc; + pub static ERC20_ABI: ethers::contract::Lazy = + ethers::contract::Lazy::new(|| { + serde_json :: from_str ("[\n {\n \"inputs\": [\n {\n \"internalType\": \"string\",\n \"name\": \"name\",\n \"type\": \"string\"\n },\n {\n \"internalType\": \"string\",\n \"name\": \"symbol\",\n \"type\": \"string\"\n }\n ],\n \"stateMutability\": \"nonpayable\",\n \"type\": \"constructor\"\n },\n {\n \"anonymous\": false,\n \"inputs\": [\n {\n \"indexed\": true,\n \"internalType\": \"address\",\n \"name\": \"owner\",\n \"type\": \"address\"\n },\n {\n \"indexed\": true,\n \"internalType\": \"address\",\n \"name\": \"spender\",\n \"type\": \"address\"\n },\n {\n \"indexed\": false,\n \"internalType\": \"uint256\",\n \"name\": \"value\",\n \"type\": \"uint256\"\n }\n ],\n \"name\": \"Approval\",\n \"type\": \"event\"\n },\n {\n \"anonymous\": false,\n \"inputs\": [\n {\n \"indexed\": true,\n \"internalType\": \"address\",\n \"name\": \"from\",\n \"type\": \"address\"\n },\n {\n \"indexed\": true,\n \"internalType\": \"address\",\n \"name\": \"to\",\n \"type\": \"address\"\n },\n {\n \"indexed\": false,\n \"internalType\": \"uint256\",\n \"name\": \"value\",\n \"type\": \"uint256\"\n }\n ],\n \"name\": \"Transfer\",\n \"type\": \"event\"\n },\n {\n \"inputs\": [\n {\n \"internalType\": \"address\",\n \"name\": \"owner\",\n \"type\": \"address\"\n },\n {\n \"internalType\": \"address\",\n \"name\": \"spender\",\n \"type\": \"address\"\n }\n ],\n \"name\": \"allowance\",\n \"outputs\": [\n {\n \"internalType\": \"uint256\",\n \"name\": \"\",\n \"type\": \"uint256\"\n }\n ],\n \"stateMutability\": \"view\",\n \"type\": \"function\"\n },\n {\n \"inputs\": [\n {\n \"internalType\": \"address\",\n \"name\": \"spender\",\n \"type\": \"address\"\n },\n {\n \"internalType\": \"uint256\",\n \"name\": \"amount\",\n \"type\": \"uint256\"\n }\n ],\n \"name\": \"approve\",\n \"outputs\": [\n {\n \"internalType\": \"bool\",\n \"name\": \"\",\n \"type\": \"bool\"\n }\n ],\n \"stateMutability\": \"nonpayable\",\n \"type\": \"function\"\n },\n {\n \"inputs\": [\n {\n \"internalType\": \"address\",\n \"name\": \"account\",\n \"type\": \"address\"\n }\n ],\n \"name\": \"balanceOf\",\n \"outputs\": [\n {\n \"internalType\": \"uint256\",\n \"name\": \"\",\n \"type\": \"uint256\"\n }\n ],\n \"stateMutability\": \"view\",\n \"type\": \"function\"\n },\n {\n \"inputs\": [],\n \"name\": \"decimals\",\n \"outputs\": [\n {\n \"internalType\": \"uint8\",\n \"name\": \"\",\n \"type\": \"uint8\"\n }\n ],\n \"stateMutability\": \"view\",\n \"type\": \"function\"\n },\n {\n \"inputs\": [\n {\n \"internalType\": \"address\",\n \"name\": \"spender\",\n \"type\": \"address\"\n },\n {\n \"internalType\": \"uint256\",\n \"name\": \"subtractedValue\",\n \"type\": \"uint256\"\n }\n ],\n \"name\": \"decreaseAllowance\",\n \"outputs\": [\n {\n \"internalType\": \"bool\",\n \"name\": \"\",\n \"type\": \"bool\"\n }\n ],\n \"stateMutability\": \"nonpayable\",\n \"type\": \"function\"\n },\n {\n \"inputs\": [\n {\n \"internalType\": \"address\",\n \"name\": \"spender\",\n \"type\": \"address\"\n },\n {\n \"internalType\": \"uint256\",\n \"name\": \"addedValue\",\n \"type\": \"uint256\"\n }\n ],\n \"name\": \"increaseAllowance\",\n \"outputs\": [\n {\n \"internalType\": \"bool\",\n \"name\": \"\",\n \"type\": \"bool\"\n }\n ],\n \"stateMutability\": \"nonpayable\",\n \"type\": \"function\"\n },\n {\n \"inputs\": [],\n \"name\": \"name\",\n \"outputs\": [\n {\n \"internalType\": \"string\",\n \"name\": \"\",\n \"type\": \"string\"\n }\n ],\n \"stateMutability\": \"view\",\n \"type\": \"function\"\n },\n {\n \"inputs\": [],\n \"name\": \"symbol\",\n \"outputs\": [\n {\n \"internalType\": \"string\",\n \"name\": \"\",\n \"type\": \"string\"\n }\n ],\n \"stateMutability\": \"view\",\n \"type\": \"function\"\n },\n {\n \"inputs\": [],\n \"name\": \"totalSupply\",\n \"outputs\": [\n {\n \"internalType\": \"uint256\",\n \"name\": \"\",\n \"type\": \"uint256\"\n }\n ],\n \"stateMutability\": \"view\",\n \"type\": \"function\"\n },\n {\n \"inputs\": [\n {\n \"internalType\": \"address\",\n \"name\": \"recipient\",\n \"type\": \"address\"\n },\n {\n \"internalType\": \"uint256\",\n \"name\": \"amount\",\n \"type\": \"uint256\"\n }\n ],\n \"name\": \"transfer\",\n \"outputs\": [\n {\n \"internalType\": \"bool\",\n \"name\": \"\",\n \"type\": \"bool\"\n }\n ],\n \"stateMutability\": \"nonpayable\",\n \"type\": \"function\"\n },\n {\n \"inputs\": [\n {\n \"internalType\": \"address\",\n \"name\": \"sender\",\n \"type\": \"address\"\n },\n {\n \"internalType\": \"address\",\n \"name\": \"recipient\",\n \"type\": \"address\"\n },\n {\n \"internalType\": \"uint256\",\n \"name\": \"amount\",\n \"type\": \"uint256\"\n }\n ],\n \"name\": \"transferFrom\",\n \"outputs\": [\n {\n \"internalType\": \"bool\",\n \"name\": \"\",\n \"type\": \"bool\"\n }\n ],\n \"stateMutability\": \"nonpayable\",\n \"type\": \"function\"\n }\n]\n") . expect ("invalid abi") + }); + #[derive(Clone)] + pub struct ERC20(ethers::contract::Contract); + impl std::ops::Deref for ERC20 { + type Target = ethers::contract::Contract; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl std::fmt::Debug for ERC20 { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.debug_tuple(stringify!(ERC20)) + .field(&self.address()) + .finish() + } + } + impl<'a, M: ethers::providers::Middleware> ERC20 { + #[doc = r" Creates a new contract instance with the specified `ethers`"] + #[doc = r" client at the given `Address`. The contract derefs to a `ethers::Contract`"] + #[doc = r" object"] + pub fn new>( + address: T, + client: ::std::sync::Arc, + ) -> Self { + let contract = + ethers::contract::Contract::new(address.into(), ERC20_ABI.clone(), client); + Self(contract) + } + #[doc = "Calls the contract's `allowance` (0xdd62ed3e) function"] + pub fn allowance( + &self, + owner: ethers::core::types::Address, + spender: ethers::core::types::Address, + ) -> ethers::contract::builders::ContractCall { + self.0 + .method_hash([221, 98, 237, 62], (owner, spender)) + .expect("method not found (this should never happen)") + } + #[doc = "Calls the contract's `approve` (0x095ea7b3) function"] + pub fn approve( + &self, + spender: ethers::core::types::Address, + amount: ethers::core::types::U256, + ) -> ethers::contract::builders::ContractCall { + self.0 + .method_hash([9, 94, 167, 179], (spender, amount)) + .expect("method not found (this should never happen)") + } + #[doc = "Calls the contract's `balanceOf` (0x70a08231) function"] + pub fn balance_of( + &self, + account: ethers::core::types::Address, + ) -> ethers::contract::builders::ContractCall { + self.0 + .method_hash([112, 160, 130, 49], account) + .expect("method not found (this should never happen)") + } + #[doc = "Calls the contract's `decimals` (0x313ce567) function"] + pub fn decimals(&self) -> ethers::contract::builders::ContractCall { + self.0 + .method_hash([49, 60, 229, 103], ()) + .expect("method not found (this should never happen)") + } + #[doc = "Calls the contract's `decreaseAllowance` (0xa457c2d7) function"] + pub fn decrease_allowance( + &self, + spender: ethers::core::types::Address, + subtracted_value: ethers::core::types::U256, + ) -> ethers::contract::builders::ContractCall { + self.0 + .method_hash([164, 87, 194, 215], (spender, subtracted_value)) + .expect("method not found (this should never happen)") + } + #[doc = "Calls the contract's `increaseAllowance` (0x39509351) function"] + pub fn increase_allowance( + &self, + spender: ethers::core::types::Address, + added_value: ethers::core::types::U256, + ) -> ethers::contract::builders::ContractCall { + self.0 + .method_hash([57, 80, 147, 81], (spender, added_value)) + .expect("method not found (this should never happen)") + } + #[doc = "Calls the contract's `name` (0x06fdde03) function"] + pub fn name(&self) -> ethers::contract::builders::ContractCall { + self.0 + .method_hash([6, 253, 222, 3], ()) + .expect("method not found (this should never happen)") + } + #[doc = "Calls the contract's `symbol` (0x95d89b41) function"] + pub fn symbol(&self) -> ethers::contract::builders::ContractCall { + self.0 + .method_hash([149, 216, 155, 65], ()) + .expect("method not found (this should never happen)") + } + #[doc = "Calls the contract's `totalSupply` (0x18160ddd) function"] + pub fn total_supply( + &self, + ) -> ethers::contract::builders::ContractCall { + self.0 + .method_hash([24, 22, 13, 221], ()) + .expect("method not found (this should never happen)") + } + #[doc = "Calls the contract's `transfer` (0xa9059cbb) function"] + pub fn transfer( + &self, + recipient: ethers::core::types::Address, + amount: ethers::core::types::U256, + ) -> ethers::contract::builders::ContractCall { + self.0 + .method_hash([169, 5, 156, 187], (recipient, amount)) + .expect("method not found (this should never happen)") + } + #[doc = "Calls the contract's `transferFrom` (0x23b872dd) function"] + pub fn transfer_from( + &self, + sender: ethers::core::types::Address, + recipient: ethers::core::types::Address, + amount: ethers::core::types::U256, + ) -> ethers::contract::builders::ContractCall { + self.0 + .method_hash([35, 184, 114, 221], (sender, recipient, amount)) + .expect("method not found (this should never happen)") + } + #[doc = "Gets the contract's `Approval` event"] + pub fn approval_filter(&self) -> ethers::contract::builders::Event { + self.0.event() + } + #[doc = "Gets the contract's `Transfer` event"] + pub fn transfer_filter(&self) -> ethers::contract::builders::Event { + self.0.event() + } + #[doc = r" Returns an [`Event`](#ethers_contract::builders::Event) builder for all events of this contract"] + pub fn events(&self) -> ethers::contract::builders::Event { + self.0.event_with_filter(Default::default()) + } + } + #[derive( + Clone, + Debug, + Default, + Eq, + PartialEq, + ethers :: contract :: EthEvent, + ethers :: contract :: EthDisplay, + serde :: Deserialize, + serde :: Serialize, + )] + #[ethevent(name = "Approval", abi = "Approval(address,address,uint256)")] + pub struct ApprovalFilter { + #[ethevent(indexed)] + pub owner: ethers::core::types::Address, + #[ethevent(indexed)] + pub spender: ethers::core::types::Address, + pub value: ethers::core::types::U256, + } + #[derive( + Clone, + Debug, + Default, + Eq, + PartialEq, + ethers :: contract :: EthEvent, + ethers :: contract :: EthDisplay, + serde :: Deserialize, + serde :: Serialize, + )] + #[ethevent(name = "Transfer", abi = "Transfer(address,address,uint256)")] + pub struct TransferFilter { + #[ethevent(indexed)] + pub from: ethers::core::types::Address, + #[ethevent(indexed)] + pub to: ethers::core::types::Address, + pub value: ethers::core::types::U256, + } + #[derive(Debug, Clone, PartialEq, Eq, ethers :: contract :: EthAbiType)] + pub enum ERC20Events { + ApprovalFilter(ApprovalFilter), + TransferFilter(TransferFilter), + } + impl ethers::contract::EthLogDecode for ERC20Events { + fn decode_log(log: ðers::core::abi::RawLog) -> Result + where + Self: Sized, + { + if let Ok(decoded) = ApprovalFilter::decode_log(log) { + return Ok(ERC20Events::ApprovalFilter(decoded)); + } + if let Ok(decoded) = TransferFilter::decode_log(log) { + return Ok(ERC20Events::TransferFilter(decoded)); + } + Err(ethers::core::abi::Error::InvalidData) + } + } + impl ::std::fmt::Display for ERC20Events { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match self { + ERC20Events::ApprovalFilter(element) => element.fmt(f), + ERC20Events::TransferFilter(element) => element.fmt(f), + } + } + } + #[doc = "Container type for all input parameters for the `allowance`function with signature `allowance(address,address)` and selector `[221, 98, 237, 62]`"] + #[derive( + Clone, + Debug, + Default, + Eq, + PartialEq, + ethers :: contract :: EthCall, + ethers :: contract :: EthDisplay, + serde :: Deserialize, + serde :: Serialize, + )] + #[ethcall(name = "allowance", abi = "allowance(address,address)")] + pub struct AllowanceCall { + pub owner: ethers::core::types::Address, + pub spender: ethers::core::types::Address, + } + #[doc = "Container type for all input parameters for the `approve`function with signature `approve(address,uint256)` and selector `[9, 94, 167, 179]`"] + #[derive( + Clone, + Debug, + Default, + Eq, + PartialEq, + ethers :: contract :: EthCall, + ethers :: contract :: EthDisplay, + serde :: Deserialize, + serde :: Serialize, + )] + #[ethcall(name = "approve", abi = "approve(address,uint256)")] + pub struct ApproveCall { + pub spender: ethers::core::types::Address, + pub amount: ethers::core::types::U256, + } + #[doc = "Container type for all input parameters for the `balanceOf`function with signature `balanceOf(address)` and selector `[112, 160, 130, 49]`"] + #[derive( + Clone, + Debug, + Default, + Eq, + PartialEq, + ethers :: contract :: EthCall, + ethers :: contract :: EthDisplay, + serde :: Deserialize, + serde :: Serialize, + )] + #[ethcall(name = "balanceOf", abi = "balanceOf(address)")] + pub struct BalanceOfCall { + pub account: ethers::core::types::Address, + } + #[doc = "Container type for all input parameters for the `decimals`function with signature `decimals()` and selector `[49, 60, 229, 103]`"] + #[derive( + Clone, + Debug, + Default, + Eq, + PartialEq, + ethers :: contract :: EthCall, + ethers :: contract :: EthDisplay, + serde :: Deserialize, + serde :: Serialize, + )] + #[ethcall(name = "decimals", abi = "decimals()")] + pub struct DecimalsCall; + #[doc = "Container type for all input parameters for the `decreaseAllowance`function with signature `decreaseAllowance(address,uint256)` and selector `[164, 87, 194, 215]`"] + #[derive( + Clone, + Debug, + Default, + Eq, + PartialEq, + ethers :: contract :: EthCall, + ethers :: contract :: EthDisplay, + serde :: Deserialize, + serde :: Serialize, + )] + #[ethcall(name = "decreaseAllowance", abi = "decreaseAllowance(address,uint256)")] + pub struct DecreaseAllowanceCall { + pub spender: ethers::core::types::Address, + pub subtracted_value: ethers::core::types::U256, + } + #[doc = "Container type for all input parameters for the `increaseAllowance`function with signature `increaseAllowance(address,uint256)` and selector `[57, 80, 147, 81]`"] + #[derive( + Clone, + Debug, + Default, + Eq, + PartialEq, + ethers :: contract :: EthCall, + ethers :: contract :: EthDisplay, + serde :: Deserialize, + serde :: Serialize, + )] + #[ethcall(name = "increaseAllowance", abi = "increaseAllowance(address,uint256)")] + pub struct IncreaseAllowanceCall { + pub spender: ethers::core::types::Address, + pub added_value: ethers::core::types::U256, + } + #[doc = "Container type for all input parameters for the `name`function with signature `name()` and selector `[6, 253, 222, 3]`"] + #[derive( + Clone, + Debug, + Default, + Eq, + PartialEq, + ethers :: contract :: EthCall, + ethers :: contract :: EthDisplay, + serde :: Deserialize, + serde :: Serialize, + )] + #[ethcall(name = "name", abi = "name()")] + pub struct NameCall; + #[doc = "Container type for all input parameters for the `symbol`function with signature `symbol()` and selector `[149, 216, 155, 65]`"] + #[derive( + Clone, + Debug, + Default, + Eq, + PartialEq, + ethers :: contract :: EthCall, + ethers :: contract :: EthDisplay, + serde :: Deserialize, + serde :: Serialize, + )] + #[ethcall(name = "symbol", abi = "symbol()")] + pub struct SymbolCall; + #[doc = "Container type for all input parameters for the `totalSupply`function with signature `totalSupply()` and selector `[24, 22, 13, 221]`"] + #[derive( + Clone, + Debug, + Default, + Eq, + PartialEq, + ethers :: contract :: EthCall, + ethers :: contract :: EthDisplay, + serde :: Deserialize, + serde :: Serialize, + )] + #[ethcall(name = "totalSupply", abi = "totalSupply()")] + pub struct TotalSupplyCall; + #[doc = "Container type for all input parameters for the `transfer`function with signature `transfer(address,uint256)` and selector `[169, 5, 156, 187]`"] + #[derive( + Clone, + Debug, + Default, + Eq, + PartialEq, + ethers :: contract :: EthCall, + ethers :: contract :: EthDisplay, + serde :: Deserialize, + serde :: Serialize, + )] + #[ethcall(name = "transfer", abi = "transfer(address,uint256)")] + pub struct TransferCall { + pub recipient: ethers::core::types::Address, + pub amount: ethers::core::types::U256, + } + #[doc = "Container type for all input parameters for the `transferFrom`function with signature `transferFrom(address,address,uint256)` and selector `[35, 184, 114, 221]`"] + #[derive( + Clone, + Debug, + Default, + Eq, + PartialEq, + ethers :: contract :: EthCall, + ethers :: contract :: EthDisplay, + serde :: Deserialize, + serde :: Serialize, + )] + #[ethcall(name = "transferFrom", abi = "transferFrom(address,address,uint256)")] + pub struct TransferFromCall { + pub sender: ethers::core::types::Address, + pub recipient: ethers::core::types::Address, + pub amount: ethers::core::types::U256, + } + #[derive(Debug, Clone, PartialEq, Eq, ethers :: contract :: EthAbiType)] + pub enum ERC20Calls { + Allowance(AllowanceCall), + Approve(ApproveCall), + BalanceOf(BalanceOfCall), + Decimals(DecimalsCall), + DecreaseAllowance(DecreaseAllowanceCall), + IncreaseAllowance(IncreaseAllowanceCall), + Name(NameCall), + Symbol(SymbolCall), + TotalSupply(TotalSupplyCall), + Transfer(TransferCall), + TransferFrom(TransferFromCall), + } + impl ethers::core::abi::AbiDecode for ERC20Calls { + fn decode(data: impl AsRef<[u8]>) -> Result { + if let Ok(decoded) = + ::decode(data.as_ref()) + { + return Ok(ERC20Calls::Allowance(decoded)); + } + if let Ok(decoded) = + ::decode(data.as_ref()) + { + return Ok(ERC20Calls::Approve(decoded)); + } + if let Ok(decoded) = + ::decode(data.as_ref()) + { + return Ok(ERC20Calls::BalanceOf(decoded)); + } + if let Ok(decoded) = + ::decode(data.as_ref()) + { + return Ok(ERC20Calls::Decimals(decoded)); + } + if let Ok(decoded) = + ::decode(data.as_ref()) + { + return Ok(ERC20Calls::DecreaseAllowance(decoded)); + } + if let Ok(decoded) = + ::decode(data.as_ref()) + { + return Ok(ERC20Calls::IncreaseAllowance(decoded)); + } + if let Ok(decoded) = ::decode(data.as_ref()) { + return Ok(ERC20Calls::Name(decoded)); + } + if let Ok(decoded) = ::decode(data.as_ref()) + { + return Ok(ERC20Calls::Symbol(decoded)); + } + if let Ok(decoded) = + ::decode(data.as_ref()) + { + return Ok(ERC20Calls::TotalSupply(decoded)); + } + if let Ok(decoded) = + ::decode(data.as_ref()) + { + return Ok(ERC20Calls::Transfer(decoded)); + } + if let Ok(decoded) = + ::decode(data.as_ref()) + { + return Ok(ERC20Calls::TransferFrom(decoded)); + } + Err(ethers::core::abi::Error::InvalidData.into()) + } + } + impl ethers::core::abi::AbiEncode for ERC20Calls { + fn encode(self) -> Vec { + match self { + ERC20Calls::Allowance(element) => element.encode(), + ERC20Calls::Approve(element) => element.encode(), + ERC20Calls::BalanceOf(element) => element.encode(), + ERC20Calls::Decimals(element) => element.encode(), + ERC20Calls::DecreaseAllowance(element) => element.encode(), + ERC20Calls::IncreaseAllowance(element) => element.encode(), + ERC20Calls::Name(element) => element.encode(), + ERC20Calls::Symbol(element) => element.encode(), + ERC20Calls::TotalSupply(element) => element.encode(), + ERC20Calls::Transfer(element) => element.encode(), + ERC20Calls::TransferFrom(element) => element.encode(), + } + } + } + impl ::std::fmt::Display for ERC20Calls { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match self { + ERC20Calls::Allowance(element) => element.fmt(f), + ERC20Calls::Approve(element) => element.fmt(f), + ERC20Calls::BalanceOf(element) => element.fmt(f), + ERC20Calls::Decimals(element) => element.fmt(f), + ERC20Calls::DecreaseAllowance(element) => element.fmt(f), + ERC20Calls::IncreaseAllowance(element) => element.fmt(f), + ERC20Calls::Name(element) => element.fmt(f), + ERC20Calls::Symbol(element) => element.fmt(f), + ERC20Calls::TotalSupply(element) => element.fmt(f), + ERC20Calls::Transfer(element) => element.fmt(f), + ERC20Calls::TransferFrom(element) => element.fmt(f), + } + } + } + impl ::std::convert::From for ERC20Calls { + fn from(var: AllowanceCall) -> Self { + ERC20Calls::Allowance(var) + } + } + impl ::std::convert::From for ERC20Calls { + fn from(var: ApproveCall) -> Self { + ERC20Calls::Approve(var) + } + } + impl ::std::convert::From for ERC20Calls { + fn from(var: BalanceOfCall) -> Self { + ERC20Calls::BalanceOf(var) + } + } + impl ::std::convert::From for ERC20Calls { + fn from(var: DecimalsCall) -> Self { + ERC20Calls::Decimals(var) + } + } + impl ::std::convert::From for ERC20Calls { + fn from(var: DecreaseAllowanceCall) -> Self { + ERC20Calls::DecreaseAllowance(var) + } + } + impl ::std::convert::From for ERC20Calls { + fn from(var: IncreaseAllowanceCall) -> Self { + ERC20Calls::IncreaseAllowance(var) + } + } + impl ::std::convert::From for ERC20Calls { + fn from(var: NameCall) -> Self { + ERC20Calls::Name(var) + } + } + impl ::std::convert::From for ERC20Calls { + fn from(var: SymbolCall) -> Self { + ERC20Calls::Symbol(var) + } + } + impl ::std::convert::From for ERC20Calls { + fn from(var: TotalSupplyCall) -> Self { + ERC20Calls::TotalSupply(var) + } + } + impl ::std::convert::From for ERC20Calls { + fn from(var: TransferCall) -> Self { + ERC20Calls::Transfer(var) + } + } + impl ::std::convert::From for ERC20Calls { + fn from(var: TransferFromCall) -> Self { + ERC20Calls::TransferFrom(var) + } + } +} diff --git a/orchestrator/gravity_abi/src/gravity.rs b/orchestrator/gravity_abi/src/gravity.rs new file mode 100644 index 000000000..151dd6f2f --- /dev/null +++ b/orchestrator/gravity_abi/src/gravity.rs @@ -0,0 +1,1029 @@ +pub use gravity_mod::*; +#[allow(clippy::too_many_arguments)] +mod gravity_mod { + #![allow(clippy::enum_variant_names)] + #![allow(dead_code)] + #![allow(clippy::type_complexity)] + #![allow(unused_imports)] + use ethers::contract::{ + builders::{ContractCall, Event}, + Contract, Lazy, + }; + use ethers::core::{ + abi::{Abi, Detokenize, InvalidOutputType, Token, Tokenizable}, + types::*, + }; + use ethers::providers::Middleware; + #[doc = "Gravity was auto-generated with ethers-rs Abigen. More information at: https://github.com/gakonst/ethers-rs"] + use std::sync::Arc; + pub static GRAVITY_ABI: ethers::contract::Lazy = + ethers::contract::Lazy::new(|| { + serde_json :: from_str ("[\n {\n \"inputs\": [\n {\n \"internalType\": \"bytes32\",\n \"name\": \"_gravityId\",\n \"type\": \"bytes32\"\n },\n {\n \"internalType\": \"uint256\",\n \"name\": \"_powerThreshold\",\n \"type\": \"uint256\"\n },\n {\n \"internalType\": \"address[]\",\n \"name\": \"_validators\",\n \"type\": \"address[]\"\n },\n {\n \"internalType\": \"uint256[]\",\n \"name\": \"_powers\",\n \"type\": \"uint256[]\"\n }\n ],\n \"stateMutability\": \"nonpayable\",\n \"type\": \"constructor\"\n },\n {\n \"anonymous\": false,\n \"inputs\": [\n {\n \"indexed\": false,\n \"internalType\": \"string\",\n \"name\": \"_cosmosDenom\",\n \"type\": \"string\"\n },\n {\n \"indexed\": true,\n \"internalType\": \"address\",\n \"name\": \"_tokenContract\",\n \"type\": \"address\"\n },\n {\n \"indexed\": false,\n \"internalType\": \"string\",\n \"name\": \"_name\",\n \"type\": \"string\"\n },\n {\n \"indexed\": false,\n \"internalType\": \"string\",\n \"name\": \"_symbol\",\n \"type\": \"string\"\n },\n {\n \"indexed\": false,\n \"internalType\": \"uint8\",\n \"name\": \"_decimals\",\n \"type\": \"uint8\"\n },\n {\n \"indexed\": false,\n \"internalType\": \"uint256\",\n \"name\": \"_eventNonce\",\n \"type\": \"uint256\"\n }\n ],\n \"name\": \"ERC20DeployedEvent\",\n \"type\": \"event\"\n },\n {\n \"anonymous\": false,\n \"inputs\": [\n {\n \"indexed\": false,\n \"internalType\": \"bytes32\",\n \"name\": \"_invalidationId\",\n \"type\": \"bytes32\"\n },\n {\n \"indexed\": false,\n \"internalType\": \"uint256\",\n \"name\": \"_invalidationNonce\",\n \"type\": \"uint256\"\n },\n {\n \"indexed\": false,\n \"internalType\": \"bytes\",\n \"name\": \"_returnData\",\n \"type\": \"bytes\"\n },\n {\n \"indexed\": false,\n \"internalType\": \"uint256\",\n \"name\": \"_eventNonce\",\n \"type\": \"uint256\"\n }\n ],\n \"name\": \"LogicCallEvent\",\n \"type\": \"event\"\n },\n {\n \"anonymous\": false,\n \"inputs\": [\n {\n \"indexed\": true,\n \"internalType\": \"address\",\n \"name\": \"_tokenContract\",\n \"type\": \"address\"\n },\n {\n \"indexed\": true,\n \"internalType\": \"address\",\n \"name\": \"_sender\",\n \"type\": \"address\"\n },\n {\n \"indexed\": true,\n \"internalType\": \"bytes32\",\n \"name\": \"_destination\",\n \"type\": \"bytes32\"\n },\n {\n \"indexed\": false,\n \"internalType\": \"uint256\",\n \"name\": \"_amount\",\n \"type\": \"uint256\"\n },\n {\n \"indexed\": false,\n \"internalType\": \"uint256\",\n \"name\": \"_eventNonce\",\n \"type\": \"uint256\"\n }\n ],\n \"name\": \"SendToCosmosEvent\",\n \"type\": \"event\"\n },\n {\n \"anonymous\": false,\n \"inputs\": [\n {\n \"indexed\": true,\n \"internalType\": \"uint256\",\n \"name\": \"_batchNonce\",\n \"type\": \"uint256\"\n },\n {\n \"indexed\": true,\n \"internalType\": \"address\",\n \"name\": \"_token\",\n \"type\": \"address\"\n },\n {\n \"indexed\": false,\n \"internalType\": \"uint256\",\n \"name\": \"_eventNonce\",\n \"type\": \"uint256\"\n }\n ],\n \"name\": \"TransactionBatchExecutedEvent\",\n \"type\": \"event\"\n },\n {\n \"anonymous\": false,\n \"inputs\": [\n {\n \"indexed\": true,\n \"internalType\": \"uint256\",\n \"name\": \"_newValsetNonce\",\n \"type\": \"uint256\"\n },\n {\n \"indexed\": false,\n \"internalType\": \"uint256\",\n \"name\": \"_eventNonce\",\n \"type\": \"uint256\"\n },\n {\n \"indexed\": false,\n \"internalType\": \"address[]\",\n \"name\": \"_validators\",\n \"type\": \"address[]\"\n },\n {\n \"indexed\": false,\n \"internalType\": \"uint256[]\",\n \"name\": \"_powers\",\n \"type\": \"uint256[]\"\n }\n ],\n \"name\": \"ValsetUpdatedEvent\",\n \"type\": \"event\"\n },\n {\n \"inputs\": [\n {\n \"internalType\": \"string\",\n \"name\": \"_cosmosDenom\",\n \"type\": \"string\"\n },\n {\n \"internalType\": \"string\",\n \"name\": \"_name\",\n \"type\": \"string\"\n },\n {\n \"internalType\": \"string\",\n \"name\": \"_symbol\",\n \"type\": \"string\"\n },\n {\n \"internalType\": \"uint8\",\n \"name\": \"_decimals\",\n \"type\": \"uint8\"\n }\n ],\n \"name\": \"deployERC20\",\n \"outputs\": [],\n \"stateMutability\": \"nonpayable\",\n \"type\": \"function\"\n },\n {\n \"inputs\": [\n {\n \"internalType\": \"address\",\n \"name\": \"_erc20Address\",\n \"type\": \"address\"\n }\n ],\n \"name\": \"lastBatchNonce\",\n \"outputs\": [\n {\n \"internalType\": \"uint256\",\n \"name\": \"\",\n \"type\": \"uint256\"\n }\n ],\n \"stateMutability\": \"view\",\n \"type\": \"function\"\n },\n {\n \"inputs\": [\n {\n \"internalType\": \"bytes32\",\n \"name\": \"_invalidation_id\",\n \"type\": \"bytes32\"\n }\n ],\n \"name\": \"lastLogicCallNonce\",\n \"outputs\": [\n {\n \"internalType\": \"uint256\",\n \"name\": \"\",\n \"type\": \"uint256\"\n }\n ],\n \"stateMutability\": \"view\",\n \"type\": \"function\"\n },\n {\n \"inputs\": [\n {\n \"internalType\": \"address\",\n \"name\": \"_tokenContract\",\n \"type\": \"address\"\n },\n {\n \"internalType\": \"bytes32\",\n \"name\": \"_destination\",\n \"type\": \"bytes32\"\n },\n {\n \"internalType\": \"uint256\",\n \"name\": \"_amount\",\n \"type\": \"uint256\"\n }\n ],\n \"name\": \"sendToCosmos\",\n \"outputs\": [],\n \"stateMutability\": \"nonpayable\",\n \"type\": \"function\"\n },\n {\n \"inputs\": [],\n \"name\": \"state_gravityId\",\n \"outputs\": [\n {\n \"internalType\": \"bytes32\",\n \"name\": \"\",\n \"type\": \"bytes32\"\n }\n ],\n \"stateMutability\": \"view\",\n \"type\": \"function\"\n },\n {\n \"inputs\": [\n {\n \"internalType\": \"bytes32\",\n \"name\": \"\",\n \"type\": \"bytes32\"\n }\n ],\n \"name\": \"state_invalidationMapping\",\n \"outputs\": [\n {\n \"internalType\": \"uint256\",\n \"name\": \"\",\n \"type\": \"uint256\"\n }\n ],\n \"stateMutability\": \"view\",\n \"type\": \"function\"\n },\n {\n \"inputs\": [\n {\n \"internalType\": \"address\",\n \"name\": \"\",\n \"type\": \"address\"\n }\n ],\n \"name\": \"state_lastBatchNonces\",\n \"outputs\": [\n {\n \"internalType\": \"uint256\",\n \"name\": \"\",\n \"type\": \"uint256\"\n }\n ],\n \"stateMutability\": \"view\",\n \"type\": \"function\"\n },\n {\n \"inputs\": [],\n \"name\": \"state_lastEventNonce\",\n \"outputs\": [\n {\n \"internalType\": \"uint256\",\n \"name\": \"\",\n \"type\": \"uint256\"\n }\n ],\n \"stateMutability\": \"view\",\n \"type\": \"function\"\n },\n {\n \"inputs\": [],\n \"name\": \"state_lastValsetCheckpoint\",\n \"outputs\": [\n {\n \"internalType\": \"bytes32\",\n \"name\": \"\",\n \"type\": \"bytes32\"\n }\n ],\n \"stateMutability\": \"view\",\n \"type\": \"function\"\n },\n {\n \"inputs\": [],\n \"name\": \"state_lastValsetNonce\",\n \"outputs\": [\n {\n \"internalType\": \"uint256\",\n \"name\": \"\",\n \"type\": \"uint256\"\n }\n ],\n \"stateMutability\": \"view\",\n \"type\": \"function\"\n },\n {\n \"inputs\": [],\n \"name\": \"state_powerThreshold\",\n \"outputs\": [\n {\n \"internalType\": \"uint256\",\n \"name\": \"\",\n \"type\": \"uint256\"\n }\n ],\n \"stateMutability\": \"view\",\n \"type\": \"function\"\n },\n {\n \"inputs\": [\n {\n \"internalType\": \"address[]\",\n \"name\": \"_currentValidators\",\n \"type\": \"address[]\"\n },\n {\n \"internalType\": \"uint256[]\",\n \"name\": \"_currentPowers\",\n \"type\": \"uint256[]\"\n },\n {\n \"internalType\": \"uint256\",\n \"name\": \"_currentValsetNonce\",\n \"type\": \"uint256\"\n },\n {\n \"internalType\": \"uint8[]\",\n \"name\": \"_v\",\n \"type\": \"uint8[]\"\n },\n {\n \"internalType\": \"bytes32[]\",\n \"name\": \"_r\",\n \"type\": \"bytes32[]\"\n },\n {\n \"internalType\": \"bytes32[]\",\n \"name\": \"_s\",\n \"type\": \"bytes32[]\"\n },\n {\n \"internalType\": \"uint256[]\",\n \"name\": \"_amounts\",\n \"type\": \"uint256[]\"\n },\n {\n \"internalType\": \"address[]\",\n \"name\": \"_destinations\",\n \"type\": \"address[]\"\n },\n {\n \"internalType\": \"uint256[]\",\n \"name\": \"_fees\",\n \"type\": \"uint256[]\"\n },\n {\n \"internalType\": \"uint256\",\n \"name\": \"_batchNonce\",\n \"type\": \"uint256\"\n },\n {\n \"internalType\": \"address\",\n \"name\": \"_tokenContract\",\n \"type\": \"address\"\n },\n {\n \"internalType\": \"uint256\",\n \"name\": \"_batchTimeout\",\n \"type\": \"uint256\"\n }\n ],\n \"name\": \"submitBatch\",\n \"outputs\": [],\n \"stateMutability\": \"nonpayable\",\n \"type\": \"function\"\n },\n {\n \"inputs\": [\n {\n \"internalType\": \"address[]\",\n \"name\": \"_currentValidators\",\n \"type\": \"address[]\"\n },\n {\n \"internalType\": \"uint256[]\",\n \"name\": \"_currentPowers\",\n \"type\": \"uint256[]\"\n },\n {\n \"internalType\": \"uint256\",\n \"name\": \"_currentValsetNonce\",\n \"type\": \"uint256\"\n },\n {\n \"internalType\": \"uint8[]\",\n \"name\": \"_v\",\n \"type\": \"uint8[]\"\n },\n {\n \"internalType\": \"bytes32[]\",\n \"name\": \"_r\",\n \"type\": \"bytes32[]\"\n },\n {\n \"internalType\": \"bytes32[]\",\n \"name\": \"_s\",\n \"type\": \"bytes32[]\"\n },\n {\n \"components\": [\n {\n \"internalType\": \"uint256[]\",\n \"name\": \"transferAmounts\",\n \"type\": \"uint256[]\"\n },\n {\n \"internalType\": \"address[]\",\n \"name\": \"transferTokenContracts\",\n \"type\": \"address[]\"\n },\n {\n \"internalType\": \"uint256[]\",\n \"name\": \"feeAmounts\",\n \"type\": \"uint256[]\"\n },\n {\n \"internalType\": \"address[]\",\n \"name\": \"feeTokenContracts\",\n \"type\": \"address[]\"\n },\n {\n \"internalType\": \"address\",\n \"name\": \"logicContractAddress\",\n \"type\": \"address\"\n },\n {\n \"internalType\": \"bytes\",\n \"name\": \"payload\",\n \"type\": \"bytes\"\n },\n {\n \"internalType\": \"uint256\",\n \"name\": \"timeOut\",\n \"type\": \"uint256\"\n },\n {\n \"internalType\": \"bytes32\",\n \"name\": \"invalidationId\",\n \"type\": \"bytes32\"\n },\n {\n \"internalType\": \"uint256\",\n \"name\": \"invalidationNonce\",\n \"type\": \"uint256\"\n }\n ],\n \"internalType\": \"struct LogicCallArgs\",\n \"name\": \"_args\",\n \"type\": \"tuple\"\n }\n ],\n \"name\": \"submitLogicCall\",\n \"outputs\": [],\n \"stateMutability\": \"nonpayable\",\n \"type\": \"function\"\n },\n {\n \"inputs\": [\n {\n \"internalType\": \"address[]\",\n \"name\": \"_currentValidators\",\n \"type\": \"address[]\"\n },\n {\n \"internalType\": \"uint256[]\",\n \"name\": \"_currentPowers\",\n \"type\": \"uint256[]\"\n },\n {\n \"internalType\": \"uint8[]\",\n \"name\": \"_v\",\n \"type\": \"uint8[]\"\n },\n {\n \"internalType\": \"bytes32[]\",\n \"name\": \"_r\",\n \"type\": \"bytes32[]\"\n },\n {\n \"internalType\": \"bytes32[]\",\n \"name\": \"_s\",\n \"type\": \"bytes32[]\"\n },\n {\n \"internalType\": \"bytes32\",\n \"name\": \"_theHash\",\n \"type\": \"bytes32\"\n },\n {\n \"internalType\": \"uint256\",\n \"name\": \"_powerThreshold\",\n \"type\": \"uint256\"\n }\n ],\n \"name\": \"testCheckValidatorSignatures\",\n \"outputs\": [],\n \"stateMutability\": \"pure\",\n \"type\": \"function\"\n },\n {\n \"inputs\": [\n {\n \"internalType\": \"address[]\",\n \"name\": \"_validators\",\n \"type\": \"address[]\"\n },\n {\n \"internalType\": \"uint256[]\",\n \"name\": \"_powers\",\n \"type\": \"uint256[]\"\n },\n {\n \"internalType\": \"uint256\",\n \"name\": \"_valsetNonce\",\n \"type\": \"uint256\"\n },\n {\n \"internalType\": \"bytes32\",\n \"name\": \"_gravityId\",\n \"type\": \"bytes32\"\n }\n ],\n \"name\": \"testMakeCheckpoint\",\n \"outputs\": [],\n \"stateMutability\": \"pure\",\n \"type\": \"function\"\n },\n {\n \"inputs\": [\n {\n \"internalType\": \"address[]\",\n \"name\": \"_newValidators\",\n \"type\": \"address[]\"\n },\n {\n \"internalType\": \"uint256[]\",\n \"name\": \"_newPowers\",\n \"type\": \"uint256[]\"\n },\n {\n \"internalType\": \"uint256\",\n \"name\": \"_newValsetNonce\",\n \"type\": \"uint256\"\n },\n {\n \"internalType\": \"address[]\",\n \"name\": \"_currentValidators\",\n \"type\": \"address[]\"\n },\n {\n \"internalType\": \"uint256[]\",\n \"name\": \"_currentPowers\",\n \"type\": \"uint256[]\"\n },\n {\n \"internalType\": \"uint256\",\n \"name\": \"_currentValsetNonce\",\n \"type\": \"uint256\"\n },\n {\n \"internalType\": \"uint8[]\",\n \"name\": \"_v\",\n \"type\": \"uint8[]\"\n },\n {\n \"internalType\": \"bytes32[]\",\n \"name\": \"_r\",\n \"type\": \"bytes32[]\"\n },\n {\n \"internalType\": \"bytes32[]\",\n \"name\": \"_s\",\n \"type\": \"bytes32[]\"\n }\n ],\n \"name\": \"updateValset\",\n \"outputs\": [],\n \"stateMutability\": \"nonpayable\",\n \"type\": \"function\"\n }\n]\n") . expect ("invalid abi") + }); + #[derive(Clone)] + pub struct Gravity(ethers::contract::Contract); + impl std::ops::Deref for Gravity { + type Target = ethers::contract::Contract; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl std::fmt::Debug for Gravity { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.debug_tuple(stringify!(Gravity)) + .field(&self.address()) + .finish() + } + } + impl<'a, M: ethers::providers::Middleware> Gravity { + #[doc = r" Creates a new contract instance with the specified `ethers`"] + #[doc = r" client at the given `Address`. The contract derefs to a `ethers::Contract`"] + #[doc = r" object"] + pub fn new>( + address: T, + client: ::std::sync::Arc, + ) -> Self { + let contract = + ethers::contract::Contract::new(address.into(), GRAVITY_ABI.clone(), client); + Self(contract) + } + #[doc = "Calls the contract's `deployERC20` (0xf7955637) function"] + pub fn deploy_erc20( + &self, + cosmos_denom: String, + name: String, + symbol: String, + decimals: u8, + ) -> ethers::contract::builders::ContractCall { + self.0 + .method_hash([247, 149, 86, 55], (cosmos_denom, name, symbol, decimals)) + .expect("method not found (this should never happen)") + } + #[doc = "Calls the contract's `lastBatchNonce` (0x011b2174) function"] + pub fn last_batch_nonce( + &self, + erc_20_address: ethers::core::types::Address, + ) -> ethers::contract::builders::ContractCall { + self.0 + .method_hash([1, 27, 33, 116], erc_20_address) + .expect("method not found (this should never happen)") + } + #[doc = "Calls the contract's `lastLogicCallNonce` (0xc9d194d5) function"] + pub fn last_logic_call_nonce( + &self, + invalidation_id: [u8; 32], + ) -> ethers::contract::builders::ContractCall { + self.0 + .method_hash([201, 209, 148, 213], invalidation_id) + .expect("method not found (this should never happen)") + } + #[doc = "Calls the contract's `sendToCosmos` (0x1ffbe7f9) function"] + pub fn send_to_cosmos( + &self, + token_contract: ethers::core::types::Address, + destination: [u8; 32], + amount: ethers::core::types::U256, + ) -> ethers::contract::builders::ContractCall { + self.0 + .method_hash([31, 251, 231, 249], (token_contract, destination, amount)) + .expect("method not found (this should never happen)") + } + #[doc = "Calls the contract's `state_gravityId` (0xbdda81d4) function"] + pub fn state_gravity_id(&self) -> ethers::contract::builders::ContractCall { + self.0 + .method_hash([189, 218, 129, 212], ()) + .expect("method not found (this should never happen)") + } + #[doc = "Calls the contract's `state_invalidationMapping` (0x7dfb6f86) function"] + pub fn state_invalidation_mapping( + &self, + p0: [u8; 32], + ) -> ethers::contract::builders::ContractCall { + self.0 + .method_hash([125, 251, 111, 134], p0) + .expect("method not found (this should never happen)") + } + #[doc = "Calls the contract's `state_lastBatchNonces` (0xdf97174b) function"] + pub fn state_last_batch_nonces( + &self, + p0: ethers::core::types::Address, + ) -> ethers::contract::builders::ContractCall { + self.0 + .method_hash([223, 151, 23, 75], p0) + .expect("method not found (this should never happen)") + } + #[doc = "Calls the contract's `state_lastEventNonce` (0x73b20547) function"] + pub fn state_last_event_nonce( + &self, + ) -> ethers::contract::builders::ContractCall { + self.0 + .method_hash([115, 178, 5, 71], ()) + .expect("method not found (this should never happen)") + } + #[doc = "Calls the contract's `state_lastValsetCheckpoint` (0xf2b53307) function"] + pub fn state_last_valset_checkpoint( + &self, + ) -> ethers::contract::builders::ContractCall { + self.0 + .method_hash([242, 181, 51, 7], ()) + .expect("method not found (this should never happen)") + } + #[doc = "Calls the contract's `state_lastValsetNonce` (0xb56561fe) function"] + pub fn state_last_valset_nonce( + &self, + ) -> ethers::contract::builders::ContractCall { + self.0 + .method_hash([181, 101, 97, 254], ()) + .expect("method not found (this should never happen)") + } + #[doc = "Calls the contract's `state_powerThreshold` (0xe5a2b5d2) function"] + pub fn state_power_threshold( + &self, + ) -> ethers::contract::builders::ContractCall { + self.0 + .method_hash([229, 162, 181, 210], ()) + .expect("method not found (this should never happen)") + } + #[doc = "Calls the contract's `submitBatch` (0x83b435db) function"] + pub fn submit_batch( + &self, + current_validators: ::std::vec::Vec, + current_powers: ::std::vec::Vec, + current_valset_nonce: ethers::core::types::U256, + v: ::std::vec::Vec, + r: ::std::vec::Vec<[u8; 32]>, + s: ::std::vec::Vec<[u8; 32]>, + amounts: ::std::vec::Vec, + destinations: ::std::vec::Vec, + fees: ::std::vec::Vec, + batch_nonce: ethers::core::types::U256, + token_contract: ethers::core::types::Address, + batch_timeout: ethers::core::types::U256, + ) -> ethers::contract::builders::ContractCall { + self.0 + .method_hash( + [131, 180, 53, 219], + ( + current_validators, + current_powers, + current_valset_nonce, + v, + r, + s, + amounts, + destinations, + fees, + batch_nonce, + token_contract, + batch_timeout, + ), + ) + .expect("method not found (this should never happen)") + } + #[doc = "Calls the contract's `submitLogicCall` (0x0c246c82) function"] + pub fn submit_logic_call( + &self, + current_validators: ::std::vec::Vec, + current_powers: ::std::vec::Vec, + current_valset_nonce: ethers::core::types::U256, + v: ::std::vec::Vec, + r: ::std::vec::Vec<[u8; 32]>, + s: ::std::vec::Vec<[u8; 32]>, + args: LogicCallArgs, + ) -> ethers::contract::builders::ContractCall { + self.0 + .method_hash( + [12, 36, 108, 130], + ( + current_validators, + current_powers, + current_valset_nonce, + v, + r, + s, + args, + ), + ) + .expect("method not found (this should never happen)") + } + #[doc = "Calls the contract's `testCheckValidatorSignatures` (0xdb7c4e57) function"] + pub fn test_check_validator_signatures( + &self, + current_validators: ::std::vec::Vec, + current_powers: ::std::vec::Vec, + v: ::std::vec::Vec, + r: ::std::vec::Vec<[u8; 32]>, + s: ::std::vec::Vec<[u8; 32]>, + the_hash: [u8; 32], + power_threshold: ethers::core::types::U256, + ) -> ethers::contract::builders::ContractCall { + self.0 + .method_hash( + [219, 124, 78, 87], + ( + current_validators, + current_powers, + v, + r, + s, + the_hash, + power_threshold, + ), + ) + .expect("method not found (this should never happen)") + } + #[doc = "Calls the contract's `testMakeCheckpoint` (0xc227c30b) function"] + pub fn test_make_checkpoint( + &self, + validators: ::std::vec::Vec, + powers: ::std::vec::Vec, + valset_nonce: ethers::core::types::U256, + gravity_id: [u8; 32], + ) -> ethers::contract::builders::ContractCall { + self.0 + .method_hash( + [194, 39, 195, 11], + (validators, powers, valset_nonce, gravity_id), + ) + .expect("method not found (this should never happen)") + } + #[doc = "Calls the contract's `updateValset` (0xe3cb9f62) function"] + pub fn update_valset( + &self, + new_validators: ::std::vec::Vec, + new_powers: ::std::vec::Vec, + new_valset_nonce: ethers::core::types::U256, + current_validators: ::std::vec::Vec, + current_powers: ::std::vec::Vec, + current_valset_nonce: ethers::core::types::U256, + v: ::std::vec::Vec, + r: ::std::vec::Vec<[u8; 32]>, + s: ::std::vec::Vec<[u8; 32]>, + ) -> ethers::contract::builders::ContractCall { + self.0 + .method_hash( + [227, 203, 159, 98], + ( + new_validators, + new_powers, + new_valset_nonce, + current_validators, + current_powers, + current_valset_nonce, + v, + r, + s, + ), + ) + .expect("method not found (this should never happen)") + } + #[doc = "Gets the contract's `ERC20DeployedEvent` event"] + pub fn erc20_deployed_event_filter( + &self, + ) -> ethers::contract::builders::Event { + self.0.event() + } + #[doc = "Gets the contract's `LogicCallEvent` event"] + pub fn logic_call_event_filter( + &self, + ) -> ethers::contract::builders::Event { + self.0.event() + } + #[doc = "Gets the contract's `SendToCosmosEvent` event"] + pub fn send_to_cosmos_event_filter( + &self, + ) -> ethers::contract::builders::Event { + self.0.event() + } + #[doc = "Gets the contract's `TransactionBatchExecutedEvent` event"] + pub fn transaction_batch_executed_event_filter( + &self, + ) -> ethers::contract::builders::Event { + self.0.event() + } + #[doc = "Gets the contract's `ValsetUpdatedEvent` event"] + pub fn valset_updated_event_filter( + &self, + ) -> ethers::contract::builders::Event { + self.0.event() + } + #[doc = r" Returns an [`Event`](#ethers_contract::builders::Event) builder for all events of this contract"] + pub fn events(&self) -> ethers::contract::builders::Event { + self.0.event_with_filter(Default::default()) + } + } + #[derive( + Clone, + Debug, + Default, + Eq, + PartialEq, + ethers :: contract :: EthEvent, + ethers :: contract :: EthDisplay, + serde :: Deserialize, + serde :: Serialize, + )] + #[ethevent( + name = "ERC20DeployedEvent", + abi = "ERC20DeployedEvent(string,address,string,string,uint8,uint256)" + )] + pub struct Erc20DeployedEventFilter { + pub cosmos_denom: String, + #[ethevent(indexed)] + pub token_contract: ethers::core::types::Address, + pub name: String, + pub symbol: String, + pub decimals: u8, + pub event_nonce: ethers::core::types::U256, + } + #[derive( + Clone, + Debug, + Default, + Eq, + PartialEq, + ethers :: contract :: EthEvent, + ethers :: contract :: EthDisplay, + serde :: Deserialize, + serde :: Serialize, + )] + #[ethevent( + name = "LogicCallEvent", + abi = "LogicCallEvent(bytes32,uint256,bytes,uint256)" + )] + pub struct LogicCallEventFilter { + pub invalidation_id: [u8; 32], + pub invalidation_nonce: ethers::core::types::U256, + pub return_data: ethers::core::types::Bytes, + pub event_nonce: ethers::core::types::U256, + } + #[derive( + Clone, + Debug, + Default, + Eq, + PartialEq, + ethers :: contract :: EthEvent, + ethers :: contract :: EthDisplay, + serde :: Deserialize, + serde :: Serialize, + )] + #[ethevent( + name = "SendToCosmosEvent", + abi = "SendToCosmosEvent(address,address,bytes32,uint256,uint256)" + )] + pub struct SendToCosmosEventFilter { + #[ethevent(indexed)] + pub token_contract: ethers::core::types::Address, + #[ethevent(indexed)] + pub sender: ethers::core::types::Address, + #[ethevent(indexed)] + pub destination: [u8; 32], + pub amount: ethers::core::types::U256, + pub event_nonce: ethers::core::types::U256, + } + #[derive( + Clone, + Debug, + Default, + Eq, + PartialEq, + ethers :: contract :: EthEvent, + ethers :: contract :: EthDisplay, + serde :: Deserialize, + serde :: Serialize, + )] + #[ethevent( + name = "TransactionBatchExecutedEvent", + abi = "TransactionBatchExecutedEvent(uint256,address,uint256)" + )] + pub struct TransactionBatchExecutedEventFilter { + #[ethevent(indexed)] + pub batch_nonce: ethers::core::types::U256, + #[ethevent(indexed)] + pub token: ethers::core::types::Address, + pub event_nonce: ethers::core::types::U256, + } + #[derive( + Clone, + Debug, + Default, + Eq, + PartialEq, + ethers :: contract :: EthEvent, + ethers :: contract :: EthDisplay, + serde :: Deserialize, + serde :: Serialize, + )] + #[ethevent( + name = "ValsetUpdatedEvent", + abi = "ValsetUpdatedEvent(uint256,uint256,address[],uint256[])" + )] + pub struct ValsetUpdatedEventFilter { + #[ethevent(indexed)] + pub new_valset_nonce: ethers::core::types::U256, + pub event_nonce: ethers::core::types::U256, + pub validators: Vec, + pub powers: Vec, + } + #[derive(Debug, Clone, PartialEq, Eq, ethers :: contract :: EthAbiType)] + pub enum GravityEvents { + Erc20DeployedEventFilter(Erc20DeployedEventFilter), + LogicCallEventFilter(LogicCallEventFilter), + SendToCosmosEventFilter(SendToCosmosEventFilter), + TransactionBatchExecutedEventFilter(TransactionBatchExecutedEventFilter), + ValsetUpdatedEventFilter(ValsetUpdatedEventFilter), + } + impl ethers::contract::EthLogDecode for GravityEvents { + fn decode_log(log: ðers::core::abi::RawLog) -> Result + where + Self: Sized, + { + if let Ok(decoded) = Erc20DeployedEventFilter::decode_log(log) { + return Ok(GravityEvents::Erc20DeployedEventFilter(decoded)); + } + if let Ok(decoded) = LogicCallEventFilter::decode_log(log) { + return Ok(GravityEvents::LogicCallEventFilter(decoded)); + } + if let Ok(decoded) = SendToCosmosEventFilter::decode_log(log) { + return Ok(GravityEvents::SendToCosmosEventFilter(decoded)); + } + if let Ok(decoded) = TransactionBatchExecutedEventFilter::decode_log(log) { + return Ok(GravityEvents::TransactionBatchExecutedEventFilter(decoded)); + } + if let Ok(decoded) = ValsetUpdatedEventFilter::decode_log(log) { + return Ok(GravityEvents::ValsetUpdatedEventFilter(decoded)); + } + Err(ethers::core::abi::Error::InvalidData) + } + } + impl ::std::fmt::Display for GravityEvents { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match self { + GravityEvents::Erc20DeployedEventFilter(element) => element.fmt(f), + GravityEvents::LogicCallEventFilter(element) => element.fmt(f), + GravityEvents::SendToCosmosEventFilter(element) => element.fmt(f), + GravityEvents::TransactionBatchExecutedEventFilter(element) => element.fmt(f), + GravityEvents::ValsetUpdatedEventFilter(element) => element.fmt(f), + } + } + } + #[doc = "Container type for all input parameters for the `deployERC20`function with signature `deployERC20(string,string,string,uint8)` and selector `[247, 149, 86, 55]`"] + #[derive( + Clone, + Debug, + Default, + Eq, + PartialEq, + ethers :: contract :: EthCall, + ethers :: contract :: EthDisplay, + serde :: Deserialize, + serde :: Serialize, + )] + #[ethcall(name = "deployERC20", abi = "deployERC20(string,string,string,uint8)")] + pub struct DeployERC20Call { + pub cosmos_denom: String, + pub name: String, + pub symbol: String, + pub decimals: u8, + } + #[doc = "Container type for all input parameters for the `lastBatchNonce`function with signature `lastBatchNonce(address)` and selector `[1, 27, 33, 116]`"] + #[derive( + Clone, + Debug, + Default, + Eq, + PartialEq, + ethers :: contract :: EthCall, + ethers :: contract :: EthDisplay, + serde :: Deserialize, + serde :: Serialize, + )] + #[ethcall(name = "lastBatchNonce", abi = "lastBatchNonce(address)")] + pub struct LastBatchNonceCall { + pub erc_20_address: ethers::core::types::Address, + } + #[doc = "Container type for all input parameters for the `lastLogicCallNonce`function with signature `lastLogicCallNonce(bytes32)` and selector `[201, 209, 148, 213]`"] + #[derive( + Clone, + Debug, + Default, + Eq, + PartialEq, + ethers :: contract :: EthCall, + ethers :: contract :: EthDisplay, + serde :: Deserialize, + serde :: Serialize, + )] + #[ethcall(name = "lastLogicCallNonce", abi = "lastLogicCallNonce(bytes32)")] + pub struct LastLogicCallNonceCall { + pub invalidation_id: [u8; 32], + } + #[doc = "Container type for all input parameters for the `sendToCosmos`function with signature `sendToCosmos(address,bytes32,uint256)` and selector `[31, 251, 231, 249]`"] + #[derive( + Clone, + Debug, + Default, + Eq, + PartialEq, + ethers :: contract :: EthCall, + ethers :: contract :: EthDisplay, + serde :: Deserialize, + serde :: Serialize, + )] + #[ethcall(name = "sendToCosmos", abi = "sendToCosmos(address,bytes32,uint256)")] + pub struct SendToCosmosCall { + pub token_contract: ethers::core::types::Address, + pub destination: [u8; 32], + pub amount: ethers::core::types::U256, + } + #[doc = "Container type for all input parameters for the `state_gravityId`function with signature `state_gravityId()` and selector `[189, 218, 129, 212]`"] + #[derive( + Clone, + Debug, + Default, + Eq, + PartialEq, + ethers :: contract :: EthCall, + ethers :: contract :: EthDisplay, + serde :: Deserialize, + serde :: Serialize, + )] + #[ethcall(name = "state_gravityId", abi = "state_gravityId()")] + pub struct StateGravityIdCall; + #[doc = "Container type for all input parameters for the `state_invalidationMapping`function with signature `state_invalidationMapping(bytes32)` and selector `[125, 251, 111, 134]`"] + #[derive( + Clone, + Debug, + Default, + Eq, + PartialEq, + ethers :: contract :: EthCall, + ethers :: contract :: EthDisplay, + serde :: Deserialize, + serde :: Serialize, + )] + #[ethcall( + name = "state_invalidationMapping", + abi = "state_invalidationMapping(bytes32)" + )] + pub struct StateInvalidationMappingCall(pub [u8; 32]); + #[doc = "Container type for all input parameters for the `state_lastBatchNonces`function with signature `state_lastBatchNonces(address)` and selector `[223, 151, 23, 75]`"] + #[derive( + Clone, + Debug, + Default, + Eq, + PartialEq, + ethers :: contract :: EthCall, + ethers :: contract :: EthDisplay, + serde :: Deserialize, + serde :: Serialize, + )] + #[ethcall(name = "state_lastBatchNonces", abi = "state_lastBatchNonces(address)")] + pub struct StateLastBatchNoncesCall(pub ethers::core::types::Address); + #[doc = "Container type for all input parameters for the `state_lastEventNonce`function with signature `state_lastEventNonce()` and selector `[115, 178, 5, 71]`"] + #[derive( + Clone, + Debug, + Default, + Eq, + PartialEq, + ethers :: contract :: EthCall, + ethers :: contract :: EthDisplay, + serde :: Deserialize, + serde :: Serialize, + )] + #[ethcall(name = "state_lastEventNonce", abi = "state_lastEventNonce()")] + pub struct StateLastEventNonceCall; + #[doc = "Container type for all input parameters for the `state_lastValsetCheckpoint`function with signature `state_lastValsetCheckpoint()` and selector `[242, 181, 51, 7]`"] + #[derive( + Clone, + Debug, + Default, + Eq, + PartialEq, + ethers :: contract :: EthCall, + ethers :: contract :: EthDisplay, + serde :: Deserialize, + serde :: Serialize, + )] + #[ethcall( + name = "state_lastValsetCheckpoint", + abi = "state_lastValsetCheckpoint()" + )] + pub struct StateLastValsetCheckpointCall; + #[doc = "Container type for all input parameters for the `state_lastValsetNonce`function with signature `state_lastValsetNonce()` and selector `[181, 101, 97, 254]`"] + #[derive( + Clone, + Debug, + Default, + Eq, + PartialEq, + ethers :: contract :: EthCall, + ethers :: contract :: EthDisplay, + serde :: Deserialize, + serde :: Serialize, + )] + #[ethcall(name = "state_lastValsetNonce", abi = "state_lastValsetNonce()")] + pub struct StateLastValsetNonceCall; + #[doc = "Container type for all input parameters for the `state_powerThreshold`function with signature `state_powerThreshold()` and selector `[229, 162, 181, 210]`"] + #[derive( + Clone, + Debug, + Default, + Eq, + PartialEq, + ethers :: contract :: EthCall, + ethers :: contract :: EthDisplay, + serde :: Deserialize, + serde :: Serialize, + )] + #[ethcall(name = "state_powerThreshold", abi = "state_powerThreshold()")] + pub struct StatePowerThresholdCall; + #[doc = "Container type for all input parameters for the `submitBatch`function with signature `submitBatch(address[],uint256[],uint256,uint8[],bytes32[],bytes32[],uint256[],address[],uint256[],uint256,address,uint256)` and selector `[131, 180, 53, 219]`"] + #[derive( + Clone, + Debug, + Default, + Eq, + PartialEq, + ethers :: contract :: EthCall, + ethers :: contract :: EthDisplay, + serde :: Deserialize, + serde :: Serialize, + )] + #[ethcall( + name = "submitBatch", + abi = "submitBatch(address[],uint256[],uint256,uint8[],bytes32[],bytes32[],uint256[],address[],uint256[],uint256,address,uint256)" + )] + pub struct SubmitBatchCall { + pub current_validators: ::std::vec::Vec, + pub current_powers: ::std::vec::Vec, + pub current_valset_nonce: ethers::core::types::U256, + pub v: ::std::vec::Vec, + pub r: ::std::vec::Vec<[u8; 32]>, + pub s: ::std::vec::Vec<[u8; 32]>, + pub amounts: ::std::vec::Vec, + pub destinations: ::std::vec::Vec, + pub fees: ::std::vec::Vec, + pub batch_nonce: ethers::core::types::U256, + pub token_contract: ethers::core::types::Address, + pub batch_timeout: ethers::core::types::U256, + } + #[doc = "Container type for all input parameters for the `submitLogicCall`function with signature `submitLogicCall(address[],uint256[],uint256,uint8[],bytes32[],bytes32[],(uint256[],address[],uint256[],address[],address,bytes,uint256,bytes32,uint256))` and selector `[12, 36, 108, 130]`"] + #[derive( + Clone, + Debug, + Default, + Eq, + PartialEq, + ethers :: contract :: EthCall, + ethers :: contract :: EthDisplay, + serde :: Deserialize, + serde :: Serialize, + )] + #[ethcall( + name = "submitLogicCall", + abi = "submitLogicCall(address[],uint256[],uint256,uint8[],bytes32[],bytes32[],(uint256[],address[],uint256[],address[],address,bytes,uint256,bytes32,uint256))" + )] + pub struct SubmitLogicCallCall { + pub current_validators: ::std::vec::Vec, + pub current_powers: ::std::vec::Vec, + pub current_valset_nonce: ethers::core::types::U256, + pub v: ::std::vec::Vec, + pub r: ::std::vec::Vec<[u8; 32]>, + pub s: ::std::vec::Vec<[u8; 32]>, + pub args: LogicCallArgs, + } + #[doc = "Container type for all input parameters for the `testCheckValidatorSignatures`function with signature `testCheckValidatorSignatures(address[],uint256[],uint8[],bytes32[],bytes32[],bytes32,uint256)` and selector `[219, 124, 78, 87]`"] + #[derive( + Clone, + Debug, + Default, + Eq, + PartialEq, + ethers :: contract :: EthCall, + ethers :: contract :: EthDisplay, + serde :: Deserialize, + serde :: Serialize, + )] + #[ethcall( + name = "testCheckValidatorSignatures", + abi = "testCheckValidatorSignatures(address[],uint256[],uint8[],bytes32[],bytes32[],bytes32,uint256)" + )] + pub struct TestCheckValidatorSignaturesCall { + pub current_validators: ::std::vec::Vec, + pub current_powers: ::std::vec::Vec, + pub v: ::std::vec::Vec, + pub r: ::std::vec::Vec<[u8; 32]>, + pub s: ::std::vec::Vec<[u8; 32]>, + pub the_hash: [u8; 32], + pub power_threshold: ethers::core::types::U256, + } + #[doc = "Container type for all input parameters for the `testMakeCheckpoint`function with signature `testMakeCheckpoint(address[],uint256[],uint256,bytes32)` and selector `[194, 39, 195, 11]`"] + #[derive( + Clone, + Debug, + Default, + Eq, + PartialEq, + ethers :: contract :: EthCall, + ethers :: contract :: EthDisplay, + serde :: Deserialize, + serde :: Serialize, + )] + #[ethcall( + name = "testMakeCheckpoint", + abi = "testMakeCheckpoint(address[],uint256[],uint256,bytes32)" + )] + pub struct TestMakeCheckpointCall { + pub validators: ::std::vec::Vec, + pub powers: ::std::vec::Vec, + pub valset_nonce: ethers::core::types::U256, + pub gravity_id: [u8; 32], + } + #[doc = "Container type for all input parameters for the `updateValset`function with signature `updateValset(address[],uint256[],uint256,address[],uint256[],uint256,uint8[],bytes32[],bytes32[])` and selector `[227, 203, 159, 98]`"] + #[derive( + Clone, + Debug, + Default, + Eq, + PartialEq, + ethers :: contract :: EthCall, + ethers :: contract :: EthDisplay, + serde :: Deserialize, + serde :: Serialize, + )] + #[ethcall( + name = "updateValset", + abi = "updateValset(address[],uint256[],uint256,address[],uint256[],uint256,uint8[],bytes32[],bytes32[])" + )] + pub struct UpdateValsetCall { + pub new_validators: ::std::vec::Vec, + pub new_powers: ::std::vec::Vec, + pub new_valset_nonce: ethers::core::types::U256, + pub current_validators: ::std::vec::Vec, + pub current_powers: ::std::vec::Vec, + pub current_valset_nonce: ethers::core::types::U256, + pub v: ::std::vec::Vec, + pub r: ::std::vec::Vec<[u8; 32]>, + pub s: ::std::vec::Vec<[u8; 32]>, + } + #[derive(Debug, Clone, PartialEq, Eq, ethers :: contract :: EthAbiType)] + pub enum GravityCalls { + DeployERC20(DeployERC20Call), + LastBatchNonce(LastBatchNonceCall), + LastLogicCallNonce(LastLogicCallNonceCall), + SendToCosmos(SendToCosmosCall), + StateGravityId(StateGravityIdCall), + StateInvalidationMapping(StateInvalidationMappingCall), + StateLastBatchNonces(StateLastBatchNoncesCall), + StateLastEventNonce(StateLastEventNonceCall), + StateLastValsetCheckpoint(StateLastValsetCheckpointCall), + StateLastValsetNonce(StateLastValsetNonceCall), + StatePowerThreshold(StatePowerThresholdCall), + SubmitBatch(SubmitBatchCall), + SubmitLogicCall(SubmitLogicCallCall), + TestCheckValidatorSignatures(TestCheckValidatorSignaturesCall), + TestMakeCheckpoint(TestMakeCheckpointCall), + UpdateValset(UpdateValsetCall), + } + impl ethers::core::abi::AbiDecode for GravityCalls { + fn decode(data: impl AsRef<[u8]>) -> Result { + if let Ok(decoded) = + ::decode(data.as_ref()) + { + return Ok(GravityCalls::DeployERC20(decoded)); + } + if let Ok(decoded) = + ::decode(data.as_ref()) + { + return Ok(GravityCalls::LastBatchNonce(decoded)); + } + if let Ok(decoded) = + ::decode(data.as_ref()) + { + return Ok(GravityCalls::LastLogicCallNonce(decoded)); + } + if let Ok(decoded) = + ::decode(data.as_ref()) + { + return Ok(GravityCalls::SendToCosmos(decoded)); + } + if let Ok(decoded) = + ::decode(data.as_ref()) + { + return Ok(GravityCalls::StateGravityId(decoded)); + } + if let Ok(decoded) = + ::decode( + data.as_ref(), + ) + { + return Ok(GravityCalls::StateInvalidationMapping(decoded)); + } + if let Ok(decoded) = + ::decode(data.as_ref()) + { + return Ok(GravityCalls::StateLastBatchNonces(decoded)); + } + if let Ok(decoded) = + ::decode(data.as_ref()) + { + return Ok(GravityCalls::StateLastEventNonce(decoded)); + } + if let Ok(decoded) = + ::decode( + data.as_ref(), + ) + { + return Ok(GravityCalls::StateLastValsetCheckpoint(decoded)); + } + if let Ok(decoded) = + ::decode(data.as_ref()) + { + return Ok(GravityCalls::StateLastValsetNonce(decoded)); + } + if let Ok(decoded) = + ::decode(data.as_ref()) + { + return Ok(GravityCalls::StatePowerThreshold(decoded)); + } + if let Ok(decoded) = + ::decode(data.as_ref()) + { + return Ok(GravityCalls::SubmitBatch(decoded)); + } + if let Ok(decoded) = + ::decode(data.as_ref()) + { + return Ok(GravityCalls::SubmitLogicCall(decoded)); + } + if let Ok(decoded) = + ::decode( + data.as_ref(), + ) + { + return Ok(GravityCalls::TestCheckValidatorSignatures(decoded)); + } + if let Ok(decoded) = + ::decode(data.as_ref()) + { + return Ok(GravityCalls::TestMakeCheckpoint(decoded)); + } + if let Ok(decoded) = + ::decode(data.as_ref()) + { + return Ok(GravityCalls::UpdateValset(decoded)); + } + Err(ethers::core::abi::Error::InvalidData.into()) + } + } + impl ethers::core::abi::AbiEncode for GravityCalls { + fn encode(self) -> Vec { + match self { + GravityCalls::DeployERC20(element) => element.encode(), + GravityCalls::LastBatchNonce(element) => element.encode(), + GravityCalls::LastLogicCallNonce(element) => element.encode(), + GravityCalls::SendToCosmos(element) => element.encode(), + GravityCalls::StateGravityId(element) => element.encode(), + GravityCalls::StateInvalidationMapping(element) => element.encode(), + GravityCalls::StateLastBatchNonces(element) => element.encode(), + GravityCalls::StateLastEventNonce(element) => element.encode(), + GravityCalls::StateLastValsetCheckpoint(element) => element.encode(), + GravityCalls::StateLastValsetNonce(element) => element.encode(), + GravityCalls::StatePowerThreshold(element) => element.encode(), + GravityCalls::SubmitBatch(element) => element.encode(), + GravityCalls::SubmitLogicCall(element) => element.encode(), + GravityCalls::TestCheckValidatorSignatures(element) => element.encode(), + GravityCalls::TestMakeCheckpoint(element) => element.encode(), + GravityCalls::UpdateValset(element) => element.encode(), + } + } + } + impl ::std::fmt::Display for GravityCalls { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match self { + GravityCalls::DeployERC20(element) => element.fmt(f), + GravityCalls::LastBatchNonce(element) => element.fmt(f), + GravityCalls::LastLogicCallNonce(element) => element.fmt(f), + GravityCalls::SendToCosmos(element) => element.fmt(f), + GravityCalls::StateGravityId(element) => element.fmt(f), + GravityCalls::StateInvalidationMapping(element) => element.fmt(f), + GravityCalls::StateLastBatchNonces(element) => element.fmt(f), + GravityCalls::StateLastEventNonce(element) => element.fmt(f), + GravityCalls::StateLastValsetCheckpoint(element) => element.fmt(f), + GravityCalls::StateLastValsetNonce(element) => element.fmt(f), + GravityCalls::StatePowerThreshold(element) => element.fmt(f), + GravityCalls::SubmitBatch(element) => element.fmt(f), + GravityCalls::SubmitLogicCall(element) => element.fmt(f), + GravityCalls::TestCheckValidatorSignatures(element) => element.fmt(f), + GravityCalls::TestMakeCheckpoint(element) => element.fmt(f), + GravityCalls::UpdateValset(element) => element.fmt(f), + } + } + } + impl ::std::convert::From for GravityCalls { + fn from(var: DeployERC20Call) -> Self { + GravityCalls::DeployERC20(var) + } + } + impl ::std::convert::From for GravityCalls { + fn from(var: LastBatchNonceCall) -> Self { + GravityCalls::LastBatchNonce(var) + } + } + impl ::std::convert::From for GravityCalls { + fn from(var: LastLogicCallNonceCall) -> Self { + GravityCalls::LastLogicCallNonce(var) + } + } + impl ::std::convert::From for GravityCalls { + fn from(var: SendToCosmosCall) -> Self { + GravityCalls::SendToCosmos(var) + } + } + impl ::std::convert::From for GravityCalls { + fn from(var: StateGravityIdCall) -> Self { + GravityCalls::StateGravityId(var) + } + } + impl ::std::convert::From for GravityCalls { + fn from(var: StateInvalidationMappingCall) -> Self { + GravityCalls::StateInvalidationMapping(var) + } + } + impl ::std::convert::From for GravityCalls { + fn from(var: StateLastBatchNoncesCall) -> Self { + GravityCalls::StateLastBatchNonces(var) + } + } + impl ::std::convert::From for GravityCalls { + fn from(var: StateLastEventNonceCall) -> Self { + GravityCalls::StateLastEventNonce(var) + } + } + impl ::std::convert::From for GravityCalls { + fn from(var: StateLastValsetCheckpointCall) -> Self { + GravityCalls::StateLastValsetCheckpoint(var) + } + } + impl ::std::convert::From for GravityCalls { + fn from(var: StateLastValsetNonceCall) -> Self { + GravityCalls::StateLastValsetNonce(var) + } + } + impl ::std::convert::From for GravityCalls { + fn from(var: StatePowerThresholdCall) -> Self { + GravityCalls::StatePowerThreshold(var) + } + } + impl ::std::convert::From for GravityCalls { + fn from(var: SubmitBatchCall) -> Self { + GravityCalls::SubmitBatch(var) + } + } + impl ::std::convert::From for GravityCalls { + fn from(var: SubmitLogicCallCall) -> Self { + GravityCalls::SubmitLogicCall(var) + } + } + impl ::std::convert::From for GravityCalls { + fn from(var: TestCheckValidatorSignaturesCall) -> Self { + GravityCalls::TestCheckValidatorSignatures(var) + } + } + impl ::std::convert::From for GravityCalls { + fn from(var: TestMakeCheckpointCall) -> Self { + GravityCalls::TestMakeCheckpoint(var) + } + } + impl ::std::convert::From for GravityCalls { + fn from(var: UpdateValsetCall) -> Self { + GravityCalls::UpdateValset(var) + } + } + #[doc = "`LogicCallArgs(uint256[],address[],uint256[],address[],address,bytes,uint256,bytes32,uint256)`"] + #[derive( + Clone, + Debug, + Default, + Eq, + PartialEq, + ethers :: contract :: EthAbiType, + serde :: Deserialize, + serde :: Serialize, + )] + pub struct LogicCallArgs { + pub transfer_amounts: Vec, + pub transfer_token_contracts: Vec, + pub fee_amounts: Vec, + pub fee_token_contracts: Vec, + pub logic_contract_address: ethers::core::types::Address, + pub payload: ethers::core::types::Bytes, + pub time_out: ethers::core::types::U256, + pub invalidation_id: [u8; 32], + pub invalidation_nonce: ethers::core::types::U256, + } +} diff --git a/orchestrator/gravity_abi/src/lib.rs b/orchestrator/gravity_abi/src/lib.rs new file mode 100644 index 000000000..dce4f7034 --- /dev/null +++ b/orchestrator/gravity_abi/src/lib.rs @@ -0,0 +1,2 @@ +pub mod erc20; +pub mod gravity; diff --git a/orchestrator/gravity_utils/Cargo.toml b/orchestrator/gravity_utils/Cargo.toml index 37bcf6f0a..98f10a420 100644 --- a/orchestrator/gravity_utils/Cargo.toml +++ b/orchestrator/gravity_utils/Cargo.toml @@ -7,13 +7,16 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -gravity_proto = {path = "../gravity_proto/"} +gravity_abi = { path = "../gravity_abi" } +gravity_proto = { path = "../gravity_proto/" } cosmos-sdk-proto = "0.6.3" -deep_space ={git="https://github.com/iqlusioninc/deep_space/", branch="master"} +deep_space = { git="https://github.com/iqlusioninc/deep_space/", branch="master" } +ethers = "0.6.1" web30 = "0.15" clarity = "0.4.11" num256 = "0.3" serde_derive = "1.0" +serde_json = "1.0.69" serde = "1.0" tokio = "1.4" tonic = "0.4" @@ -21,6 +24,7 @@ num-bigint = "0.4" log = "0.4" url = "2" sha3 = "0.9" +rustc-hex = "2.1.0" [dev_dependencies] rand = "0.8" -actix = "0.11" \ No newline at end of file +actix = "0.12" diff --git a/orchestrator/gravity_utils/src/connection_prep.rs b/orchestrator/gravity_utils/src/connection_prep.rs index 44a69e320..3d815c500 100644 --- a/orchestrator/gravity_utils/src/connection_prep.rs +++ b/orchestrator/gravity_utils/src/connection_prep.rs @@ -2,22 +2,26 @@ //! It's a common problem to have conflicts between ipv4 and ipv6 localhost and this module is first and foremost supposed to resolve that problem //! by trying more than one thing to handle potentially misconfigured inputs. -use clarity::Address as EthAddress; +use crate::ethereum::format_eth_address; use deep_space::client::ChainStatus; use deep_space::Address as CosmosAddress; use deep_space::Contact; +use ethers::prelude::*; +use ethers::providers::Provider; +use ethers::types::Address as EthAddress; use gravity_proto::gravity::query_client::QueryClient as GravityQueryClient; use gravity_proto::gravity::DelegateKeysByEthereumSignerRequest; use gravity_proto::gravity::DelegateKeysByOrchestratorRequest; +use std::convert::TryFrom; use std::process::exit; +use std::sync::Arc; use std::time::Duration; use tokio::time::sleep as delay_for; use tonic::transport::Channel; use url::Url; -use web30::client::Web3; pub struct Connections { - pub web3: Option, + pub eth_provider: Option>, pub grpc: Option>, pub contact: Option, } @@ -31,7 +35,7 @@ pub async fn create_rpc_connections( eth_rpc_url: Option, timeout: Duration, ) -> Connections { - let mut web3 = None; + let mut eth_provider = None; let mut grpc = None; let mut contact = None; if let Some(grpc_url) = grpc_url { @@ -114,11 +118,15 @@ pub async fn create_rpc_connections( .unwrap_or_else(|_| panic!("Invalid Ethereum RPC url {}", eth_rpc_url)); check_scheme(&url, ð_rpc_url); let eth_url = eth_rpc_url.trim_end_matches('/'); - let base_web30 = Web3::new(eth_url, timeout); - let try_base = base_web30.eth_block_number().await; + // TODO(bolten): should probably set a non-default interval, but what is the appropriate + // value? + let base_eth_provider = Provider::::try_from(eth_url).unwrap_or_else(|_| { + panic!("Could not instantiate Ethereum HTTP provider: {}", eth_url) + }); + let try_base = base_eth_provider.get_block_number().await; match try_base { // it worked, lets go! - Ok(_) => web3 = Some(base_web30), + Ok(_) => eth_provider = Some(base_eth_provider), // did not work, now we check if it's localhost Err(e) => { warn!( @@ -131,19 +139,31 @@ pub async fn create_rpc_connections( let prefix = url.scheme(); let ipv6_url = format!("{}://::1:{}", prefix, port); let ipv4_url = format!("{}://127.0.0.1:{}", prefix, port); - let ipv6_web3 = Web3::new(&ipv6_url, timeout); - let ipv4_web3 = Web3::new(&ipv4_url, timeout); - let ipv6_test = ipv6_web3.eth_block_number().await; - let ipv4_test = ipv4_web3.eth_block_number().await; + let ipv6_eth_provider = Provider::::try_from(ipv6_url.as_str()) + .unwrap_or_else(|_| { + panic!( + "Could not instantiate Ethereum HTTP provider: {}", + &ipv6_url + ) + }); + let ipv4_eth_provider = Provider::::try_from(ipv4_url.as_str()) + .unwrap_or_else(|_| { + panic!( + "Could not instantiate Ethereum HTTP provider: {}", + &ipv4_url + ) + }); + let ipv6_test = ipv6_eth_provider.get_block_number().await; + let ipv4_test = ipv4_eth_provider.get_block_number().await; warn!("Trying fallback urls {} {}", ipv6_url, ipv4_url); match (ipv4_test, ipv6_test) { (Ok(_), Err(_)) => { info!("Url fallback succeeded, your Ethereum rpc url {} has been corrected to {}", eth_rpc_url, ipv4_url); - web3 = Some(ipv4_web3) + eth_provider = Some(ipv4_eth_provider) } (Err(_), Ok(_)) => { info!("Url fallback succeeded, your Ethereum rpc url {} has been corrected to {}", eth_rpc_url, ipv6_url); - web3 = Some(ipv6_web3) + eth_provider = Some(ipv6_eth_provider) }, (Ok(_), Ok(_)) => panic!("This should never happen? Why didn't things work the first time?"), (Err(_), Err(_)) => panic!("Could not connect to Ethereum rpc, are you sure it's running and on the specified port? {}", eth_rpc_url) @@ -155,10 +175,24 @@ pub async fn create_rpc_connections( // transparently upgrade to https if available, we can't transparently downgrade for obvious security reasons let https_on_80_url = format!("https://{}:80", body); let https_on_443_url = format!("https://{}:443", body); - let https_on_80_web3 = Web3::new(&https_on_80_url, timeout); - let https_on_443_web3 = Web3::new(&https_on_443_url, timeout); - let https_on_80_test = https_on_80_web3.eth_block_number().await; - let https_on_443_test = https_on_443_web3.eth_block_number().await; + let https_on_80_eth_provider = + Provider::::try_from(https_on_80_url.as_str()).unwrap_or_else(|_| { + panic!( + "Could not instantiate Ethereum HTTP provider: {}", + &https_on_80_url + ) + }); + let https_on_443_eth_provider = Provider::::try_from( + https_on_443_url.as_str(), + ) + .unwrap_or_else(|_| { + panic!( + "Could not instantiate Ethereum HTTP provider: {}", + &https_on_443_url + ) + }); + let https_on_80_test = https_on_80_eth_provider.get_block_number().await; + let https_on_443_test = https_on_443_eth_provider.get_block_number().await; warn!( "Trying fallback urls {} {}", https_on_443_url, https_on_80_url @@ -166,11 +200,11 @@ pub async fn create_rpc_connections( match (https_on_80_test, https_on_443_test) { (Ok(_), Err(_)) => { info!("Https upgrade succeeded, your Ethereum rpc url {} has been corrected to {}", eth_rpc_url, https_on_80_url); - web3 = Some(https_on_80_web3) + eth_provider = Some(https_on_80_eth_provider) }, (Err(_), Ok(_)) => { info!("Https upgrade succeeded, your Ethereum rpc url {} has been corrected to {}", eth_rpc_url, https_on_443_url); - web3 = Some(https_on_443_web3) + eth_provider = Some(https_on_443_eth_provider) }, (Ok(_), Ok(_)) => panic!("This should never happen? Why didn't things work the first time?"), (Err(_), Err(_)) => panic!("Could not connect to Ethereum rpc, are you sure it's running and on the specified port? {}", eth_rpc_url) @@ -183,7 +217,7 @@ pub async fn create_rpc_connections( } Connections { - web3, + eth_provider, grpc, contact, } @@ -235,7 +269,7 @@ pub async fn check_delegate_addresses( ) { let eth_response = client .delegate_keys_by_ethereum_signer(DelegateKeysByEthereumSignerRequest { - ethereum_signer: delegate_eth_address.to_string(), + ethereum_signer: format_eth_address(delegate_eth_address), }) .await; let orchestrator_response = client @@ -318,9 +352,13 @@ pub async fn check_for_fee_denom(fee_denom: &str, address: CosmosAddress, contac } } +// TODO(bolten): is using LocalWallet too specific? /// Checks the user has some Ethereum in their address to pay for things -pub async fn check_for_eth(address: EthAddress, web3: &Web3) { - let balance = web3.eth_get_balance(address).await.unwrap(); +pub async fn check_for_eth( + address: EthAddress, + eth_client: Arc, LocalWallet>>, +) { + let balance = eth_client.get_balance(address, None).await.unwrap(); if balance == 0u8.into() { warn!("You don't have any Ethereum! You will need to send some to {} for this program to work. Dust will do for basic operations, more info about average relaying costs will be presented as the program runs", address); } diff --git a/orchestrator/gravity_utils/src/error.rs b/orchestrator/gravity_utils/src/error.rs index 64d82addc..832460546 100644 --- a/orchestrator/gravity_utils/src/error.rs +++ b/orchestrator/gravity_utils/src/error.rs @@ -4,19 +4,46 @@ use clarity::Error as ClarityError; use deep_space::error::AddressError as CosmosAddressError; use deep_space::error::CosmosGrpcError; +use deep_space::error::PrivateKeyError as CosmosPrivateKeyError; +use ethers::abi::ethereum_types::FromDecStrErr as EthersParseUintError; +use ethers::abi::Error as EthersAbiError; +use ethers::contract::AbiError as EthersContractAbiError; +use ethers::prelude::errors::EtherscanError; +use ethers::prelude::gas_oracle::GasOracleError as EthersGasOracleError; +use ethers::prelude::signer::SignerMiddlewareError; +use ethers::prelude::ContractError; +use ethers::prelude::ProviderError as EthersProviderError; +use ethers::prelude::*; +use ethers::signers::WalletError as EthersWalletError; +use ethers::types::SignatureError as EthersSignatureError; use num_bigint::ParseBigIntError; +use rustc_hex::FromHexError as EthersParseAddressError; use std::fmt::{self, Debug}; +use std::num::ParseIntError; +use std::string::FromUtf8Error; use tokio::time::error::Elapsed; use tonic::Status; -use web30::jsonrpc::error::Web3Error; #[derive(Debug)] #[allow(clippy::large_enum_variant)] pub enum GravityError { - InvalidBigInt(ParseBigIntError), CosmosGrpcError(CosmosGrpcError), CosmosAddressError(CosmosAddressError), - EthereumRestError(Web3Error), + CosmosPrivateKeyError(CosmosPrivateKeyError), + EthereumBadDataError(String), + EthereumRestError(SignerMiddlewareError, LocalWallet>), + EthersAbiError(EthersAbiError), + EthersContractAbiError(EthersContractAbiError), + EthersContractError(ContractError, LocalWallet>>), + EthersGasOracleError(EthersGasOracleError), + EthersParseAddressError(EthersParseAddressError), + EthersParseUintError(EthersParseUintError), + EthersProviderError(EthersProviderError), + EthersSignatureError(EthersSignatureError), + EthersWalletError(EthersWalletError), + EtherscanError(EtherscanError), + GravityContractError(String), + InvalidArgumentError(String), InvalidBridgeStateError(String), FailedToUpdateValset, EthereumContractError(String), @@ -27,6 +54,9 @@ pub enum GravityError { GravityGrpcError(Status), InsufficientVotingPowerToPass(String), ParseBigIntError(ParseBigIntError), + ParseIntError(ParseIntError), + FromUtf8Error(FromUtf8Error), + OverflowError(String), } impl fmt::Display for GravityError { @@ -34,11 +64,34 @@ impl fmt::Display for GravityError { match self { GravityError::GravityGrpcError(val) => write!(f, "Gravity gRPC error {}", val), GravityError::CosmosGrpcError(val) => write!(f, "Cosmos gRPC error {}", val), - GravityError::InvalidBigInt(val) => { - write!(f, "Got invalid BigInt from cosmos! {}", val) - } GravityError::CosmosAddressError(val) => write!(f, "Cosmos Address error {}", val), - GravityError::EthereumRestError(val) => write!(f, "Ethereum REST error {}", val), + GravityError::CosmosPrivateKeyError(val) => { + write!(f, "Cosmos private key error: {}", val) + } + GravityError::EthereumBadDataError(val) => { + write!(f, "Received unexpected data from Ethereum: {}", val) + } + GravityError::EthereumRestError(val) => write!(f, "Ethereum REST error: {}", val), + GravityError::EthersAbiError(val) => write!(f, "Ethers ABI error: {}", val), + GravityError::EthersContractAbiError(val) => { + write!(f, "Ethers contract ABI error: {}", val) + } + GravityError::EthersContractError(val) => write!(f, "Ethers contract error: {}", val), + GravityError::EthersGasOracleError(val) => { + write!(f, "Ethers gas oracle error: {}", val) + } + GravityError::EthersParseAddressError(val) => { + write!(f, "Ethers H160 address parse error: {}", val) + } + GravityError::EthersParseUintError(val) => { + write!(f, "Ethers U256 parse error: {}", val) + } + GravityError::EthersProviderError(val) => write!(f, "Ethers provider error: {}", val), + GravityError::EthersSignatureError(val) => write!(f, "Ethers signature error: {}", val), + GravityError::EthersWalletError(val) => write!(f, "Ethers wallet error: {}", val), + GravityError::EtherscanError(val) => write!(f, "Etherscan error: {}", val), + GravityError::GravityContractError(val) => write!(f, "Gravity contract error: {}", val), + GravityError::InvalidArgumentError(val) => write!(f, "Invalid argument error: {}", val), GravityError::InvalidOptionsError(val) => { write!(f, "Invalid TX options for this call {}", val) } @@ -56,6 +109,11 @@ impl fmt::Display for GravityError { write!(f, "{}", val) } GravityError::ParseBigIntError(val) => write!(f, "Failed to parse big integer {}", val), + GravityError::ParseIntError(val) => write!(f, "Failed to parse integer: {}", val), + GravityError::FromUtf8Error(val) => { + write!(f, "Failed to parse bytes to UTF-8: {}", val) + }, + GravityError::OverflowError(val) => write!(f, "Overflow error: {}", val), } } } @@ -68,6 +126,18 @@ impl From for GravityError { } } +impl From for GravityError { + fn from(error: CosmosAddressError) -> Self { + GravityError::CosmosAddressError(error) + } +} + +impl From for GravityError { + fn from(error: CosmosPrivateKeyError) -> Self { + GravityError::CosmosPrivateKeyError(error) + } +} + impl From for GravityError { fn from(_error: Elapsed) -> Self { GravityError::TimeoutError @@ -80,23 +150,92 @@ impl From for GravityError { } } -impl From for GravityError { - fn from(error: Web3Error) -> Self { +impl From, LocalWallet>> for GravityError { + fn from(error: SignerMiddlewareError, LocalWallet>) -> Self { GravityError::EthereumRestError(error) } } + +impl From for GravityError { + fn from(error: EthersAbiError) -> Self { + GravityError::EthersAbiError(error) + } +} + +impl From for GravityError { + fn from(error: EthersContractAbiError) -> Self { + GravityError::EthersContractAbiError(error) + } +} + +impl From, LocalWallet>>> for GravityError { + fn from(error: ContractError, LocalWallet>>) -> Self { + GravityError::EthersContractError(error) + } +} + +impl From for GravityError { + fn from(error: EthersGasOracleError) -> Self { + GravityError::EthersGasOracleError(error) + } +} + +impl From for GravityError { + fn from(error: EthersParseAddressError) -> Self { + GravityError::EthersParseAddressError(error) + } +} + +impl From for GravityError { + fn from(error: EthersParseUintError) -> Self { + GravityError::EthersParseUintError(error) + } +} + +impl From for GravityError { + fn from(error: EthersProviderError) -> Self { + GravityError::EthersProviderError(error) + } +} + +impl From for GravityError { + fn from(error: EthersSignatureError) -> Self { + GravityError::EthersSignatureError(error) + } +} + +impl From for GravityError { + fn from(error: EthersWalletError) -> Self { + GravityError::EthersWalletError(error) + } +} + +impl From for GravityError { + fn from(error: EtherscanError) -> Self { + GravityError::EtherscanError(error) + } +} + impl From for GravityError { fn from(error: Status) -> Self { GravityError::GravityGrpcError(error) } } -impl From for GravityError { - fn from(error: CosmosAddressError) -> Self { - GravityError::CosmosAddressError(error) - } -} + impl From for GravityError { fn from(error: ParseBigIntError) -> Self { - GravityError::InvalidBigInt(error) + GravityError::ParseBigIntError(error) + } +} + +impl From for GravityError { + fn from(error: ParseIntError) -> Self { + GravityError::ParseIntError(error) + } +} + +impl From for GravityError { + fn from(error: FromUtf8Error) -> Self { + GravityError::FromUtf8Error(error) } } diff --git a/orchestrator/gravity_utils/src/ethereum.rs b/orchestrator/gravity_utils/src/ethereum.rs new file mode 100644 index 000000000..7130dfd48 --- /dev/null +++ b/orchestrator/gravity_utils/src/ethereum.rs @@ -0,0 +1,163 @@ +use crate::error::GravityError; +use ethers::prelude::*; +use ethers::types::Address as EthAddress; +use std::{panic, result::Result}; + +pub fn downcast_to_f32(input: U256) -> Option { + // technically the max value of u128 is larger than f32, but + // in practicality this won't matter for any of the cases we + // would care about downcasting from a U256, and Rust will + // gracefully saturate the cast + match panic::catch_unwind(|| input.as_u128() as f32) { + Ok(downcasted) => Some(downcasted), + Err(_) => None, + } +} + +pub fn downcast_to_u64(input: U256) -> Option { + match panic::catch_unwind(|| input.as_u64()) { + Ok(downcasted) => Some(downcasted), + Err(_) => None, + } +} + +pub fn downcast_to_u128(input: U256) -> Option { + match panic::catch_unwind(|| input.as_u128()) { + Ok(downcasted) => Some(downcasted), + Err(_) => None, + } +} + +pub fn format_eth_address(address: EthAddress) -> String { + format!("0x{}", bytes_to_hex_str(address.as_bytes())) +} + +pub fn bytes_to_hex_str(bytes: &[u8]) -> String { + bytes + .iter() + .map(|b| format!("{:0>2x?}", b)) + .fold(String::new(), |acc, x| acc + &x) +} + +pub fn hex_str_to_bytes(s: &str) -> Result, GravityError> { + let s = match s.strip_prefix("0x") { + Some(s) => s, + None => &s, + }; + let bytes = s + .as_bytes() + .chunks(2) + .map::, _>(|ch| { + let str = String::from_utf8(ch.to_vec())?; + let byte = u8::from_str_radix(&str, 16)?; + + Ok(byte) + }) + .collect::, _>>()?; + + Ok(bytes) +} + +pub fn vec_u8_to_fixed_32(v: Vec) -> Result<[u8; 32], GravityError> { + if v.len() != 32 { + return Err(GravityError::InvalidArgumentError(format!( + "Error converting Vec to [u8; 32], length is not 32: {:?}", + v + ))); + } + + Ok(u8_slice_to_fixed_32(&v[..])?) +} + +pub fn u8_slice_to_fixed_32(v: &[u8]) -> Result<[u8; 32], GravityError> { + if v.len() != 32 { + return Err(GravityError::InvalidArgumentError(format!( + "Error converting &[u8] to [u8; 32], length is not 32: {:?}", + v + ))); + } + + let mut v_slice: [u8; 32] = Default::default(); + v_slice.copy_from_slice(v); + Ok(v_slice) +} + +#[test] +fn overflow_f32() { + assert_eq!(downcast_to_f32(42.into()), Some(42f32)); + assert_eq!(downcast_to_f32(U256::MAX), None); +} + +#[test] +fn overflow_u64() { + assert_eq!(downcast_to_u64(42.into()), Some(42u64)); + assert_eq!(downcast_to_u64(U256::MAX), None); +} + +#[test] +fn overflow_u128() { + assert_eq!(downcast_to_u128(42.into()), Some(42u128)); + assert_eq!(downcast_to_u128(U256::MAX), None); +} + +#[test] +fn encode_bytes() { + assert_eq!(bytes_to_hex_str(&[0xf]), "0f".to_owned()); + assert_eq!(bytes_to_hex_str(&[0xff]), "ff".to_owned()); + assert_eq!( + bytes_to_hex_str(&[0xde, 0xad, 0xbe, 0xef]), + "deadbeef".to_owned() + ); +} + +#[test] +fn decode_bytes() { + assert_eq!( + hex_str_to_bytes(&"deadbeef".to_owned()).expect("Unable to decode"), + [222, 173, 190, 239] + ); +} + +#[test] +fn decode_odd_amount_of_bytes() { + assert_eq!(hex_str_to_bytes(&"f".to_owned()).unwrap(), vec![15]); +} + +#[test] +fn bytes_raises_decode_error() { + use crate::error::GravityError; + + let e = hex_str_to_bytes(&"\u{012345}deadbeef".to_owned()).unwrap_err(); + + match e { + GravityError::FromUtf8Error(_) => {} + _ => panic!(), + }; +} + +#[test] +fn bytes_raises_parse_error() { + use crate::error::GravityError; + + let e = hex_str_to_bytes(&"Lorem ipsum".to_owned()).unwrap_err(); + match e { + GravityError::ParseIntError(_) => {} + _ => panic!(), + } +} + +#[test] +fn parse_prefixed_empty() { + assert_eq!( + hex_str_to_bytes(&"0x".to_owned()).unwrap(), + Vec::::new() + ); +} + +#[test] +fn parse_prefixed_non_empty() { + assert_eq!( + hex_str_to_bytes(&"0xdeadbeef".to_owned()).unwrap(), + vec![0xde, 0xad, 0xbe, 0xef] + ); +} diff --git a/orchestrator/gravity_utils/src/lib.rs b/orchestrator/gravity_utils/src/lib.rs index 13f655786..55f465d56 100644 --- a/orchestrator/gravity_utils/src/lib.rs +++ b/orchestrator/gravity_utils/src/lib.rs @@ -7,5 +7,6 @@ extern crate log; pub mod connection_prep; pub mod error; +pub mod ethereum; pub mod message_signatures; pub mod types; diff --git a/orchestrator/gravity_utils/src/message_signatures.rs b/orchestrator/gravity_utils/src/message_signatures.rs index 91b96e3b5..345d2d800 100644 --- a/orchestrator/gravity_utils/src/message_signatures.rs +++ b/orchestrator/gravity_utils/src/message_signatures.rs @@ -1,6 +1,7 @@ use crate::types::{LogicCall, TransactionBatch, Valset}; -use clarity::abi::{encode_tokens, Token}; -use clarity::utils::get_ethereum_msg_hash; +use ethers::core::abi::{self, Token}; +use ethers::utils::hash_message; +use ethers::utils::keccak256; /// takes the required input data and produces the required signature to confirm a validator /// set update on the Gravity Ethereum contract. This value will then be signed before being @@ -9,25 +10,33 @@ use clarity::utils::get_ethereum_msg_hash; /// digest that is normally signed or may be used as a 'hash of the message' pub fn encode_valset_confirm(gravity_id: String, valset: Valset) -> Vec { let (eth_addresses, powers) = valset.filter_empty_addresses(); - encode_tokens(&[ - Token::FixedString(gravity_id), - Token::FixedString("checkpoint".to_string()), - valset.nonce.into(), - eth_addresses.into(), - powers.into(), + let eth_addresses = eth_addresses + .iter() + .map(|address| Token::Address(*address)) + .collect(); + let powers = powers + .iter() + .map(|power| Token::Uint((*power).into())) + .collect(); + + abi::encode(&[ + Token::FixedBytes(gravity_id.into_bytes()), + Token::FixedBytes("checkpoint".to_string().into_bytes()), + Token::Uint(valset.nonce.into()), + Token::Array(eth_addresses), + Token::Array(powers), ]) } pub fn encode_valset_confirm_hashed(gravity_id: String, valset: Valset) -> Vec { - let digest = encode_valset_confirm(gravity_id, valset); - get_ethereum_msg_hash(&digest) + let digest = keccak256(encode_valset_confirm(gravity_id, valset).as_slice()); + hash_message(digest).as_bytes().to_vec() } #[test] fn test_valset_signature() { - use crate::types::ValsetMember; - use clarity::utils::hex_str_to_bytes; - use sha3::{Digest, Keccak256}; + use crate::{ethereum::hex_str_to_bytes, types::ValsetMember}; + use ethers::utils::keccak256; let correct_hash: Vec = hex_str_to_bytes("0x88165860d955aee7dc3e83d9d1156a5864b708841965585d206dbef6e9e1a499") @@ -64,8 +73,8 @@ fn test_valset_signature() { ], }; let checkpoint = encode_valset_confirm("foo".to_string(), valset); - let checkpoint_hash = Keccak256::digest(&checkpoint); - assert_eq!(correct_hash, checkpoint_hash.as_slice()); + let checkpoint_hash = keccak256(&checkpoint); + assert_eq!(correct_hash, checkpoint_hash); // the same valset, except with an intentionally incorrect hash let valset = Valset { @@ -98,8 +107,8 @@ fn test_valset_signature() { ], }; let checkpoint = encode_valset_confirm("foo".to_string(), valset); - let checkpoint_hash = Keccak256::digest(&checkpoint); - assert_ne!(correct_hash, checkpoint_hash.as_slice()) + let checkpoint_hash = keccak256(&checkpoint); + assert_ne!(correct_hash, checkpoint_hash) } /// takes the required input data and produces the required signature to confirm a transaction @@ -108,32 +117,35 @@ fn test_valset_signature() { /// Note: This is the message, you need to run Keccak256::digest() in order to get the 32byte /// digest that is normally signed or may be used as a 'hash of the message' pub fn encode_tx_batch_confirm(gravity_id: String, batch: TransactionBatch) -> Vec { - let (amounts, destinations, fees) = batch.get_checkpoint_values(); - encode_tokens(&[ - Token::FixedString(gravity_id), - Token::FixedString("transactionBatch".to_string()), + let (amounts, destinations, fees) = batch.get_checkpoint_values_tokens(); + + abi::encode(&[ + Token::FixedBytes(gravity_id.into_bytes()), + Token::FixedBytes("transactionBatch".to_string().into_bytes()), amounts, destinations, fees, - batch.nonce.into(), - batch.token_contract.into(), - batch.batch_timeout.into(), + Token::Uint(batch.nonce.into()), + Token::Address(batch.token_contract), + Token::Uint(batch.batch_timeout.into()), ]) } pub fn encode_tx_batch_confirm_hashed(gravity_id: String, batch: TransactionBatch) -> Vec { - let digest = encode_tx_batch_confirm(gravity_id, batch); - get_ethereum_msg_hash(&digest) + let digest = keccak256(encode_tx_batch_confirm(gravity_id, batch).as_slice()); + hash_message(digest).as_bytes().to_vec() } -#[test] -fn test_batch_signature() { - use crate::types::BatchTransaction; - use crate::types::Erc20Token; - use clarity::utils::hex_str_to_bytes; - use clarity::PrivateKey as EthPrivateKey; +#[tokio::test] +async fn test_batch_signature() { + use crate::{ + ethereum::{hex_str_to_bytes, u8_slice_to_fixed_32}, + types::{BatchTransaction, Erc20Token}, + }; + use ethers::core::k256::ecdsa::SigningKey; + use ethers::prelude::*; + use ethers::utils::keccak256; use rand::Rng; - use sha3::{Digest, Keccak256}; let correct_hash: Vec = hex_str_to_bytes("0xa3a7ee0a363b8ad2514e7ee8f110d7449c0d88f3b0913c28c1751e6e0079a9b2") @@ -167,27 +179,35 @@ fn test_batch_signature() { }; let checkpoint = encode_tx_batch_confirm("foo".to_string(), batch.clone()); - let checkpoint_hash = Keccak256::digest(&checkpoint); + let checkpoint_hash = keccak256(&checkpoint); assert_eq!(correct_hash.len(), checkpoint_hash.len()); - assert_eq!(correct_hash, checkpoint_hash.as_slice()); + assert_eq!(correct_hash, checkpoint_hash); // checkpoint is correct lets make sure our signature code works let mut rng = rand::thread_rng(); let secret: [u8; 32] = rng.gen(); - let eth_key = EthPrivateKey::from_slice(&secret).unwrap(); - let eth_address = eth_key.to_public_key().unwrap(); - let checkpoint = encode_tx_batch_confirm_hashed("foo".to_string(), batch); + let eth_key = SigningKey::from_bytes(&secret).unwrap(); + let eth_wallet = LocalWallet::from(eth_key); + let eth_address = eth_wallet.address(); + let checkpoint = + keccak256(encode_tx_batch_confirm("foo".to_string(), batch.clone()).as_slice()); + let checkpoint_hash = encode_tx_batch_confirm_hashed("foo".to_string(), batch.clone()); + let checkpoint_hash = u8_slice_to_fixed_32(&checkpoint_hash).unwrap(); - let eth_signature = eth_key.sign_hash(&checkpoint); + let eth_signature = eth_wallet.sign_message(checkpoint).await.unwrap(); - assert_eq!(eth_address, eth_signature.recover(&checkpoint).unwrap()); + assert_eq!(eth_address, eth_signature.recover(checkpoint_hash).unwrap()); } -#[test] -fn test_specific_batch_signature() { - use crate::types::BatchTransaction; - use crate::types::Erc20Token; - use clarity::PrivateKey as EthPrivateKey; +#[tokio::test] +async fn test_specific_batch_signature() { + use crate::{ + ethereum::u8_slice_to_fixed_32, + types::{BatchTransaction, Erc20Token}, + }; + use ethers::core::k256::ecdsa::SigningKey; + use ethers::prelude::*; + use ethers::utils::keccak256; use rand::Rng; let erc20_addr = "0x0635FF793Edf48cf5dB294916720A78e6e490E40" @@ -221,14 +241,18 @@ fn test_specific_batch_signature() { let mut rng = rand::thread_rng(); let secret: [u8; 32] = rng.gen(); // the starting location of the funds - let eth_key = EthPrivateKey::from_slice(&secret).unwrap(); - let eth_address = eth_key.to_public_key().unwrap(); + let eth_key = SigningKey::from_bytes(&secret).unwrap(); + let eth_wallet = LocalWallet::from(eth_key); + let eth_address = eth_wallet.address(); - let checkpoint = encode_tx_batch_confirm_hashed("foo".to_string(), batch); + let checkpoint = + keccak256(encode_tx_batch_confirm("foo".to_string(), batch.clone()).as_slice()); + let checkpoint_hash = encode_tx_batch_confirm_hashed("foo".to_string(), batch.clone()); + let checkpoint_hash = u8_slice_to_fixed_32(&checkpoint_hash).unwrap(); - let eth_signature = eth_key.sign_hash(&checkpoint); + let eth_signature = eth_wallet.sign_message(checkpoint).await.unwrap(); - assert_eq!(eth_address, eth_signature.recover(&checkpoint).unwrap()); + assert_eq!(eth_address, eth_signature.recover(checkpoint_hash).unwrap()); } /// takes the required input data and produces the required signature to confirm a logic @@ -237,45 +261,54 @@ fn test_specific_batch_signature() { /// Note: This is the message, you need to run Keccak256::digest() in order to get the 32byte /// digest that is normally signed or may be used as a 'hash of the message' pub fn encode_logic_call_confirm(gravity_id: String, call: LogicCall) -> Vec { - let mut transfer_amounts = Vec::new(); - let mut transfer_token_contracts = Vec::new(); - let mut fee_amounts = Vec::new(); - let mut fee_token_contracts = Vec::new(); - for item in call.transfers.iter() { - transfer_amounts.push(Token::Uint(item.amount.clone())); - transfer_token_contracts.push(item.token_contract_address); - } - for item in call.fees.iter() { - fee_amounts.push(Token::Uint(item.amount.clone())); - fee_token_contracts.push(item.token_contract_address); - } - - encode_tokens(&[ - Token::FixedString(gravity_id), // Gravity Instance ID - Token::FixedString("logicCall".to_string()), //Function Name - Token::Dynamic(transfer_amounts), //Array of Transfer amounts - transfer_token_contracts.into(), //ERC-20 contract for transfers - Token::Dynamic(fee_amounts), // Array of Fees - fee_token_contracts.into(), // ERC-20 contract for fee payments - call.logic_contract_address.into(), // Address of a logic contract - Token::UnboundedBytes(call.payload), // Encoded arguments to logic contract - call.timeout.into(), // Timeout on batch - Token::Bytes(call.invalidation_id), // Scope of logic batch - call.invalidation_nonce.into(), // Nonce of logic batch. See 2-d nonce scheme. + let transfer_amounts = call + .transfers + .iter() + .map(|transfer| Token::Uint(transfer.amount)) + .collect(); + let transfer_token_contracts = call + .transfers + .iter() + .map(|transfer| Token::Address(transfer.token_contract_address)) + .collect(); + let fee_amounts = call + .fees + .iter() + .map(|fee| Token::Uint(fee.amount)) + .collect(); + let fee_token_contracts = call + .fees + .iter() + .map(|fee| Token::Address(fee.token_contract_address)) + .collect(); + + abi::encode(&[ + Token::FixedBytes(gravity_id.into_bytes()), // Gravity Instance ID + Token::FixedBytes("logicCall".to_string().into_bytes()), // Function Name + Token::Array(transfer_amounts), // Array of Transfer amounts + Token::Array(transfer_token_contracts), // ERC-20 contract for transfers + Token::Array(fee_amounts), // Array of Fees + Token::Array(fee_token_contracts), // ERC-20 contract for fee payments + Token::Address(call.logic_contract_address), // Address of a logic contract + Token::Bytes(call.payload), // Encoded arguments to logic contract + Token::Uint(call.timeout.into()), // Timeout on batch + Token::FixedBytes(call.invalidation_id), // Scope of logic batch + Token::Uint(call.invalidation_nonce.into()), // Nonce of logic batch. See 2-d nonce scheme. ]) } pub fn encode_logic_call_confirm_hashed(gravity_id: String, call: LogicCall) -> Vec { - let digest = encode_logic_call_confirm(gravity_id, call); - get_ethereum_msg_hash(&digest) + let digest = keccak256(encode_logic_call_confirm(gravity_id, call).as_slice()); + hash_message(digest).as_bytes().to_vec() } #[test] fn test_logic_call_signature() { - use crate::types::Erc20Token; - use crate::types::LogicCall; - use clarity::utils::hex_str_to_bytes; - use sha3::{Digest, Keccak256}; + use crate::{ + ethereum::hex_str_to_bytes, + types::{Erc20Token, LogicCall}, + }; + use ethers::utils::keccak256; let correct_hash: Vec = hex_str_to_bytes("0x1de95c9ace999f8ec70c6dc8d045942da2612950567c4861aca959c0650194da") @@ -309,8 +342,8 @@ fn test_logic_call_signature() { let checkpoint = encode_logic_call_confirm("foo".to_string(), logic_call); println!("{}", checkpoint.len() / 32); - let checkpoint_hash = Keccak256::digest(&checkpoint); + let checkpoint_hash = keccak256(&checkpoint); assert_eq!(correct_hash.len(), checkpoint_hash.len()); - assert_eq!(correct_hash, checkpoint_hash.as_slice()) + assert_eq!(correct_hash, checkpoint_hash) } diff --git a/orchestrator/gravity_utils/src/types/batches.rs b/orchestrator/gravity_utils/src/types/batches.rs index e6127e5fd..4975d1724 100644 --- a/orchestrator/gravity_utils/src/types/batches.rs +++ b/orchestrator/gravity_utils/src/types/batches.rs @@ -1,8 +1,9 @@ use super::*; use crate::error::GravityError; -use clarity::Signature as EthSignature; -use clarity::{abi::Token, Address as EthAddress}; use deep_space::Address as CosmosAddress; +use ethers::core::abi::Token; +use ethers::types::{Address as EthAddress, Signature as EthSignature}; +use std::{convert::TryFrom, result::Result}; /// This represents an individual transaction being bridged over to Ethereum /// parallel is the OutgoingTransferTx in x/gravity/types/batch.go @@ -22,6 +23,7 @@ impl BatchTransaction { "Can not have tx with null erc20_token!".to_string(), )); } + Ok(BatchTransaction { id: input.id, sender: input.sender.parse()?, @@ -45,21 +47,42 @@ pub struct TransactionBatch { impl TransactionBatch { /// extracts the amounts, destinations and fees as submitted to the Ethereum contract /// and used for signatures - pub fn get_checkpoint_values(&self) -> (Token, Token, Token) { - let mut amounts = Vec::new(); - let mut destinations = Vec::new(); - let mut fees = Vec::new(); - for item in self.transactions.iter() { - amounts.push(Token::Uint(item.erc20_token.amount.clone())); - fees.push(Token::Uint(item.erc20_fee.amount.clone())); - destinations.push(item.ethereum_recipient) - } + pub fn get_checkpoint_values(&self) -> (Vec, Vec, Vec) { + let amounts: Vec = self + .transactions + .iter() + .map(|tx| tx.erc20_token.amount) + .collect(); + let destinations: Vec = self + .transactions + .iter() + .map(|tx| tx.ethereum_recipient) + .collect(); + let fees: Vec = self + .transactions + .iter() + .map(|tx| tx.erc20_fee.amount) + .collect(); + assert_eq!(amounts.len(), destinations.len()); assert_eq!(fees.len(), destinations.len()); + + (amounts, destinations, fees) + } + + pub fn get_checkpoint_values_tokens(&self) -> (Token, Token, Token) { + let (amounts, destinations, fees) = self.get_checkpoint_values(); + let amounts_tokens = amounts.iter().map(|amount| Token::Uint(*amount)).collect(); + let destinations_tokens = destinations + .iter() + .map(|destination| Token::Address(*destination)) + .collect(); + let fees_tokens = fees.iter().map(|fee| Token::Uint(*fee)).collect(); + ( - Token::Dynamic(amounts), - destinations.into(), - Token::Dynamic(fees), + Token::Array(amounts_tokens), + Token::Array(destinations_tokens), + Token::Array(fees_tokens), ) } @@ -69,15 +92,23 @@ impl TransactionBatch { for tx in input.transactions { let tx = BatchTransaction::from_proto(tx)?; if let Some(total_fee) = running_total_fee { + let running_amount = total_fee.amount.checked_add(tx.erc20_fee.amount); + if running_amount.is_none() { + return Err(GravityError::OverflowError( + format!("U256 overflow when adding all fees together for transaction batch with nonce {}", input.batch_nonce) + )) + } + running_total_fee = Some(Erc20Token { token_contract_address: total_fee.token_contract_address, - amount: total_fee.amount + tx.erc20_fee.amount.clone(), + amount: running_amount.unwrap(), }); } else { running_total_fee = Some(tx.erc20_fee.clone()) } transactions.push(tx); } + if let Some(total_fee) = running_total_fee { Ok(TransactionBatch { batch_timeout: input.timeout, @@ -111,7 +142,7 @@ impl BatchConfirmResponse { nonce: input.batch_nonce, token_contract: input.token_contract.parse()?, ethereum_signer: input.ethereum_signer.parse()?, - eth_signature: EthSignature::from_bytes(&input.signature)?, + eth_signature: EthSignature::try_from(input.signature.as_slice())?, }) } } @@ -121,6 +152,6 @@ impl Confirm for BatchConfirmResponse { self.ethereum_signer } fn get_signature(&self) -> EthSignature { - self.eth_signature.clone() + self.eth_signature } } diff --git a/orchestrator/gravity_utils/src/types/ethereum_events.rs b/orchestrator/gravity_utils/src/types/ethereum_events.rs index b6d83a986..01f681312 100644 --- a/orchestrator/gravity_utils/src/types/ethereum_events.rs +++ b/orchestrator/gravity_utils/src/types/ethereum_events.rs @@ -6,151 +6,157 @@ use super::ValsetMember; use crate::error::GravityError; -use clarity::Address as EthAddress; -use deep_space::utils::bytes_to_hex_str; +use crate::ethereum::downcast_to_u64; use deep_space::Address as CosmosAddress; -use num256::Uint256; -use std::unimplemented; -use web30::types::Log; +use ethers::abi::RawLog; +use ethers::prelude::*; +use ethers::types::Address as EthAddress; +use gravity_abi::gravity::*; +use std::result::Result; + +// given a Log retrieved by querying the Ethereum chain, decode it into one of +// the generated event types for the Gravity contract +fn log_to_ethers_event(log: &Log) -> Result { + T::decode_log(&RawLog { + topics: log.topics.clone(), + data: log.data.to_vec(), + }) + .map_err(From::from) +} + +// our event model structs use U256 to represent block height, but Logs provide it +// to us as a U64 (strangely, no direct conversion from U64, so we have to do this type dance) +fn block_height_from_log(log: &Log) -> Result { + match log.block_number.clone() { + Some(block_height) => Ok(block_height.as_u64().into()), + None => Err(GravityError::InvalidEventLogError(format!( + "Log does not have block number, we only search logs already in blocks? {:?}", + log + ))), + } +} + +// some traits to avoid code duplication + +pub trait FromLog: Sized { + fn from_log(input: &Log) -> Result; +} + +pub trait FromLogWithPrefix: Sized { + fn from_log(input: &Log, prefix: &str) -> Result; +} + +pub trait EventNonce { + fn get_event_nonce(&self) -> U256; +} + +pub trait FromLogs { + fn from_logs(input: &[Log]) -> Result, GravityError> { + let mut res = Vec::new(); + for item in input { + res.push(T::from_log(item)?); + } + Ok(res) + } +} + +pub trait FromLogsWithPrefix { + fn from_logs( + input: &[Log], + prefix: &str, + ) -> Result, GravityError> { + let mut res = Vec::new(); + for item in input { + res.push(T::from_log(item, prefix)?); + } + Ok(res) + } +} + +pub trait EventNonceFilter: Sized { + /// returns all values in the array with event nonces greater + /// than the provided value + fn filter_by_event_nonce(event_nonce: u64, input: &[T]) -> Vec { + input + .iter() + .filter(|item| item.get_event_nonce() > event_nonce.into()) + .map(|item| (*item).clone()) + .collect() + } +} /// A parsed struct representing the Ethereum event fired by the Gravity contract /// when the validator set is updated. #[derive(Serialize, Deserialize, Debug, Default, Clone, Eq, PartialEq, Hash)] pub struct ValsetUpdatedEvent { - pub valset_nonce: Uint256, - pub event_nonce: Uint256, - pub block_height: Uint256, + pub valset_nonce: U256, + pub event_nonce: U256, + pub block_height: U256, pub members: Vec, } -impl ValsetUpdatedEvent { +impl FromLog for ValsetUpdatedEvent { /// This function is not an abi compatible bytes parser, but it's actually /// not hard at all to extract data like this by hand. - pub fn from_log(input: &Log) -> Result { - // we have one indexed event so we should fined two indexes, one the event itself - // and one the indexed nonce - if input.topics.get(1).is_none() { - return Err(GravityError::InvalidEventLogError( - "Too few topics".to_string(), - )); - } - let valset_nonce_data = &input.topics[1]; - let valset_nonce = Uint256::from_bytes_be(valset_nonce_data); - if valset_nonce > u64::MAX.into() { - return Err(GravityError::InvalidEventLogError( - "Nonce overflow, probably incorrect parsing".to_string(), - )); - } - let valset_nonce: u64 = valset_nonce.to_string().parse().unwrap(); - - // first index is the event nonce, following two have event data we don't - // care about, fourth index contains the length of the eth address array - let index_start = 0; - let index_end = index_start + 32; - let nonce_data = &input.data[index_start..index_end]; - let event_nonce = Uint256::from_bytes_be(nonce_data); - if event_nonce > u64::MAX.into() { - return Err(GravityError::InvalidEventLogError( - "Nonce overflow, probably incorrect parsing".to_string(), - )); - } - let event_nonce: u64 = event_nonce.to_string().parse().unwrap(); - // first index is the event nonce, following two have event data we don't - // care about, fourth index contains the length of the eth address array - let index_start = 3 * 32; - let index_end = index_start + 32; - let eth_addresses_offset = index_start + 32; - let len_eth_addresses = Uint256::from_bytes_be(&input.data[index_start..index_end]); - if len_eth_addresses > usize::MAX.into() { - return Err(GravityError::InvalidEventLogError( - "Ethereum array len overflow, probably incorrect parsing".to_string(), - )); - } - let len_eth_addresses: usize = len_eth_addresses.to_string().parse().unwrap(); - let index_start = (4 + len_eth_addresses) * 32; - let index_end = index_start + 32; - let powers_offset = index_start + 32; - let len_powers = Uint256::from_bytes_be(&input.data[index_start..index_end]); - if len_powers > usize::MAX.into() { - return Err(GravityError::InvalidEventLogError( - "Powers array len overflow, probably incorrect parsing".to_string(), - )); - } - let len_powers: usize = len_eth_addresses.to_string().parse().unwrap(); - if len_powers != len_eth_addresses { - return Err(GravityError::InvalidEventLogError( - "Array len mismatch, probably incorrect parsing".to_string(), - )); + fn from_log(input: &Log) -> Result { + let event: ValsetUpdatedEventFilter = log_to_ethers_event(input)?; + if event.powers.len() != event.validators.len() { + return Err(GravityError::InvalidEventLogError(format!( + "ValsetUpdatedEvent powers and validators have different length: {:?}", + event + ))); } - let mut validators = Vec::new(); - for i in 0..len_eth_addresses { - let power_start = (i * 32) + powers_offset; - let power_end = power_start + 32; - let address_start = (i * 32) + eth_addresses_offset; - let address_end = address_start + 32; - let power = Uint256::from_bytes_be(&input.data[power_start..power_end]); - // an eth address at 20 bytes is 12 bytes shorter than the Uint256 it's stored in. - let eth_address = EthAddress::from_slice(&input.data[address_start + 12..address_end]); - if eth_address.is_err() { - return Err(GravityError::InvalidEventLogError( - "Ethereum Address parsing error, probably incorrect parsing".to_string(), - )); - } - let eth_address = Some(eth_address.unwrap()); - if power > u64::MAX.into() { - return Err(GravityError::InvalidEventLogError( - "Power greater than u64::MAX, probably incorrect parsing".to_string(), - )); + let mut powers: Vec = Vec::new(); + for power in &event.powers { + if let Some(downcast_power) = downcast_to_u64(*power) { + powers.push(downcast_power); + } else { + return Err(GravityError::InvalidEventLogError(format!( + "ValsetUpdatedEvent contains powers that cannot be downcast to u64: {:?}", + event + ))); } - let power: u64 = power.to_string().parse().unwrap(); - validators.push(ValsetMember { power, eth_address }) } + + let validators: Vec = powers + .iter() + .zip(event.validators.iter()) + .map(|(power, validator)| ValsetMember { + power: *power, + eth_address: Some(*validator), + }) + .collect(); + let mut check = validators.clone(); check.sort(); check.reverse(); // if the validator set is not sorted we're in a bad spot + // TODO(bolten): perhaps there is a better way to handle this than logging the event? + // what would the downstream effects of not returning a ValsetUpdatedEvent here? if validators != check { - trace!( + warn!( "Someone submitted an unsorted validator set, this means all updates will fail until someone feeds in this unsorted value by hand {:?} instead of {:?}", validators, check ); } - let block_height = if let Some(bn) = input.block_number.clone() { - bn - } else { - return Err(GravityError::InvalidEventLogError( - "Log does not have block number, we only search logs already in blocks?" - .to_string(), - )); - }; Ok(ValsetUpdatedEvent { - valset_nonce: valset_nonce.into(), - event_nonce: event_nonce.into(), - block_height, + valset_nonce: event.new_valset_nonce, + event_nonce: event.event_nonce, + block_height: block_height_from_log(&input)?, members: validators, }) } - pub fn from_logs(input: &[Log]) -> Result, GravityError> { - let mut res = Vec::new(); - for item in input { - res.push(ValsetUpdatedEvent::from_log(item)?); - } - Ok(res) - } - /// returns all values in the array with event nonces greater - /// than the provided value - pub fn filter_by_event_nonce(event_nonce: u64, input: &[Self]) -> Vec { - let mut ret = Vec::new(); - for item in input { - if item.event_nonce > event_nonce.into() { - ret.push(item.clone()) - } - } - ret +} + +impl FromLogs for ValsetUpdatedEvent {} +impl EventNonce for ValsetUpdatedEvent { + fn get_event_nonce(&self) -> U256 { + self.event_nonce } } +impl EventNonceFilter for ValsetUpdatedEvent {} /// A parsed struct representing the Ethereum event fired by the Gravity contract when /// a transaction batch is executed. @@ -158,74 +164,38 @@ impl ValsetUpdatedEvent { pub struct TransactionBatchExecutedEvent { /// the nonce attached to the transaction batch that follows /// it throughout it's lifecycle - pub batch_nonce: Uint256, + pub batch_nonce: U256, /// The block height this event occurred at - pub block_height: Uint256, + pub block_height: U256, /// The ERC20 token contract address for the batch executed, since batches are uniform /// in token type there is only one pub erc20: EthAddress, /// the event nonce representing a unique ordering of events coming out /// of the Gravity solidity contract. Ensuring that these events can only be played /// back in order - pub event_nonce: Uint256, + pub event_nonce: U256, } -impl TransactionBatchExecutedEvent { - pub fn from_log(input: &Log) -> Result { - if let (Some(batch_nonce_data), Some(erc20_data)) = - (input.topics.get(1), input.topics.get(2)) - { - let batch_nonce = Uint256::from_bytes_be(batch_nonce_data); - let erc20 = EthAddress::from_slice(&erc20_data[12..32])?; - let event_nonce = Uint256::from_bytes_be(&input.data); - let block_height = if let Some(bn) = input.block_number.clone() { - bn - } else { - return Err(GravityError::InvalidEventLogError( - "Log does not have block number, we only search logs already in blocks?" - .to_string(), - )); - }; - if event_nonce > u64::MAX.into() - || batch_nonce > u64::MAX.into() - || block_height > u64::MAX.into() - { - Err(GravityError::InvalidEventLogError( - "Event nonce overflow, probably incorrect parsing".to_string(), - )) - } else { - Ok(TransactionBatchExecutedEvent { - batch_nonce, - block_height, - erc20, - event_nonce, - }) - } - } else { - Err(GravityError::InvalidEventLogError( - "Too few topics".to_string(), - )) - } - } - pub fn from_logs(input: &[Log]) -> Result, GravityError> { - let mut res = Vec::new(); - for item in input { - res.push(TransactionBatchExecutedEvent::from_log(item)?); - } - Ok(res) +impl FromLog for TransactionBatchExecutedEvent { + fn from_log(input: &Log) -> Result { + let event: TransactionBatchExecutedEventFilter = log_to_ethers_event(input)?; + + Ok(TransactionBatchExecutedEvent { + batch_nonce: event.batch_nonce, + block_height: block_height_from_log(&input)?, + erc20: event.token, + event_nonce: event.event_nonce, + }) } - /// returns all values in the array with event nonces greater - /// than the provided value - pub fn filter_by_event_nonce(event_nonce: u64, input: &[Self]) -> Vec { - let mut ret = Vec::new(); - for item in input { - if item.event_nonce > event_nonce.into() { - ret.push(item.clone()) - } - } - ret +} + +impl FromLogs for TransactionBatchExecutedEvent {} +impl EventNonce for TransactionBatchExecutedEvent { + fn get_event_nonce(&self) -> U256 { + self.event_nonce } } +impl EventNonceFilter for TransactionBatchExecutedEvent {} /// A parsed struct representing the Ethereum event fired when someone makes a deposit /// on the Gravity contract @@ -238,77 +208,36 @@ pub struct SendToCosmosEvent { /// The Cosmos destination pub destination: CosmosAddress, /// The amount of the erc20 token that is being sent - pub amount: Uint256, + pub amount: U256, /// The transaction's nonce, used to make sure there can be no accidental duplication - pub event_nonce: Uint256, + pub event_nonce: U256, /// The block height this event occurred at - pub block_height: Uint256, + pub block_height: U256, } -impl SendToCosmosEvent { - pub fn from_log(input: &Log, prefix: &str) -> Result { - let topics = ( - input.topics.get(1), - input.topics.get(2), - input.topics.get(3), - ); - if let (Some(erc20_data), Some(sender_data), Some(destination_data)) = topics { - let erc20 = EthAddress::from_slice(&erc20_data[12..32])?; - let sender = EthAddress::from_slice(&sender_data[12..32])?; - // this is required because deep_space requires a fixed length slice to - // create an address from bytes. - let mut c_address_bytes: [u8; 20] = [0; 20]; - c_address_bytes.copy_from_slice(&destination_data[12..32]); - let destination = CosmosAddress::from_bytes(c_address_bytes, prefix).unwrap(); - let amount = Uint256::from_bytes_be(&input.data[..32]); - let event_nonce = Uint256::from_bytes_be(&input.data[32..]); - let block_height = if let Some(bn) = input.block_number.clone() { - bn - } else { - return Err(GravityError::InvalidEventLogError( - "Log does not have block number, we only search logs already in blocks?" - .to_string(), - )); - }; - if event_nonce > u64::MAX.into() || block_height > u64::MAX.into() { - Err(GravityError::InvalidEventLogError( - "Event nonce overflow, probably incorrect parsing".to_string(), - )) - } else { - Ok(SendToCosmosEvent { - erc20, - sender, - destination, - amount, - event_nonce, - block_height, - }) - } - } else { - Err(GravityError::InvalidEventLogError( - "Too few topics".to_string(), - )) - } - } - pub fn from_logs(input: &[Log], prefix: &str) -> Result, GravityError> { - let mut res = Vec::new(); - for item in input { - res.push(Self::from_log(item, prefix)?); - } - Ok(res) +impl FromLogWithPrefix for SendToCosmosEvent { + fn from_log(input: &Log, prefix: &str) -> Result { + let event: SendToCosmosEventFilter = log_to_ethers_event(input)?; + + // TODO(bolten): verify this method of retrieval for Cosmos addresses works as expected + Ok(SendToCosmosEvent { + erc20: event.token_contract, + sender: event.sender, + destination: CosmosAddress::from_slice(&event.destination[12..32], prefix)?, + amount: event.amount, + event_nonce: event.event_nonce, + block_height: block_height_from_log(&input)?, + }) } - /// returns all values in the array with event nonces greater - /// than the provided value - pub fn filter_by_event_nonce(event_nonce: u64, input: &[Self]) -> Vec { - let mut ret = Vec::new(); - for item in input { - if item.event_nonce > event_nonce.into() { - ret.push(item.clone()) - } - } - ret +} + +impl FromLogsWithPrefix for SendToCosmosEvent {} +impl EventNonce for SendToCosmosEvent { + fn get_event_nonce(&self) -> U256 { + self.event_nonce } } +impl EventNonceFilter for SendToCosmosEvent {} /// A parsed struct representing the Ethereum event fired when someone uses the Gravity /// contract to deploy a new ERC20 contract representing a Cosmos asset @@ -326,191 +255,63 @@ pub struct Erc20DeployedEvent { pub symbol: String, /// The number of decimals required to represent the smallest unit of this token pub decimals: u8, - pub event_nonce: Uint256, - pub block_height: Uint256, + pub event_nonce: U256, + pub block_height: U256, } -impl Erc20DeployedEvent { - pub fn from_log(input: &Log) -> Result { - let token_contract = input.topics.get(1); - if let Some(new_token_contract_data) = token_contract { - let erc20 = EthAddress::from_slice(&new_token_contract_data[12..32])?; - let index_start = 3 * 32; - let index_end = index_start + 32; - let decimals = Uint256::from_bytes_be(&input.data[index_start..index_end]); - if decimals > u8::MAX.into() { - return Err(GravityError::InvalidEventLogError( - "Decimals overflow, probably incorrect parsing".to_string(), - )); - } - let decimals: u8 = decimals.to_string().parse().unwrap(); - - let index_start = 4 * 32; - let index_end = index_start + 32; - let nonce = Uint256::from_bytes_be(&input.data[index_start..index_end]); - if nonce > u64::MAX.into() { - return Err(GravityError::InvalidEventLogError( - "Nonce overflow, probably incorrect parsing".to_string(), - )); - } - - let index_start = 5 * 32; - let index_end = index_start + 32; - let denom_len = Uint256::from_bytes_be(&input.data[index_start..index_end]); - // it's not probable that we have 4+ gigabytes of event data - if denom_len > u32::MAX.into() { - return Err(GravityError::InvalidEventLogError( - "denom length overflow, probably incorrect parsing".to_string(), - )); - } - let denom_len: usize = denom_len.to_string().parse().unwrap(); - let index_start = 6 * 32; - let index_end = index_start + denom_len; - let denom = String::from_utf8(input.data[index_start..index_end].to_vec()); - trace!("Denom {:?}", denom); - if denom.is_err() { - return Err(GravityError::InvalidEventLogError(format!( - "{:?} is not valid utf8, probably incorrect parsing", - denom - ))); - } - let denom = denom.unwrap(); - - // beyond this point we are parsing strings placed - // after a variable length string and we will need to compute offsets - - // this trick computes the next 32 byte (256 bit) word index, then multiplies by - // 32 to get the bytes offset, this is required since we have dynamic length types but - // the next entry always starts on a round 32 byte word. - let index_start = ((index_end + 31) / 32) * 32; - let index_end = index_start + 32; - let erc20_name_len = Uint256::from_bytes_be(&input.data[index_start..index_end]); - // it's not probable that we have 4+ gigabytes of event data - if erc20_name_len > u32::MAX.into() { - return Err(GravityError::InvalidEventLogError( - "ERC20 Name length overflow, probably incorrect parsing".to_string(), - )); - } - let erc20_name_len: usize = erc20_name_len.to_string().parse().unwrap(); - let index_start = index_end; - let index_end = index_start + erc20_name_len; - let erc20_name = String::from_utf8(input.data[index_start..index_end].to_vec()); - if erc20_name.is_err() { - return Err(GravityError::InvalidEventLogError(format!( - "{:?} is not valid utf8, probably incorrect parsing", - erc20_name - ))); - } - trace!("ERC20 Name {:?}", erc20_name); - let erc20_name = erc20_name.unwrap(); - - let index_start = ((index_end + 31) / 32) * 32; - let index_end = index_start + 32; - let symbol_len = Uint256::from_bytes_be(&input.data[index_start..index_end]); - // it's not probable that we have 4+ gigabytes of event data - if symbol_len > u32::MAX.into() { - return Err(GravityError::InvalidEventLogError( - "Symbol length overflow, probably incorrect parsing".to_string(), - )); - } - let symbol_len: usize = symbol_len.to_string().parse().unwrap(); - let index_start = index_end; - let index_end = index_start + symbol_len; - let symbol = String::from_utf8(input.data[index_start..index_end].to_vec()); - trace!("Symbol {:?}", symbol); - if symbol.is_err() { - return Err(GravityError::InvalidEventLogError(format!( - "{:?} is not valid utf8, probably incorrect parsing", - symbol - ))); - } - let symbol = symbol.unwrap(); +impl FromLog for Erc20DeployedEvent { + fn from_log(input: &Log) -> Result { + let event: Erc20DeployedEventFilter = log_to_ethers_event(input)?; - let block_height = if let Some(bn) = input.block_number.clone() { - bn - } else { - return Err(GravityError::InvalidEventLogError( - "Log does not have block number, we only search logs already in blocks?" - .to_string(), - )); - }; - - Ok(Erc20DeployedEvent { - cosmos_denom: denom, - name: erc20_name, - decimals, - event_nonce: nonce, - erc20_address: erc20, - symbol, - block_height, - }) - } else { - Err(GravityError::InvalidEventLogError( - "Too few topics".to_string(), - )) - } - } - pub fn from_logs(input: &[Log]) -> Result, GravityError> { - let mut res = Vec::new(); - for item in input { - res.push(Erc20DeployedEvent::from_log(item)?); - } - Ok(res) + Ok(Erc20DeployedEvent { + cosmos_denom: event.cosmos_denom, + erc20_address: event.token_contract, + name: event.name, + symbol: event.symbol, + decimals: event.decimals, + event_nonce: event.event_nonce, + block_height: block_height_from_log(&input)?, + }) } - /// returns all values in the array with event nonces greater - /// than the provided value - pub fn filter_by_event_nonce(event_nonce: u64, input: &[Self]) -> Vec { - let mut ret = Vec::new(); - for item in input { - if item.event_nonce > event_nonce.into() { - ret.push(item.clone()) - } - } - ret +} + +impl FromLogs for Erc20DeployedEvent {} +impl EventNonce for Erc20DeployedEvent { + fn get_event_nonce(&self) -> U256 { + self.event_nonce } } +impl EventNonceFilter for Erc20DeployedEvent {} + /// A parsed struct representing the Ethereum event fired when someone uses the Gravity /// contract to deploy a new ERC20 contract representing a Cosmos asset #[derive(Serialize, Deserialize, Debug, Default, Clone, Eq, PartialEq, Hash)] pub struct LogicCallExecutedEvent { pub invalidation_id: Vec, - pub invalidation_nonce: Uint256, + pub invalidation_nonce: U256, pub return_data: Vec, - pub event_nonce: Uint256, - pub block_height: Uint256, + pub event_nonce: U256, + pub block_height: U256, } -impl LogicCallExecutedEvent { - pub fn from_log(_input: &Log) -> Result { - unimplemented!("foo") - } - pub fn from_logs(input: &[Log]) -> Result, GravityError> { - let mut res = Vec::new(); - for item in input { - res.push(LogicCallExecutedEvent::from_log(item)?); - } - Ok(res) - } - /// returns all values in the array with event nonces greater - /// than the provided value - pub fn filter_by_event_nonce(event_nonce: u64, input: &[Self]) -> Vec { - let mut ret = Vec::new(); - for item in input { - if item.event_nonce > event_nonce.into() { - ret.push(item.clone()) - } - } - ret +impl FromLog for LogicCallExecutedEvent { + fn from_log(input: &Log) -> Result { + let event: LogicCallEventFilter = log_to_ethers_event(input)?; + + Ok(LogicCallExecutedEvent { + invalidation_id: event.invalidation_id.into(), + invalidation_nonce: event.invalidation_nonce, + return_data: event.return_data.to_vec(), + event_nonce: event.event_nonce, + block_height: block_height_from_log(&input)?, + }) } } -/// Function used for debug printing hex dumps -/// of ethereum events -fn _debug_print_data(input: &[u8]) { - let count = input.len() / 32; - println!("data hex dump"); - for i in 0..count { - println!("0x{}", bytes_to_hex_str(&input[(i * 32)..((i * 32) + 32)])) +impl FromLogs for LogicCallExecutedEvent {} +impl EventNonce for LogicCallExecutedEvent { + fn get_event_nonce(&self) -> U256 { + self.event_nonce } - println!("end dump"); } +impl EventNonceFilter for LogicCallExecutedEvent {} diff --git a/orchestrator/gravity_utils/src/types/logic_call.rs b/orchestrator/gravity_utils/src/types/logic_call.rs index 9486ea3a1..ddc37e26a 100644 --- a/orchestrator/gravity_utils/src/types/logic_call.rs +++ b/orchestrator/gravity_utils/src/types/logic_call.rs @@ -1,6 +1,7 @@ use super::*; use crate::error::GravityError; -use clarity::{Address as EthAddress, Signature as EthSignature}; +use ethers::types::{Address as EthAddress, Signature as EthSignature}; +use std::{convert::TryFrom, result::Result}; /// the response we get when querying for a valset confirmation #[derive(Serialize, Deserialize, Debug, Default, Clone)] @@ -24,11 +25,6 @@ impl LogicCall { for fee in input.fees { fees.push(Erc20Token::from_proto(fee)?) } - if transfers.is_empty() || fees.is_empty() { - return Err(GravityError::InvalidBridgeStateError( - "Transaction batch containing no transactions!".to_string(), - )); - } Ok(LogicCall { transfers, @@ -59,7 +55,7 @@ impl LogicCallConfirmResponse { invalidation_id: input.invalidation_scope, invalidation_nonce: input.invalidation_nonce, ethereum_signer: input.ethereum_signer.parse()?, - eth_signature: EthSignature::from_bytes(&input.signature)?, + eth_signature: EthSignature::try_from(input.signature.as_slice())?, }) } } @@ -69,6 +65,6 @@ impl Confirm for LogicCallConfirmResponse { self.ethereum_signer } fn get_signature(&self) -> EthSignature { - self.eth_signature.clone() + self.eth_signature } } diff --git a/orchestrator/gravity_utils/src/types/mod.rs b/orchestrator/gravity_utils/src/types/mod.rs index b84cfe0b5..801b88525 100644 --- a/orchestrator/gravity_utils/src/types/mod.rs +++ b/orchestrator/gravity_utils/src/types/mod.rs @@ -1,11 +1,13 @@ -use clarity::Address as EthAddress; -use num256::Uint256; mod batches; mod ethereum_events; mod logic_call; mod signatures; mod valsets; + use crate::error::GravityError; +use ethers::prelude::*; +use ethers::types::Address as EthAddress; +use std::result::Result; pub use batches::*; pub use ethereum_events::*; @@ -15,7 +17,7 @@ pub use valsets::*; #[derive(Serialize, Deserialize, Debug, Default, Clone, Eq, PartialEq, Hash)] pub struct Erc20Token { - pub amount: Uint256, + pub amount: U256, #[serde(rename = "contract")] pub token_contract_address: EthAddress, } @@ -23,8 +25,8 @@ pub struct Erc20Token { impl Erc20Token { pub fn from_proto(input: gravity_proto::gravity::Erc20Token) -> Result { Ok(Erc20Token { - amount: input.amount.parse()?, - token_contract_address: EthAddress::parse_and_validate(&input.contract)?, + amount: U256::from_dec_str(input.amount.as_str())?, + token_contract_address: input.contract.parse()?, }) } } diff --git a/orchestrator/gravity_utils/src/types/signatures.rs b/orchestrator/gravity_utils/src/types/signatures.rs index 8f95d81d9..942d65758 100644 --- a/orchestrator/gravity_utils/src/types/signatures.rs +++ b/orchestrator/gravity_utils/src/types/signatures.rs @@ -1,7 +1,7 @@ -use clarity::Signature as EthSignature; -use clarity::{abi::Token, Address as EthAddress}; -use num256::Uint256; +use ethers::prelude::*; +use ethers::types::{Address as EthAddress, Signature as EthSignature}; use std::cmp::Ordering; +use std::convert::TryFrom; /// A sortable struct of a validator and it's signatures /// this can be used for either transaction batch or validator @@ -10,9 +10,9 @@ use std::cmp::Ordering; pub struct GravitySignature { pub power: u64, pub eth_address: EthAddress, - pub v: Uint256, - pub r: Uint256, - pub s: Uint256, + pub v: u64, + pub r: U256, + pub s: U256, } impl Ord for GravitySignature { @@ -44,37 +44,37 @@ impl PartialOrd for GravitySignature { pub struct GravitySignatureArrays { pub addresses: Vec, pub powers: Vec, - pub v: Token, - pub r: Token, - pub s: Token, + pub v: Vec, + pub r: Vec<[u8; 32]>, + pub s: Vec<[u8; 32]>, } /// This function handles converting the GravitySignature type into an Ethereum /// submittable arrays, including the finicky token encoding tricks you need to /// perform in order to distinguish between a uint8[] and bytes32[] pub fn to_arrays(input: Vec) -> GravitySignatureArrays { - let mut addresses = Vec::new(); - let mut powers = Vec::new(); - let mut v = Vec::new(); - let mut r = Vec::new(); - let mut s = Vec::new(); - for val in input { - addresses.push(val.eth_address); - powers.push(val.power); - v.push(val.v); - r.push(val.r); - s.push(val.s); - } + let addresses = input.iter().map(|sig| sig.eth_address).collect(); + let powers = input.iter().map(|sig| sig.power).collect(); + // TODO(bolten): we're also throwing panics if we encounter downcast errors in + // ethereum_gravity/src/utils.rs, we should consider broadly how to handle + // these sorts of error conditions + let v = input + .iter() + .map(|sig| u8::try_from(sig.v).expect("Gravity Signature v overflow! Bridge halt!")) + .collect(); + let r = input.iter().map(|sig| sig.r.into()).collect(); + let s = input.iter().map(|sig| sig.s.into()).collect(); + GravitySignatureArrays { addresses, powers, - v: v.into(), - r: r.into(), - s: s.into(), + v, + r, + s, } } -#[derive(Serialize, Deserialize, Debug, Default, Clone, Eq, PartialEq, Hash)] +#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] pub struct SigWithAddress { pub eth_address: EthAddress, pub eth_signature: EthSignature, diff --git a/orchestrator/gravity_utils/src/types/valsets.rs b/orchestrator/gravity_utils/src/types/valsets.rs index ab4ae0f33..46692c426 100644 --- a/orchestrator/gravity_utils/src/types/valsets.rs +++ b/orchestrator/gravity_utils/src/types/valsets.rs @@ -1,15 +1,16 @@ use super::*; -use crate::error::GravityError; -use clarity::Address as EthAddress; -use clarity::Signature as EthSignature; +use crate::{ + error::GravityError, + ethereum::{format_eth_address, u8_slice_to_fixed_32}, +}; use deep_space::error::CosmosGrpcError; +use ethers::types::{Address as EthAddress, Signature as EthSignature}; +use std::convert::TryFrom; use std::fmt::Debug; use std::{ cmp::Ordering, collections::{HashMap, HashSet}, - fmt, - str, - // str::FromStr, + fmt, str, }; /// The total power in the Gravity bridge is normalized to u32 max every @@ -66,7 +67,7 @@ impl ValsetConfirmResponse { Ok(ValsetConfirmResponse { eth_signer: input.ethereum_signer.parse()?, nonce: input.signer_set_nonce, - eth_signature: EthSignature::from_bytes(&input.signature)?, + eth_signature: EthSignature::try_from(input.signature.as_slice())?, }) } } @@ -76,7 +77,7 @@ impl Confirm for ValsetConfirmResponse { self.eth_signer } fn get_signature(&self) -> EthSignature { - self.eth_signature.clone() + self.eth_signature } } @@ -101,7 +102,7 @@ impl Valset { powers.push(val.power); } None => { - addresses.push(EthAddress::default()); + addresses.push(EthAddress::zero()); powers.push(val.power); } } @@ -149,15 +150,15 @@ impl Valset { if let Some(eth_address) = member.eth_address { if let Some(sig) = signatures_hashmap.get(ð_address) { assert_eq!(sig.get_eth_address(), eth_address); - assert!(sig.get_signature().is_valid()); - let recover_key = sig.get_signature().recover(signed_message).unwrap(); + let sig_hash = u8_slice_to_fixed_32(signed_message)?; + let recover_key = sig.get_signature().recover(sig_hash)?; if recover_key == sig.get_eth_address() { out.push(GravitySignature { power: member.power, eth_address: sig.get_eth_address(), - v: sig.get_signature().v.clone(), - r: sig.get_signature().r.clone(), - s: sig.get_signature().s.clone(), + v: sig.get_signature().v, + r: sig.get_signature().r, + s: sig.get_signature().s, }); power_of_good_sigs += member.power; } else { @@ -276,52 +277,6 @@ impl Valset { } res } - - /// This function takes the current valset and compares it to a provided one - /// returning a percentage difference in their power allocation. This is a very - /// important function as it's used to decide when the validator sets are updated - /// on the Ethereum chain and when new validator sets are requested on the Cosmos - /// side. In theory an error here, if unnoticed for long enough, could allow funds - /// to be stolen from the bridge without the validators in question still having stake - /// to lose. - /// Returned value must be less than or equal to two - pub fn power_diff(&self, other: &Valset) -> f32 { - let mut total_power_diff = 0u64; - let a = self.to_hashmap(); - let b = other.to_hashmap(); - let a_map = self.to_hashset(); - let b_map = other.to_hashset(); - // items in A and B, we go through these and compute the absolute value of the - // difference in power and sum it. - let intersection = a_map.intersection(&b_map); - // items in A but not in B or vice versa, since we're just trying to compute the difference - // we can simply sum all of these up. - let symmetric_difference = a_map.symmetric_difference(&b_map); - for item in symmetric_difference { - let mut power = None; - if let Some(val) = a.get(item) { - power = Some(val); - } else if let Some(val) = b.get(item) { - power = Some(val); - } - // impossible for this to panic without a failure in the logic - // of the symmetric difference function - let power = power.unwrap(); - total_power_diff += power; - } - for item in intersection { - // can't panic since there must be an entry for both. - let power_a = a[item]; - let power_b = b[item]; - if power_a > power_b { - total_power_diff += power_a - power_b; - } else { - total_power_diff += power_b - power_a; - } - } - - (total_power_diff as f32) / (u32::MAX as f32) - } } impl From for Valset { @@ -409,7 +364,12 @@ impl ValsetMember { impl fmt::Display for ValsetMember { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.eth_address { - Some(a) => write!(f, "Address: {} Power: {}", a, self.power), + Some(a) => write!( + f, + "Address: {} Power: {}", + format_eth_address(a), + self.power + ), None => write!(f, "Address: None Power: {}", self.power), } } @@ -444,7 +404,7 @@ impl From<&gravity_proto::gravity::EthereumSigner> for ValsetMember { impl From<&ValsetMember> for gravity_proto::gravity::EthereumSigner { fn from(input: &ValsetMember) -> gravity_proto::gravity::EthereumSigner { let ethereum_address = match input.eth_address { - Some(e) => e.to_string(), + Some(e) => format_eth_address(e), None => String::new(), }; gravity_proto::gravity::EthereumSigner { diff --git a/orchestrator/orchestrator/Cargo.toml b/orchestrator/orchestrator/Cargo.toml index 2dcde9787..1462fd140 100644 --- a/orchestrator/orchestrator/Cargo.toml +++ b/orchestrator/orchestrator/Cargo.toml @@ -8,33 +8,30 @@ edition = "2018" name = "orchestrator" path = "src/lib.rs" -[[bin]] -name = "orchestrator" -path = "src/main.rs" - [dependencies] relayer = { path = "../relayer" } ethereum_gravity = { path = "../ethereum_gravity" } cosmos_gravity = { path = "../cosmos_gravity" } +gravity_abi = { path = "../gravity_abi" } gravity_utils = { path = "../gravity_utils" } gravity_proto = { path = "../gravity_proto" } deep_space={git="https://github.com/iqlusioninc/deep_space/", branch = "master" } +ethers = "0.6.1" serde_derive = "1.0" clarity = "0.4.11" docopt = "1" serde = "1.0" -actix-rt = "2.2" +actix-rt = "2.5" lazy_static = "1" web30 = "0.15" -num256 = "0.3" log = "0.4" env_logger = "0.8" serde_json = "1.0" tokio = "1.4.0" rand = "0.8" tonic = "0.4" -futures = "0.3" +futures = "0.3.18" openssl-probe = "0.1" axum = "0.1.2" diff --git a/orchestrator/orchestrator/src/ethereum_event_watcher.rs b/orchestrator/orchestrator/src/ethereum_event_watcher.rs index 571345dc6..520a56312 100644 --- a/orchestrator/orchestrator/src/ethereum_event_watcher.rs +++ b/orchestrator/orchestrator/src/ethereum_event_watcher.rs @@ -2,229 +2,231 @@ //! or a transaction batch update. It then responds to these events by performing actions on the Cosmos chain if required use crate::get_with_retry::get_block_number_with_retry; -use crate::get_with_retry::get_net_version_with_retry; +use crate::get_with_retry::get_chain_id_with_retry; use crate::metrics; -use clarity::{utils::bytes_to_hex_str, Address as EthAddress, Uint256}; use cosmos_gravity::build; use cosmos_gravity::query::get_last_event_nonce; use deep_space::private_key::PrivateKey as CosmosPrivateKey; use deep_space::{Contact, Msg}; +use ethereum_gravity::types::EthClient; +use ethers::prelude::*; +use ethers::types::Address as EthAddress; +use gravity_abi::gravity::*; use gravity_proto::gravity::query_client::QueryClient as GravityQueryClient; +use gravity_utils::ethereum::downcast_to_u64; +use gravity_utils::types::EventNonceFilter; +use gravity_utils::types::{FromLogs, FromLogsWithPrefix}; use gravity_utils::{ error::GravityError, + ethereum::bytes_to_hex_str, types::{ Erc20DeployedEvent, LogicCallExecutedEvent, SendToCosmosEvent, TransactionBatchExecutedEvent, ValsetUpdatedEvent, }, }; -use std::time; +use std::{result::Result, time}; use tonic::transport::Channel; -use web30::client::Web3; -use web30::jsonrpc::error::Web3Error; pub async fn check_for_events( - web3: &Web3, + eth_client: EthClient, contact: &Contact, grpc_client: &mut GravityQueryClient, gravity_contract_address: EthAddress, cosmos_key: CosmosPrivateKey, - starting_block: Uint256, + starting_block: U64, msg_sender: tokio::sync::mpsc::Sender>, -) -> Result { +) -> Result { let prefix = contact.get_prefix(); let our_cosmos_address = cosmos_key.to_address(&prefix).unwrap(); - let latest_block = get_block_number_with_retry(web3).await; - let latest_block = latest_block - get_block_delay(web3).await; - - metrics::set_ethereum_check_for_events_starting_block(starting_block.clone()); - metrics::set_ethereum_check_for_events_end_block(latest_block.clone()); - - let deposits = web3 - .check_for_events( - starting_block.clone(), - Some(latest_block.clone()), - vec![gravity_contract_address], - vec!["SendToCosmosEvent(address,address,bytes32,uint256,uint256)"], + let latest_block = get_block_number_with_retry(eth_client.clone()).await; + let latest_block = latest_block - get_block_delay(eth_client.clone()).await?; + + metrics::set_ethereum_check_for_events_starting_block(starting_block.as_u64()); + metrics::set_ethereum_check_for_events_end_block(latest_block.as_u64()); + + let filter_gravity_contract_address = ValueOrArray::Value(gravity_contract_address); + + let mut erc20_deployed_filter = Filter::new() + .address(filter_gravity_contract_address.clone()) + .event(&Erc20DeployedEventFilter::abi_signature()); + let mut logic_call_filter = Filter::new() + .address(filter_gravity_contract_address.clone()) + .event(&LogicCallEventFilter::abi_signature()); + let mut send_to_cosmos_filter = Filter::new() + .address(filter_gravity_contract_address.clone()) + .event(&SendToCosmosEventFilter::abi_signature()); + let mut transaction_batch_filter = Filter::new() + .address(filter_gravity_contract_address.clone()) + .event(&TransactionBatchExecutedEventFilter::abi_signature()); + let mut valset_updated_filter = Filter::new() + .address(filter_gravity_contract_address.clone()) + .event(&ValsetUpdatedEventFilter::abi_signature()); + + let search_range = starting_block..latest_block; + + // select uses an inclusive version of the range + erc20_deployed_filter = erc20_deployed_filter.select(search_range.clone()); + logic_call_filter = logic_call_filter.select(search_range.clone()); + send_to_cosmos_filter = send_to_cosmos_filter.select(search_range.clone()); + transaction_batch_filter = transaction_batch_filter.select(search_range.clone()); + valset_updated_filter = valset_updated_filter.select(search_range.clone()); + + let erc20_deployed_events = eth_client.get_logs(&erc20_deployed_filter).await?; + debug!("ERC20 events detected {:?}", erc20_deployed_events); + let erc20_deployed_events = Erc20DeployedEvent::from_logs(&erc20_deployed_events)?; + debug!("parsed erc20 deploys {:?}", erc20_deployed_events); + + let logic_call_events = eth_client.get_logs(&logic_call_filter).await?; + debug!("Logic call events detected {:?}", logic_call_events); + let logic_call_events = LogicCallExecutedEvent::from_logs(&logic_call_events)?; + debug!("parsed logic call executions {:?}", logic_call_events); + + let send_to_cosmos_events = eth_client.get_logs(&send_to_cosmos_filter).await?; + debug!("Send to Cosmos events detected {:?}", send_to_cosmos_events); + let send_to_cosmos_events = SendToCosmosEvent::from_logs(&send_to_cosmos_events, &prefix)?; + debug!("parsed send to cosmos events {:?}", send_to_cosmos_events); + + let transaction_batch_events = eth_client.get_logs(&transaction_batch_filter).await?; + debug!("Batch events detected {:?}", transaction_batch_events); + let transaction_batch_events = + TransactionBatchExecutedEvent::from_logs(&transaction_batch_events)?; + debug!("parsed batches {:?}", transaction_batch_events); + + let valset_updated_events = eth_client.get_logs(&valset_updated_filter).await?; + debug!("Valset events detected {:?}", valset_updated_events); + let valset_updated_events = ValsetUpdatedEvent::from_logs(&valset_updated_events)?; + debug!("parsed valsets {:?}", valset_updated_events); + + // note that starting block overlaps with our last checked block, because we have to deal with + // the possibility that the relayer was killed after relaying only one of multiple events in a single + // block, so we also need this routine so make sure we don't send in the first event in this hypothetical + // multi event block again. In theory we only send all events for every block and that will pass of fail + // atomicly but lets not take that risk. + let last_event_nonce = get_last_event_nonce(grpc_client, our_cosmos_address).await?; + metrics::set_cosmos_last_event_nonce(last_event_nonce); + + let erc20_deployed_events: Vec = + Erc20DeployedEvent::filter_by_event_nonce(last_event_nonce, &erc20_deployed_events); + let logic_call_events: Vec = + LogicCallExecutedEvent::filter_by_event_nonce(last_event_nonce, &logic_call_events); + let send_to_cosmos_events: Vec = + SendToCosmosEvent::filter_by_event_nonce(last_event_nonce, &send_to_cosmos_events); + let transaction_batch_events: Vec = + TransactionBatchExecutedEvent::filter_by_event_nonce( + last_event_nonce, + &transaction_batch_events, + ); + let valset_updated_events: Vec = + ValsetUpdatedEvent::filter_by_event_nonce(last_event_nonce, &valset_updated_events); + + for erc20_deployed_event in erc20_deployed_events.iter() { + info!( + "Oracle observed ERC20 deploy with denom {} erc20 name {} and symbol {} and event_nonce {}", + erc20_deployed_event.cosmos_denom, + erc20_deployed_event.name, + erc20_deployed_event.symbol, + erc20_deployed_event.event_nonce, ) - .await; - debug!("Deposit events detected {:?}", deposits); - - let batches = web3 - .check_for_events( - starting_block.clone(), - Some(latest_block.clone()), - vec![gravity_contract_address], - vec!["TransactionBatchExecutedEvent(uint256,address,uint256)"], - ) - .await; - debug!("Batche events detected {:?}", batches); - - let valsets = web3 - .check_for_events( - starting_block.clone(), - Some(latest_block.clone()), - vec![gravity_contract_address], - vec!["ValsetUpdatedEvent(uint256,uint256,address[],uint256[])"], - ) - .await; - debug!("Valset events detected {:?}", valsets); - - let erc20_deployed = web3 - .check_for_events( - starting_block.clone(), - Some(latest_block.clone()), - vec![gravity_contract_address], - vec!["ERC20DeployedEvent(string,address,string,string,uint8,uint256)"], - ) - .await; - debug!("ERC20 events detected {:?}", erc20_deployed); - - let logic_calls = web3 - .check_for_events( - starting_block.clone(), - Some(latest_block.clone()), - vec![gravity_contract_address], - vec!["LogicCallEvent(bytes32,uint256,bytes,uint256)"], + } + + for logic_call_event in logic_call_events.iter() { + info!( + "Oracle observed logic call execution with invalidation_id {} invalidation_nonce {} and event_nonce {}", + bytes_to_hex_str(&logic_call_event.invalidation_id), + logic_call_event.invalidation_nonce, + logic_call_event.event_nonce + ); + } + + for send_to_cosmos_event in send_to_cosmos_events.iter() { + info!( + "Oracle observed deposit with ethereum sender {}, cosmos_reciever {}, amount {}, and event nonce {}", + send_to_cosmos_event.sender, + send_to_cosmos_event.destination, + send_to_cosmos_event.amount, + send_to_cosmos_event.event_nonce + ); + } + + for transaction_batch_event in transaction_batch_events.iter() { + info!( + "Oracle observed batch with batch_nonce {}, erc20 {}, and event_nonce {}", + transaction_batch_event.batch_nonce, + transaction_batch_event.erc20, + transaction_batch_event.event_nonce + ); + } + + for valset_updated_event in valset_updated_events.iter() { + info!( + "Oracle observed valset with valset_nonce {}, event_nonce {}, block_height {} and members {:?}", + valset_updated_event.valset_nonce, + valset_updated_event.event_nonce, + valset_updated_event.block_height, + valset_updated_event.members ) - .await; - debug!("Logic call events detected {:?}", logic_calls); + } - if let (Ok(valsets), Ok(batches), Ok(deposits), Ok(deploys), Ok(logic_calls)) = - (valsets, batches, deposits, erc20_deployed, logic_calls) + if !erc20_deployed_events.is_empty() + || !logic_call_events.is_empty() + || !send_to_cosmos_events.is_empty() + || !transaction_batch_events.is_empty() + || !valset_updated_events.is_empty() { - let deposits = SendToCosmosEvent::from_logs(&deposits, &prefix)?; - debug!("parsed deposits {:?}", deposits); - - let batches = TransactionBatchExecutedEvent::from_logs(&batches)?; - debug!("parsed batches {:?}", batches); - - let valsets = ValsetUpdatedEvent::from_logs(&valsets)?; - debug!("parsed valsets {:?}", valsets); - - let erc20_deploys = Erc20DeployedEvent::from_logs(&deploys)?; - debug!("parsed erc20 deploys {:?}", erc20_deploys); - - let logic_calls = LogicCallExecutedEvent::from_logs(&logic_calls)?; - debug!("logic call executions {:?}", logic_calls); - - // note that starting block overlaps with our last checked block, because we have to deal with - // the possibility that the relayer was killed after relaying only one of multiple events in a single - // block, so we also need this routine so make sure we don't send in the first event in this hypothetical - // multi event block again. In theory we only send all events for every block and that will pass of fail - // atomicly but lets not take that risk. - let last_event_nonce = get_last_event_nonce(grpc_client, our_cosmos_address).await?; - metrics::set_cosmos_last_event_nonce(last_event_nonce); - - let deposits = SendToCosmosEvent::filter_by_event_nonce(last_event_nonce, &deposits); - let batches = - TransactionBatchExecutedEvent::filter_by_event_nonce(last_event_nonce, &batches); - let valsets = ValsetUpdatedEvent::filter_by_event_nonce(last_event_nonce, &valsets); - let erc20_deploys = - Erc20DeployedEvent::filter_by_event_nonce(last_event_nonce, &erc20_deploys); - let logic_calls = - LogicCallExecutedEvent::filter_by_event_nonce(last_event_nonce, &logic_calls); - - for deposit in deposits.iter() { - info!( - "Oracle observed deposit with ethereum sender {}, cosmos_reciever {}, amount {}, and event nonce {}", - deposit.sender, deposit.destination, deposit.amount, deposit.event_nonce - ); + let messages = build::ethereum_event_messages( + contact, + cosmos_key, + send_to_cosmos_events.to_owned(), + transaction_batch_events.to_owned(), + erc20_deployed_events.to_owned(), + logic_call_events.to_owned(), + valset_updated_events.to_owned(), + ); + + info!("Sending {} messages to cosmos", messages.len()); + + if let Some(erc20_deployed_event) = erc20_deployed_events.last() { + metrics::set_ethereum_last_erc20_event(erc20_deployed_event.event_nonce); + metrics::set_ethereum_last_erc20_block(erc20_deployed_event.block_height); } - for batch in batches.iter() { - info!( - "Oracle observed batch with batch_nonce {}, erc20 {}, and event_nonce {}", - batch.batch_nonce, batch.erc20, batch.event_nonce - ); + if let Some(send_to_cosmos_event) = send_to_cosmos_events.last() { + metrics::set_ethereum_last_deposit_event(send_to_cosmos_event.event_nonce); + metrics::set_ethereum_last_deposit_block(send_to_cosmos_event.block_height); } - for valset in valsets.iter() { - info!( - "Oracle observed valset with valset_nonce {}, event_nonce {}, block_height {} and members {:?}", - valset.valset_nonce, valset.event_nonce, valset.block_height, valset.members, - ) + if let Some(transaction_batch_event) = transaction_batch_events.last() { + metrics::set_ethereum_last_batch_event(transaction_batch_event.event_nonce); + metrics::set_ethereum_last_batch_nonce(transaction_batch_event.batch_nonce); } - for erc20_deploy in erc20_deploys.iter() { - info!( - "Oracle observed ERC20 deploy with denom {} erc20 name {} and symbol {} and event_nonce {}", - erc20_deploy.cosmos_denom, erc20_deploy.name, erc20_deploy.symbol, erc20_deploy.event_nonce, - ) + if let Some(valset_updated_event) = valset_updated_events.last() { + metrics::set_ethereum_last_valset_event(valset_updated_event.event_nonce); + metrics::set_ethereum_last_valset_nonce(valset_updated_event.valset_nonce); } - for logic_call in logic_calls.iter() { - info!( - "Oracle observed logic call execution with invalidation_id {} invalidation_nonce {} and event_nonce {}", - bytes_to_hex_str(&logic_call.invalidation_id), - logic_call.invalidation_nonce, - logic_call.event_nonce - ); + if let Some(logic_call_event) = logic_call_events.last() { + metrics::set_ethereum_last_logic_call_event(logic_call_event.event_nonce); + metrics::set_ethereum_last_logic_call_nonce(logic_call_event.invalidation_nonce); } - if !deposits.is_empty() - || !batches.is_empty() - || !valsets.is_empty() - || !erc20_deploys.is_empty() - || !logic_calls.is_empty() - { - let messages = build::ethereum_event_messages( - contact, - cosmos_key, - deposits.to_owned(), - batches.to_owned(), - erc20_deploys.to_owned(), - logic_calls.to_owned(), - valsets.to_owned(), - ); - - info!("Sending {} messages to cosmos", messages.len()); - - if let Some(deposit) = deposits.last() { - metrics::set_ethereum_last_deposit_event(deposit.event_nonce.clone()); - metrics::set_ethereum_last_deposit_block(deposit.block_height.clone()); - } - - if let Some(batch) = batches.last() { - metrics::set_ethereum_last_batch_event(batch.event_nonce.clone()); - metrics::set_ethereum_last_batch_nonce(batch.batch_nonce.clone()); - } - - if let Some(valset) = valsets.last() { - metrics::set_ethereum_last_valset_event(valset.event_nonce.clone()); - metrics::set_ethereum_last_valset_nonce(valset.valset_nonce.clone()); - } - - if let Some(erc20_deploy) = erc20_deploys.last() { - metrics::set_ethereum_last_erc20_event(erc20_deploy.event_nonce.clone()); - metrics::set_ethereum_last_erc20_block(erc20_deploy.block_height.clone()); - } - - if let Some(logic_call) = logic_calls.last() { - metrics::set_ethereum_last_logic_call_event(logic_call.event_nonce.clone()); - metrics::set_ethereum_last_logic_call_nonce(logic_call.invalidation_nonce.clone()); - } - - msg_sender - .send(messages) - .await - .expect("Could not send messages"); - - let timeout = time::Duration::from_secs(30); - contact.wait_for_next_block(timeout).await?; - - let new_event_nonce = get_last_event_nonce(grpc_client, our_cosmos_address).await?; - if new_event_nonce == last_event_nonce { - return Err(GravityError::InvalidBridgeStateError( - format!("Claims did not process, trying to update but still on {}, trying again in a moment", last_event_nonce), - )); - } + msg_sender + .send(messages) + .await + .expect("Could not send messages"); + + let timeout = time::Duration::from_secs(30); + contact.wait_for_next_block(timeout).await?; + + let new_event_nonce = get_last_event_nonce(grpc_client, our_cosmos_address).await?; + if new_event_nonce == last_event_nonce { + return Err(GravityError::InvalidBridgeStateError( + format!("Claims did not process, trying to update but still on {}, trying again in a moment", last_event_nonce), + )); } - Ok(latest_block) - } else { - Err(GravityError::EthereumRestError(Web3Error::BadResponse( - "Failed to get logs!".to_string(), - ))) } + + Ok(latest_block) } /// The number of blocks behind the 'latest block' on Ethereum our event checking should be. @@ -247,17 +249,28 @@ pub async fn check_for_events( /// Given an uncle every 2.8 minutes, a 6 deep reorg would be 2.8 minutes * (100^4) or one /// 6 deep reorg every 53,272 years. /// -pub async fn get_block_delay(web3: &Web3) -> Uint256 { - let net_version = get_net_version_with_retry(web3).await; +pub async fn get_block_delay(eth_client: EthClient) -> Result { + // TODO(bolten): technically we want the network id from net_version, but chain id + // should be the same for our use cases...when this PR is in a released version of + // ethers we can move back to net_version: + // https://github.com/gakonst/ethers-rs/pull/595 + let chain_id_result = get_chain_id_with_retry(eth_client.clone()).await; + let chain_id = downcast_to_u64(chain_id_result); + if chain_id.is_none() { + return Err(GravityError::EthereumBadDataError(format!( + "Chain ID is larger than u64 max: {}", + chain_id_result + ))); + } - match net_version { + match chain_id.unwrap() { // Mainline Ethereum, Ethereum classic, or the Ropsten, Mordor testnets // all POW Chains - 1 | 3 | 7 => 6u8.into(), + 1 | 3 | 7 => Ok(6u8.into()), // Rinkeby, Goerli, Dev, our own Gravity Ethereum testnet, and Kotti respectively // all non-pow chains - 4 | 5 | 2018 | 15 | 6 => 0u8.into(), + 4 | 5 | 2018 | 15 | 6 => Ok(0u8.into()), // assume the safe option (POW) where we don't know - _ => 6u8.into(), + _ => Ok(6u8.into()), } } diff --git a/orchestrator/orchestrator/src/get_with_retry.rs b/orchestrator/orchestrator/src/get_with_retry.rs index 62d2105b9..3c9fe975a 100644 --- a/orchestrator/orchestrator/src/get_with_retry.rs +++ b/orchestrator/orchestrator/src/get_with_retry.rs @@ -1,22 +1,22 @@ //! Basic utility functions to stubbornly get data -use clarity::Uint256; use cosmos_gravity::query::get_last_event_nonce; use deep_space::address::Address as CosmosAddress; +use ethereum_gravity::types::EthClient; +use ethers::prelude::*; use gravity_proto::gravity::query_client::QueryClient as GravityQueryClient; use std::time::Duration; use tokio::time::sleep as delay_for; use tonic::transport::Channel; -use web30::client::Web3; pub const RETRY_TIME: Duration = Duration::from_secs(5); /// gets the current block number, no matter how long it takes -pub async fn get_block_number_with_retry(web3: &Web3) -> Uint256 { - let mut res = web3.eth_block_number().await; +pub async fn get_block_number_with_retry(eth_client: EthClient) -> U64 { + let mut res = eth_client.get_block_number().await; while res.is_err() { error!("Failed to get latest block! Is your Eth node working?"); delay_for(RETRY_TIME).await; - res = web3.eth_block_number().await; + res = eth_client.get_block_number().await; } res.unwrap() } @@ -39,12 +39,12 @@ pub async fn get_last_event_nonce_with_retry( } /// gets the net version, no matter how long it takes -pub async fn get_net_version_with_retry(web3: &Web3) -> u64 { - let mut res = web3.net_version().await; +pub async fn get_chain_id_with_retry(eth_client: EthClient) -> U256 { + let mut res = eth_client.get_chainid().await; while res.is_err() { error!("Failed to get net version! Is your Eth node working?"); delay_for(RETRY_TIME).await; - res = web3.net_version().await; + res = eth_client.get_chainid().await; } res.unwrap() } diff --git a/orchestrator/orchestrator/src/lib.rs b/orchestrator/orchestrator/src/lib.rs index 1c363fa52..c5bb6ecf5 100644 --- a/orchestrator/orchestrator/src/lib.rs +++ b/orchestrator/orchestrator/src/lib.rs @@ -1,3 +1,14 @@ +//! Orchestrator is a sort of specialized relayer for the Gravity bridge that runs on every validator. +//! Things this library is responsible for +//! * Performing all the Ethereum signing required to submit updates and generate batches +//! * Progressing the validator set update generation process. +//! * Observing events on the Ethereum chain and submitting oracle messages for validator consensus +//! Things this library needs +//! * Access to the validators signing Ethereum key +//! * Access to the validators Cosmos key +//! * Access to an Cosmos chain RPC server +//! * Access to an Ethereum chain RPC server + pub mod ethereum_event_watcher; pub mod get_with_retry; pub mod main_loop; diff --git a/orchestrator/orchestrator/src/main.rs b/orchestrator/orchestrator/src/main.rs deleted file mode 100644 index 5d30f6b0c..000000000 --- a/orchestrator/orchestrator/src/main.rs +++ /dev/null @@ -1,165 +0,0 @@ -//! Orchestrator is a sort of specialized relayer for Althea-Gravity that runs on every validator. -//! Things this binary is responsible for -//! * Performing all the Ethereum signing required to submit updates and generate batches -//! * Progressing the validator set update generation process. -//! * Observing events on the Ethereum chain and submitting oracle messages for validator consensus -//! Things this binary needs -//! * Access to the validators signing Ethereum key -//! * Access to the validators Cosmos key -//! * Access to an Cosmos chain RPC server -//! * Access to an Ethereum chain RPC server - -#[macro_use] -extern crate serde_derive; -#[macro_use] -extern crate lazy_static; -#[macro_use] -extern crate log; - -mod ethereum_event_watcher; -mod get_with_retry; -mod main_loop; -mod metrics; -mod oracle_resync; - -use crate::main_loop::orchestrator_main_loop; -use clarity::Address as EthAddress; -use clarity::PrivateKey as EthPrivateKey; -use deep_space::private_key::PrivateKey as CosmosPrivateKey; -use docopt::Docopt; -use env_logger::Env; -use gravity_utils::connection_prep::{ - check_delegate_addresses, check_for_eth, wait_for_cosmos_node_ready, -}; -use gravity_utils::connection_prep::{check_for_fee_denom, create_rpc_connections}; -use main_loop::{ETH_ORACLE_LOOP_SPEED, ETH_SIGNER_LOOP_SPEED}; -use relayer::main_loop::LOOP_SPEED as RELAYER_LOOP_SPEED; -use std::cmp::min; - -#[derive(Debug, Deserialize)] -struct Args { - flag_cosmos_phrase: String, - flag_ethereum_key: String, - flag_cosmos_grpc: String, - flag_address_prefix: String, - flag_ethereum_rpc: String, - flag_contract_address: String, - flag_fees: String, - flag_metrics_listen: String, -} - -lazy_static! { - pub static ref USAGE: String = format!( - "Usage: {} --cosmos-phrase= --ethereum-key= --cosmos-grpc= --address-prefix= --ethereum-rpc= --fees= --contract-address= --metrics-listen= - Options: - -h --help Show this screen. - --cosmos-phrase= The mnenmonic of the Cosmos account key of the validator - --ethereum-key= The Ethereum private key of the validator - --cosmos-grpc= The Cosmos gRPC url, usually the validator - --address-prefix= The prefix for addresses on this Cosmos chain - --ethereum-rpc= The Ethereum RPC url, should be a self hosted node - --fees= The Cosmos Denom in which to pay Cosmos chain fees - --contract-address= The Ethereum contract address for Gravity, this is temporary - --metrics-listen= The address metrics server listens on [default: 127.0.0.1:3000]. - About: - The Validator companion binary for Gravity. This must be run by all Gravity chain validators - and is a mix of a relayer + oracle + ethereum signing infrastructure - Written By: {} - Version {}", - env!("CARGO_PKG_NAME"), - env!("CARGO_PKG_AUTHORS"), - env!("CARGO_PKG_VERSION"), - ); -} - -#[actix_rt::main] -async fn main() { - env_logger::Builder::from_env(Env::default().default_filter_or("info")).init(); - // On Linux static builds we need to probe ssl certs path to be able to - // do TLS stuff. - openssl_probe::init_ssl_cert_env_vars(); - - let args: Args = Docopt::new(USAGE.as_str()) - .and_then(|d| d.deserialize()) - .unwrap_or_else(|e| e.exit()); - let cosmos_key = CosmosPrivateKey::from_phrase(&args.flag_cosmos_phrase, "") - .expect("Invalid Private Cosmos Key!"); - let ethereum_key: EthPrivateKey = args - .flag_ethereum_key - .parse() - .expect("Invalid Ethereum private key!"); - let contract_address: EthAddress = args - .flag_contract_address - .parse() - .expect("Invalid contract address!"); - let metrics_listen = args - .flag_metrics_listen - .parse() - .expect("Invalid metrics listen address!"); - - let fee_denom = args.flag_fees; - - let timeout = min( - min(ETH_SIGNER_LOOP_SPEED, ETH_ORACLE_LOOP_SPEED), - RELAYER_LOOP_SPEED, - ); - - trace!("Probing RPC connections"); - // probe all rpc connections and see if they are valid - let connections = create_rpc_connections( - args.flag_address_prefix, - Some(args.flag_cosmos_grpc), - Some(args.flag_ethereum_rpc), - timeout, - ) - .await; - - let mut grpc = connections.grpc.clone().unwrap(); - let contact = connections.contact.clone().unwrap(); - let web3 = connections.web3.clone().unwrap(); - - let public_eth_key = ethereum_key - .to_public_key() - .expect("Invalid Ethereum Private Key!"); - let public_cosmos_key = cosmos_key.to_address(&contact.get_prefix()).unwrap(); - info!("Starting Gravity Validator companion binary Relayer + Oracle + Eth Signer"); - info!( - "Ethereum Address: {} Cosmos Address {}", - public_eth_key, public_cosmos_key - ); - - // check if the cosmos node is syncing, if so wait for it - // we can't move any steps above this because they may fail on an incorrect - // historic chain state while syncing occurs - wait_for_cosmos_node_ready(&contact).await; - - // check if the delegate addresses are correctly configured - check_delegate_addresses( - &mut grpc, - public_eth_key, - public_cosmos_key, - &contact.get_prefix(), - ) - .await; - - // check if we actually have the promised balance of tokens to pay fees - check_for_fee_denom(&fee_denom, public_cosmos_key, &contact).await; - check_for_eth(public_eth_key, &web3).await; - - orchestrator_main_loop( - cosmos_key, - ethereum_key, - connections.web3.unwrap(), - connections.contact.unwrap(), - connections.grpc.unwrap(), - contract_address, - (1f64, fee_denom.to_owned()), - &metrics_listen, - 1f32, - 5000u128, - 1.0f64, - false, - 5, - ) - .await; -} diff --git a/orchestrator/orchestrator/src/main_loop.rs b/orchestrator/orchestrator/src/main_loop.rs index e04a4eef2..907961cb0 100644 --- a/orchestrator/orchestrator/src/main_loop.rs +++ b/orchestrator/orchestrator/src/main_loop.rs @@ -1,14 +1,13 @@ -//! This file contains the main loops for two distinct functions that just happen to reside int his same binary for ease of use. The Ethereum Signer and the Ethereum Oracle are both roles in Gravity -//! that can only be run by a validator. This single binary the 'Orchestrator' runs not only these two rules but also the untrusted role of a relayer, that does not need any permissions and has it's -//! own crate and binary so that anyone may run it. +//! This file contains the main loops for two distinct functions that just happen to reside in this same binary for ease of use. +//! The Ethereum Signer and the Ethereum Oracle are both roles in Gravity that can only be run by a validator. This single binary +//! the 'Orchestrator' runs not only these two roles but also the untrusted role of a relayer, that does not need any permissions +//! and has its own crate and binary so that anyone may run it. use crate::metrics; use crate::{ ethereum_event_watcher::check_for_events, metrics::metrics_main_loop, oracle_resync::get_last_checked_block, }; -use clarity::{address::Address as EthAddress, Uint256}; -use clarity::{utils::bytes_to_hex_str, PrivateKey as EthPrivateKey}; use cosmos_gravity::send::send_main_loop; use cosmos_gravity::{ build, @@ -21,14 +20,16 @@ use deep_space::client::ChainStatus; use deep_space::error::CosmosGrpcError; use deep_space::private_key::PrivateKey as CosmosPrivateKey; use deep_space::{Contact, Msg}; +use ethereum_gravity::types::EthClient; use ethereum_gravity::utils::get_gravity_id; +use ethers::{prelude::*, types::Address as EthAddress}; use gravity_proto::gravity::query_client::QueryClient as GravityQueryClient; +use gravity_utils::ethereum::bytes_to_hex_str; use relayer::main_loop::relayer_main_loop; use std::convert::TryInto; use std::{net, time::Duration}; use tokio::time::sleep as delay_for; use tonic::transport::Channel; -use web30::client::Web3; /// The execution speed governing all loops in this file /// which is to say all loops started by Orchestrator main @@ -45,15 +46,14 @@ pub const ETH_ORACLE_LOOP_SPEED: Duration = Duration::from_secs(13); #[allow(clippy::too_many_arguments)] pub async fn orchestrator_main_loop( cosmos_key: CosmosPrivateKey, - ethereum_key: EthPrivateKey, - web3: Web3, contact: Contact, + eth_client: EthClient, grpc_client: GravityQueryClient, gravity_contract_address: EthAddress, gas_price: (f64, String), metrics_listen: &net::SocketAddr, - eth_gas_multiplier: f32, - blocks_to_search: u128, + eth_gas_price_multiplier: f32, + blocks_to_search: u64, gas_adjustment: f64, relayer_opt_out: bool, cosmos_msg_batch_size: u32, @@ -71,8 +71,8 @@ pub async fn orchestrator_main_loop( let b = eth_oracle_main_loop( cosmos_key, - web3.clone(), contact.clone(), + eth_client.clone(), grpc_client.clone(), gravity_contract_address, blocks_to_search, @@ -81,9 +81,8 @@ pub async fn orchestrator_main_loop( let c = eth_signer_main_loop( cosmos_key, - ethereum_key, - web3.clone(), contact.clone(), + eth_client.clone(), grpc_client.clone(), gravity_contract_address, tx.clone(), @@ -93,11 +92,10 @@ pub async fn orchestrator_main_loop( if !relayer_opt_out { let e = relayer_main_loop( - ethereum_key, - web3, + eth_client.clone(), grpc_client.clone(), gravity_contract_address, - eth_gas_multiplier, + eth_gas_price_multiplier, ); futures::future::join5(a, b, c, d, e).await; } else { @@ -112,20 +110,19 @@ const DELAY: Duration = Duration::from_secs(5); #[allow(unused_variables)] pub async fn eth_oracle_main_loop( cosmos_key: CosmosPrivateKey, - web3: Web3, contact: Contact, + eth_client: EthClient, grpc_client: GravityQueryClient, gravity_contract_address: EthAddress, - blocks_to_search: u128, + blocks_to_search: u64, msg_sender: tokio::sync::mpsc::Sender>, ) { let our_cosmos_address = cosmos_key.to_address(&contact.get_prefix()).unwrap(); - let long_timeout_web30 = Web3::new(&web3.get_url(), Duration::from_secs(120)); - let mut last_checked_block: Uint256 = get_last_checked_block( + let mut last_checked_block = get_last_checked_block( grpc_client.clone(), our_cosmos_address, gravity_contract_address, - &long_timeout_web30, + eth_client.clone(), blocks_to_search, ) .await; @@ -135,12 +132,12 @@ pub async fn eth_oracle_main_loop( loop { let (async_resp, _) = tokio::join!( async { - let latest_eth_block = web3.eth_block_number().await; + let latest_eth_block = eth_client.get_block_number().await; let latest_cosmos_block = contact.get_chain_status().await; match (latest_eth_block, latest_cosmos_block) { (Ok(latest_eth_block), Ok(ChainStatus::Moving { block_height })) => { metrics::set_cosmos_block_height(block_height.clone()); - metrics::set_ethereum_block_height(latest_eth_block.clone()); + metrics::set_ethereum_block_height(latest_eth_block.as_u64()); trace!( "Latest Eth block {} Latest Cosmos block {}", latest_eth_block, @@ -175,7 +172,7 @@ pub async fn eth_oracle_main_loop( // Relays events from Ethereum -> Cosmos match check_for_events( - &web3, + eth_client.clone(), &contact, &mut grpc_client, gravity_contract_address, @@ -208,18 +205,16 @@ pub async fn eth_oracle_main_loop( #[allow(unused_variables)] pub async fn eth_signer_main_loop( cosmos_key: CosmosPrivateKey, - ethereum_key: EthPrivateKey, - web3: Web3, contact: Contact, + eth_client: EthClient, grpc_client: GravityQueryClient, contract_address: EthAddress, msg_sender: tokio::sync::mpsc::Sender>, ) { let our_cosmos_address = cosmos_key.to_address(&contact.get_prefix()).unwrap(); - let our_ethereum_address = ethereum_key.to_public_key().unwrap(); let mut grpc_client = grpc_client; - let gravity_id = get_gravity_id(contract_address, our_ethereum_address, &web3).await; + let gravity_id = get_gravity_id(contract_address, eth_client.clone()).await; if gravity_id.is_err() { error!("Failed to get GravityID, check your Eth node"); return; @@ -229,12 +224,12 @@ pub async fn eth_signer_main_loop( loop { let (async_resp, _) = tokio::join!( async { - let latest_eth_block = web3.eth_block_number().await; + let latest_eth_block = eth_client.get_block_number().await; let latest_cosmos_block = contact.get_chain_status().await; match (latest_eth_block, latest_cosmos_block) { (Ok(latest_eth_block), Ok(ChainStatus::Moving { block_height })) => { metrics::set_cosmos_block_height(block_height.clone()); - metrics::set_ethereum_block_height(latest_eth_block.clone()); + metrics::set_ethereum_block_height(latest_eth_block.as_u64()); trace!( "Latest Eth block {} Latest Cosmos block {}", latest_eth_block, @@ -280,11 +275,12 @@ pub async fn eth_signer_main_loop( ); let messages = build::signer_set_tx_confirmation_messages( &contact, - ethereum_key, + eth_client.clone(), valsets, cosmos_key, gravity_id.clone(), - ); + ) + .await; msg_sender .send(messages) .await @@ -294,7 +290,7 @@ pub async fn eth_signer_main_loop( Err(e) => { metrics::UNSIGNED_VALSET_FAILURES.inc(); error!( - "Failed to get unsigned valsets, check your Cosmos gRPC {:?}", + "Failed to get unsigned valset, check your Cosmos gRPC {:?}", e ); } @@ -315,11 +311,12 @@ pub async fn eth_signer_main_loop( let transaction_batches = vec![last_unsigned_batch]; let messages = build::batch_tx_confirmation_messages( &contact, - ethereum_key, + eth_client.clone(), transaction_batches, cosmos_key, gravity_id.clone(), - ); + ) + .await; msg_sender .send(messages) .await @@ -347,11 +344,12 @@ pub async fn eth_signer_main_loop( let logic_calls = vec![logic_call]; let messages = build::contract_call_tx_confirmation_messages( &contact, - ethereum_key, + eth_client.clone(), logic_calls, cosmos_key, gravity_id.clone(), - ); + ) + .await; msg_sender .send(messages) .await @@ -362,7 +360,7 @@ pub async fn eth_signer_main_loop( error!( "Failed to get unsigned Logic Calls, check your Cosmos gRPC {:?}", e - ); + ) } }, delay_for(ETH_SIGNER_LOOP_SPEED) @@ -370,9 +368,11 @@ pub async fn eth_signer_main_loop( } } -#[allow(dead_code)] -pub async fn check_for_eth(orchestrator_address: EthAddress, web3: Web3) { - let balance = web3.eth_get_balance(orchestrator_address).await.unwrap(); +pub async fn check_for_eth(orchestrator_address: EthAddress, eth_client: EthClient) { + let balance = eth_client + .get_balance(orchestrator_address, None) + .await + .unwrap(); if balance == 0u8.into() { warn!("You don't have any Ethereum! You will need to send some to {} for this program to work. Dust will do for basic operations, more info about average relaying costs will be presented as the program runs", orchestrator_address); } diff --git a/orchestrator/orchestrator/src/metrics.rs b/orchestrator/orchestrator/src/metrics.rs index 09fdff5a3..73ec306ee 100644 --- a/orchestrator/orchestrator/src/metrics.rs +++ b/orchestrator/orchestrator/src/metrics.rs @@ -1,6 +1,7 @@ use std::{convert::TryInto, net}; use axum::prelude::*; +use ethers::prelude::*; use hyper::Server; use lazy_static::lazy_static; use prometheus::*; @@ -63,7 +64,7 @@ lazy_static! { .unwrap(); } -// Guages (guarded by setters) +// Gauges (guarded by setters) lazy_static! { static ref COSMOS_BLOCK_HEIGHT: IntGauge = register_int_gauge!(opts!( "cosmos_block_height", @@ -177,77 +178,77 @@ pub fn set_cosmos_last_event_nonce(v: u64) { set_u64(&COSMOS_LAST_EVENT_NONCE, v); } -pub fn set_ethereum_block_height(v: clarity::Uint256) { - set_uint256(ÐEREUM_BLOCK_HEIGHT, v); +pub fn set_ethereum_block_height(v: u64) { + set_u64(ÐEREUM_BLOCK_HEIGHT, v); } -pub fn set_ethereum_check_for_events_end_block(v: clarity::Uint256) { - set_uint256(ÐEREUM_CHECK_FOR_EVENTS_END_BLOCK, v); +pub fn set_ethereum_check_for_events_end_block(v: u64) { + set_u64(ÐEREUM_CHECK_FOR_EVENTS_END_BLOCK, v); } -pub fn set_ethereum_check_for_events_starting_block(v: clarity::Uint256) { - set_uint256(ÐEREUM_CHECK_FOR_EVENTS_STARTING_BLOCK, v); +pub fn set_ethereum_check_for_events_starting_block(v: u64) { + set_u64(ÐEREUM_CHECK_FOR_EVENTS_STARTING_BLOCK, v); } -pub fn set_ethereum_last_batch_event(v: clarity::Uint256) { - set_uint256(ÐEREUM_LAST_BATCH_EVENT, v.clone()); - set_uint256(ÐEREUM_LAST_EVENT_NONCE, v); +pub fn set_ethereum_last_batch_event(v: U256) { + set_u256(ÐEREUM_LAST_BATCH_EVENT, v); + set_u256(ÐEREUM_LAST_EVENT_NONCE, v); } -pub fn set_ethereum_last_batch_nonce(v: clarity::Uint256) { - set_uint256(ÐEREUM_LAST_BATCH_NONCE, v); +pub fn set_ethereum_last_batch_nonce(v: U256) { + set_u256(ÐEREUM_LAST_BATCH_NONCE, v); } -pub fn set_ethereum_last_deposit_block(v: clarity::Uint256) { - set_uint256(ÐEREUM_LAST_DEPOSIT_BLOCK, v); +pub fn set_ethereum_last_deposit_block(v: U256) { + set_u256(ÐEREUM_LAST_DEPOSIT_BLOCK, v); } -pub fn set_ethereum_last_deposit_event(v: clarity::Uint256) { - set_uint256(ÐEREUM_LAST_DEPOSIT_EVENT, v.clone()); - set_uint256(ÐEREUM_LAST_EVENT_NONCE, v); +pub fn set_ethereum_last_deposit_event(v: U256) { + set_u256(ÐEREUM_LAST_DEPOSIT_EVENT, v); + set_u256(ÐEREUM_LAST_EVENT_NONCE, v); } -pub fn set_ethereum_last_erc20_block(v: clarity::Uint256) { - set_uint256(ÐEREUM_LAST_ERC20_BLOCK, v); +pub fn set_ethereum_last_erc20_block(v: U256) { + set_u256(ÐEREUM_LAST_ERC20_BLOCK, v); } -pub fn set_ethereum_last_erc20_event(v: clarity::Uint256) { - set_uint256(ÐEREUM_LAST_ERC20_EVENT, v.clone()); - set_uint256(ÐEREUM_LAST_EVENT_NONCE, v); +pub fn set_ethereum_last_erc20_event(v: U256) { + set_u256(ÐEREUM_LAST_ERC20_EVENT, v); + set_u256(ÐEREUM_LAST_EVENT_NONCE, v); } -pub fn set_ethereum_last_logic_call_event(v: clarity::Uint256) { - set_uint256(ÐEREUM_LAST_LOGIC_CALL_EVENT, v.clone()); - set_uint256(ÐEREUM_LAST_EVENT_NONCE, v); +pub fn set_ethereum_last_logic_call_event(v: U256) { + set_u256(ÐEREUM_LAST_LOGIC_CALL_EVENT, v); + set_u256(ÐEREUM_LAST_EVENT_NONCE, v); } -pub fn set_ethereum_last_logic_call_nonce(v: clarity::Uint256) { - set_uint256(ÐEREUM_LAST_LOGIC_CALL_NONCE, v); +pub fn set_ethereum_last_logic_call_nonce(v: U256) { + set_u256(ÐEREUM_LAST_LOGIC_CALL_NONCE, v); } -pub fn set_ethereum_last_valset_event(v: clarity::Uint256) { - set_uint256(ÐEREUM_LAST_VALSET_EVENT, v.clone()); - set_uint256(ÐEREUM_LAST_EVENT_NONCE, v); +pub fn set_ethereum_last_valset_event(v: U256) { + set_u256(ÐEREUM_LAST_VALSET_EVENT, v); + set_u256(ÐEREUM_LAST_EVENT_NONCE, v); } -pub fn set_ethereum_last_valset_nonce(v: clarity::Uint256) { - set_uint256(ÐEREUM_LAST_VALSET_NONCE, v); +pub fn set_ethereum_last_valset_nonce(v: U256) { + set_u256(ÐEREUM_LAST_VALSET_NONCE, v); } -pub fn set_ethereum_bal(v: clarity::Uint256) { - set_uint256(ÐEREUM_BAL, v); +pub fn set_ethereum_bal(v: U256) { + set_u256(ÐEREUM_BAL, v); } -fn set_u64(guage: &IntGauge, value: u64) { +fn set_u64(gauge: &IntGauge, value: u64) { let v = value.try_into().unwrap_or(-1); - if v > guage.get() { - guage.set(v); + if v > gauge.get() { + gauge.set(v); } } -fn set_uint256(guage: &IntGauge, value: clarity::Uint256) { - let v = value.to_str_radix(10).parse().unwrap_or(-1); - if v > guage.get() { - guage.set(v); +fn set_u256(gauge: &IntGauge, value: U256) { + let v = value.to_string().parse().unwrap_or(-1); + if v > gauge.get() { + gauge.set(v); } } diff --git a/orchestrator/orchestrator/src/oracle_resync.rs b/orchestrator/orchestrator/src/oracle_resync.rs index 124088db9..9606d05cf 100644 --- a/orchestrator/orchestrator/src/oracle_resync.rs +++ b/orchestrator/orchestrator/src/oracle_resync.rs @@ -1,13 +1,16 @@ -use clarity::{Address, Uint256}; use deep_space::address::Address as CosmosAddress; +use ethereum_gravity::types::EthClient; +use ethers::prelude::*; +use ethers::types::Address as EthAddress; +use gravity_abi::gravity::*; use gravity_proto::gravity::query_client::QueryClient as GravityQueryClient; use gravity_utils::types::{ Erc20DeployedEvent, LogicCallExecutedEvent, SendToCosmosEvent, TransactionBatchExecutedEvent, ValsetUpdatedEvent, }; +use gravity_utils::types::{FromLog, FromLogWithPrefix}; use tokio::time::sleep as delay_for; use tonic::transport::Channel; -use web30::client::Web3; use crate::get_with_retry::get_block_number_with_retry; use crate::get_with_retry::get_last_event_nonce_with_retry; @@ -18,14 +21,14 @@ use crate::get_with_retry::RETRY_TIME; pub async fn get_last_checked_block( grpc_client: GravityQueryClient, our_cosmos_address: CosmosAddress, - gravity_contract_address: Address, - web3: &Web3, - blocks_to_search: u128, -) -> Uint256 { + gravity_contract_address: EthAddress, + eth_client: EthClient, + blocks_to_search: u64, +) -> U64 { + // TODO(bolten): original version of this used a 120 second timeout when querying + // the eth chain, should we replicate that in eth_client? let mut grpc_client = grpc_client; - - let latest_block = get_block_number_with_retry(web3).await; - let mut last_event_nonce: Uint256 = + let mut last_event_nonce: U256 = get_last_event_nonce_with_retry(&mut grpc_client, our_cosmos_address) .await .into(); @@ -37,143 +40,139 @@ pub async fn get_last_checked_block( last_event_nonce = 1u8.into(); } - let mut current_block: Uint256 = latest_block.clone(); + let filter_gravity_contract_address = ValueOrArray::Value(gravity_contract_address); + + // TODO(bolten): there might be a cleaner way to get these event ABI signature strings + // from the generated ABI files, should look into that + let mut erc20_deployed_filter = Filter::new() + .address(filter_gravity_contract_address.clone()) + .event(&Erc20DeployedEventFilter::abi_signature()); + let mut logic_call_filter = Filter::new() + .address(filter_gravity_contract_address.clone()) + .event(&LogicCallEventFilter::abi_signature()); + let mut send_to_cosmos_filter = Filter::new() + .address(filter_gravity_contract_address.clone()) + .event(&SendToCosmosEventFilter::abi_signature()); + let mut transaction_batch_filter = Filter::new() + .address(filter_gravity_contract_address.clone()) + .event(&TransactionBatchExecutedEventFilter::abi_signature()); + let mut valset_updated_filter = Filter::new() + .address(filter_gravity_contract_address.clone()) + .event(&ValsetUpdatedEventFilter::abi_signature()); + + let mut end_search_block = get_block_number_with_retry(eth_client.clone()).await; + let blocks_to_search: U64 = blocks_to_search.into(); - while current_block.clone() > 0u8.into() { + while end_search_block > 0u8.into() { info!( "Oracle is resyncing, looking back into the history to find our last event nonce {}, on block {}", - last_event_nonce, current_block + last_event_nonce, end_search_block ); - let end_search = if current_block.clone() < blocks_to_search.into() { - 0u8.into() - } else { - current_block.clone() - blocks_to_search.into() - }; - let batch_events = web3 - .check_for_events( - end_search.clone(), - Some(current_block.clone()), - vec![gravity_contract_address], - vec!["TransactionBatchExecutedEvent(uint256,address,uint256)"], - ) - .await; - let send_to_cosmos_events = web3 - .check_for_events( - end_search.clone(), - Some(current_block.clone()), - vec![gravity_contract_address], - vec!["SendToCosmosEvent(address,address,bytes32,uint256,uint256)"], - ) - .await; - let erc20_deployed_events = web3 - .check_for_events( - end_search.clone(), - Some(current_block.clone()), - vec![gravity_contract_address], - vec!["ERC20DeployedEvent(string,address,string,string,uint8,uint256)"], - ) - .await; - let logic_call_executed_events = web3 - .check_for_events( - end_search.clone(), - Some(current_block.clone()), - vec![gravity_contract_address], - vec!["LogicCallEvent(bytes32,uint256,bytes,uint256)"], - ) - .await; - - // valset update events have one special property - // that is useful to us in this handler a valset update event for nonce 0 is emitted - // in the contract constructor meaning once you find that event you can exit the search - // with confidence that you have not missed any events without searching the entire blockchain - // history - let valset_events = web3 - .check_for_events( - end_search.clone(), - Some(current_block.clone()), - vec![gravity_contract_address], - vec!["ValsetUpdatedEvent(uint256,uint256,address[],uint256[])"], - ) - .await; - if batch_events.is_err() + + let start_search_block = end_search_block.saturating_sub(blocks_to_search); + let search_range = start_search_block..end_search_block; + + // select uses an inclusive version of the range + erc20_deployed_filter = erc20_deployed_filter.select(search_range.clone()); + logic_call_filter = logic_call_filter.select(search_range.clone()); + send_to_cosmos_filter = send_to_cosmos_filter.select(search_range.clone()); + transaction_batch_filter = transaction_batch_filter.select(search_range.clone()); + valset_updated_filter = valset_updated_filter.select(search_range.clone()); + + let erc20_deployed_events = eth_client.get_logs(&erc20_deployed_filter).await; + let logic_call_events = eth_client.get_logs(&logic_call_filter).await; + let send_to_cosmos_events = eth_client.get_logs(&send_to_cosmos_filter).await; + let transaction_batch_events = eth_client.get_logs(&transaction_batch_filter).await; + let valset_updated_events = eth_client.get_logs(&valset_updated_filter).await; + + // valset update events have one special property that is useful to us in this handler: + // a valset update event for nonce 0 is emitted in the contract constructor meaning once you + // find that event you can exit the search with confidence that you have not missed any events + // without searching the entire blockchain history + + if erc20_deployed_events.is_err() + || logic_call_events.is_err() || send_to_cosmos_events.is_err() - || valset_events.is_err() - || erc20_deployed_events.is_err() - || logic_call_executed_events.is_err() + || transaction_batch_events.is_err() + || valset_updated_events.is_err() { - error!("Failed to get blockchain events while resyncing, is your Eth node working? If you see only one of these it's fine",); + error!("Failed to get blockchain events while resyncing, is your Eth node working? If you see only one of these it's fine"); delay_for(RETRY_TIME).await; continue; } - let batch_events = batch_events.unwrap(); - let send_to_cosmos_events = send_to_cosmos_events.unwrap(); - let mut valset_events = valset_events.unwrap(); + let erc20_deployed_events = erc20_deployed_events.unwrap(); - let logic_call_executed_events = logic_call_executed_events.unwrap(); + let logic_call_events = logic_call_events.unwrap(); + let send_to_cosmos_events = send_to_cosmos_events.unwrap(); + let transaction_batch_events = transaction_batch_events.unwrap(); + let mut valset_updated_events = valset_updated_events.unwrap(); // look for and return the block number of the event last seen on the Cosmos chain // then we will play events from that block (including that block, just in case // there is more than one event there) onwards. We use valset nonce 0 as an indicator // of what block the contract was deployed on. - for event in batch_events { - match TransactionBatchExecutedEvent::from_log(&event) { - Ok(batch) => { + for event in erc20_deployed_events { + match Erc20DeployedEvent::from_log(&event) { + Ok(deploy) => { trace!( - "{} batch event nonce {} last event nonce", - batch.event_nonce, + "{} deploy event nonce {} last event nonce", + deploy.event_nonce, last_event_nonce ); - if batch.event_nonce == last_event_nonce && event.block_number.is_some() { + if deploy.event_nonce == last_event_nonce && event.block_number.is_some() { return event.block_number.unwrap(); } } - Err(e) => error!("Got batch event that we can't parse {}", e), + Err(e) => error!("Got ERC20Deployed event that we can't parse {}", e), } } - for event in send_to_cosmos_events { - let prefix = our_cosmos_address.get_prefix(); - match SendToCosmosEvent::from_log(&event, &prefix) { - Ok(send) => { + + for event in logic_call_events { + match LogicCallExecutedEvent::from_log(&event) { + Ok(call) => { trace!( - "{} send event nonce {} last event nonce", - send.event_nonce, + "{} LogicCall event nonce {} last event nonce", + call.event_nonce, last_event_nonce ); - if send.event_nonce == last_event_nonce && event.block_number.is_some() { + if call.event_nonce == last_event_nonce && event.block_number.is_some() { return event.block_number.unwrap(); } } - Err(e) => error!("Got SendToCosmos event that we can't parse {}", e), + Err(e) => error!("Got ERC20Deployed event that we can't parse {}", e), } } - for event in erc20_deployed_events { - match Erc20DeployedEvent::from_log(&event) { - Ok(deploy) => { + + for event in send_to_cosmos_events { + let prefix = our_cosmos_address.get_prefix(); + match SendToCosmosEvent::from_log(&event, &prefix.as_str()) { + Ok(send) => { trace!( - "{} deploy event nonce {} last event nonce", - deploy.event_nonce, + "{} send event nonce {} last event nonce", + send.event_nonce, last_event_nonce ); - if deploy.event_nonce == last_event_nonce && event.block_number.is_some() { + if send.event_nonce == last_event_nonce && event.block_number.is_some() { return event.block_number.unwrap(); } } - Err(e) => error!("Got ERC20Deployed event that we can't parse {}", e), + Err(e) => error!("Got SendToCosmos event that we can't parse {}", e), } } - for event in logic_call_executed_events { - match LogicCallExecutedEvent::from_log(&event) { - Ok(call) => { + + for event in transaction_batch_events { + match TransactionBatchExecutedEvent::from_log(&event) { + Ok(batch) => { trace!( - "{} LogicCall event nonce {} last event nonce", - call.event_nonce, + "{} batch event nonce {} last event nonce", + batch.event_nonce, last_event_nonce ); - if call.event_nonce == last_event_nonce && event.block_number.is_some() { + if batch.event_nonce == last_event_nonce && event.block_number.is_some() { return event.block_number.unwrap(); } } - Err(e) => error!("Got ERC20Deployed event that we can't parse {}", e), + Err(e) => error!("Got batch event that we can't parse {}", e), } } @@ -182,8 +181,8 @@ pub async fn get_last_checked_block( // we will encounter the first validator sets first and exit early and incorrectly. // note that reversing everything won't actually get you that much of a performance gain // because this only involves events within the searching block range. - valset_events.reverse(); - for event in valset_events { + valset_updated_events.reverse(); + for event in valset_updated_events { match ValsetUpdatedEvent::from_log(&event) { Ok(valset) => { // if we've found this event it is the first possible event from the contract @@ -211,7 +210,8 @@ pub async fn get_last_checked_block( Err(e) => error!("Got valset event that we can't parse {}", e), } } - current_block = end_search; + + end_search_block = start_search_block.saturating_sub(1u8.into()); // filter ranges are inclusive, avoid searching same block } // we should exit above when we find the zero valset, if we have the wrong contract address through we could be at it a while as we go over diff --git a/orchestrator/register_delegate_keys/Cargo.toml b/orchestrator/register_delegate_keys/Cargo.toml index d7bdd0504..9281979c3 100644 --- a/orchestrator/register_delegate_keys/Cargo.toml +++ b/orchestrator/register_delegate_keys/Cargo.toml @@ -16,12 +16,13 @@ gravity_utils = {path = "../gravity_utils"} gravity_proto = {path = "../gravity_proto/"} deep_space ={git="https://github.com/iqlusioninc/deep_space/", branch="master"} +ethers = "0.6.1" contact = "0.4" serde_derive = "1.0" clarity = "0.4.11" docopt = "1" serde = "1.0" -actix-rt = "2.2.0" +actix-rt = "2.5" lazy_static = "1" web30 = "0.15" env_logger = "0.8" diff --git a/orchestrator/register_delegate_keys/src/main.rs b/orchestrator/register_delegate_keys/src/main.rs index e7b20ea69..79b418b62 100644 --- a/orchestrator/register_delegate_keys/src/main.rs +++ b/orchestrator/register_delegate_keys/src/main.rs @@ -5,14 +5,15 @@ extern crate serde_derive; #[macro_use] extern crate lazy_static; -use log::error; - use clarity::PrivateKey as EthPrivateKey; use cosmos_gravity::send::update_gravity_delegate_addresses; use deep_space::{mnemonic::Mnemonic, private_key::PrivateKey as CosmosPrivateKey}; use docopt::Docopt; +use ethers::core::k256::ecdsa::SigningKey; +use ethers::prelude::*; use gravity_utils::connection_prep::check_for_fee_denom; use gravity_utils::connection_prep::{create_rpc_connections, wait_for_cosmos_node_ready}; +use log::error; use rand::{thread_rng, Rng}; use std::time::Duration; @@ -106,14 +107,20 @@ async fn main() { key }; - let ethereum_address = ethereum_key.to_public_key().unwrap(); + // TODO(bolten): left clarity in place for the above bit because it seems like + // SigningKey/VerifyingKey don't implement the Display trait + let signing_key = SigningKey::from_bytes(ðereum_key.to_bytes()).unwrap(); + let ethereum_wallet = LocalWallet::from(signing_key); + + let ethereum_address = ethereum_wallet.address(); let cosmos_address = cosmos_key.to_address(&contact.get_prefix()).unwrap(); + let res = update_gravity_delegate_addresses( &contact, ethereum_address, cosmos_address, validator_key, - ethereum_key, + ethereum_wallet, (0f64, "".to_string()), 1.0f64, ) diff --git a/orchestrator/relayer/Cargo.toml b/orchestrator/relayer/Cargo.toml index 53388d3a5..791466925 100644 --- a/orchestrator/relayer/Cargo.toml +++ b/orchestrator/relayer/Cargo.toml @@ -15,18 +15,19 @@ path = "src/main.rs" [dependencies] ethereum_gravity = { path = "../ethereum_gravity" } cosmos_gravity = { path = "../cosmos_gravity" } +gravity_abi = { path = "../gravity_abi" } gravity_utils = { path = "../gravity_utils" } gravity_proto = { path = "../gravity_proto" } deep_space ={git="https://github.com/iqlusioninc/deep_space/", branch="master"} +ethers = "0.6.1" serde_derive = "1.0" clarity = "0.4.11" docopt = "1" serde = "1.0" -actix-rt = "2" +actix-rt = "2.5" lazy_static = "1" web30 = "0.15" -num256 = "0.3" log = "0.4" env_logger = "0.8" tokio = "1.4" @@ -35,4 +36,4 @@ openssl-probe = "0.1" [dev-dependencies] -actix = "0.11" +actix = "0.12" diff --git a/orchestrator/relayer/src/batch_relaying.rs b/orchestrator/relayer/src/batch_relaying.rs index 9603994b1..c6896c1c9 100644 --- a/orchestrator/relayer/src/batch_relaying.rs +++ b/orchestrator/relayer/src/batch_relaying.rs @@ -1,20 +1,18 @@ -use clarity::address::Address as EthAddress; -use clarity::PrivateKey as EthPrivateKey; -use clarity::Uint256; - use cosmos_gravity::query::get_latest_transaction_batches; use cosmos_gravity::query::get_transaction_batch_signatures; -use ethereum_gravity::utils::{downcast_to_u128, get_tx_batch_nonce}; -use ethereum_gravity::{one_eth, submit_batch::send_eth_transaction_batch}; +use ethereum_gravity::{ + one_eth_f32, submit_batch::send_eth_transaction_batch, types::EthClient, + utils::get_tx_batch_nonce, +}; +use ethers::prelude::*; +use ethers::types::Address as EthAddress; use gravity_proto::gravity::query_client::QueryClient as GravityQueryClient; +use gravity_utils::ethereum::downcast_to_f32; use gravity_utils::message_signatures::encode_tx_batch_confirm_hashed; -use gravity_utils::types::Valset; -use gravity_utils::types::{BatchConfirmResponse, TransactionBatch}; +use gravity_utils::types::{BatchConfirmResponse, TransactionBatch, Valset}; use std::collections::HashMap; use std::time::Duration; use tonic::transport::Channel; -use web30::client::Web3; -use web30::types::SendTxOption; #[derive(Debug, Clone)] struct SubmittableBatch { @@ -33,27 +31,25 @@ struct SubmittableBatch { pub async fn relay_batches( // the validator set currently in the contract on Ethereum current_valset: Valset, - ethereum_key: EthPrivateKey, - web3: &Web3, + eth_client: EthClient, grpc_client: &mut GravityQueryClient, gravity_contract_address: EthAddress, gravity_id: String, timeout: Duration, - gas_multiplier: f32, + eth_gas_price_multiplier: f32, ) { let possible_batches = get_batches_and_signatures(current_valset.clone(), grpc_client, gravity_id.clone()).await; - trace!("possible batches {:?}", possible_batches); + debug!("possible batches {:?}", possible_batches); submit_batches( current_valset, - ethereum_key, - web3, + eth_client.clone(), gravity_contract_address, gravity_id, timeout, - gas_multiplier, + eth_gas_price_multiplier, possible_batches, ) .await; @@ -77,13 +73,13 @@ async fn get_batches_and_signatures( } else { return HashMap::new(); }; - trace!("Latest batches {:?}", latest_batches); + debug!("Latest batches {:?}", latest_batches); let mut possible_batches = HashMap::new(); for batch in latest_batches { let sigs = get_transaction_batch_signatures(grpc_client, batch.nonce, batch.token_contract).await; - trace!("Got sigs {:?}", sigs); + debug!("Got sigs {:?}", sigs); if let Ok(sigs) = sigs { // this checks that the signatures for the batch are actually possible to submit to the chain let hash = encode_tx_batch_confirm_hashed(gravity_id.clone(), batch.clone()); @@ -131,19 +127,17 @@ async fn get_batches_and_signatures( #[allow(clippy::too_many_arguments)] async fn submit_batches( current_valset: Valset, - ethereum_key: EthPrivateKey, - web3: &Web3, + eth_client: EthClient, gravity_contract_address: EthAddress, gravity_id: String, timeout: Duration, - gas_multiplier: f32, + eth_gas_price_multiplier: f32, possible_batches: HashMap>, ) { - let our_ethereum_address = ethereum_key.to_public_key().unwrap(); - let ethereum_block_height = if let Ok(bn) = web3.eth_block_number().await { + let ethereum_block_height = if let Ok(bn) = eth_client.get_block_number().await { bn } else { - warn!("Failed to get eth block height, is your eth node working?"); + error!("Failed to get eth block height, is your eth node working?"); return; }; @@ -153,13 +147,8 @@ async fn submit_batches( // do that though. for (token_type, possible_batches) in possible_batches { let erc20_contract = token_type; - let latest_ethereum_batch = get_tx_batch_nonce( - gravity_contract_address, - erc20_contract, - our_ethereum_address, - web3, - ) - .await; + let latest_ethereum_batch = + get_tx_batch_nonce(gravity_contract_address, erc20_contract, eth_client.clone()).await; if latest_ethereum_batch.is_err() { error!( "Failed to get latest Ethereum batch with {:?}", @@ -173,8 +162,7 @@ async fn submit_batches( let oldest_signed_batch = batch.batch; let oldest_signatures = batch.sigs; - let timeout_height: Uint256 = oldest_signed_batch.batch_timeout.into(); - if timeout_height < ethereum_block_height { + if oldest_signed_batch.batch_timeout < ethereum_block_height.as_u64() { warn!( "Batch {}/{} has timed out and can not be submitted", oldest_signed_batch.nonce, oldest_signed_batch.token_contract @@ -188,39 +176,51 @@ async fn submit_batches( current_valset.clone(), oldest_signed_batch.clone(), &oldest_signatures, - web3, gravity_contract_address, gravity_id.clone(), - ethereum_key, + eth_client.clone(), ) .await; + if cost.is_err() { error!("Batch cost estimate failed with {:?}", cost); continue; } - let cost = cost.unwrap(); + + let mut cost = cost.unwrap(); + let total_cost = downcast_to_f32(cost.get_total()); + if total_cost.is_none() { + error!( + "Total gas cost greater than f32 max, skipping batch submission: {}", + oldest_signed_batch.nonce + ); + continue; + } + let total_cost = total_cost.unwrap(); + let gas_price_as_f32 = downcast_to_f32(cost.gas_price).unwrap(); // if the total cost isn't greater, this isn't + info!( - "We have detected latest batch {} but latest on Ethereum is {} This batch is estimated to cost {} Gas / {:.4} ETH to submit", - latest_cosmos_batch_nonce, - latest_ethereum_batch, - cost.gas_price.clone(), - downcast_to_u128(cost.get_total()).unwrap() as f32 - / downcast_to_u128(one_eth()).unwrap() as f32 - ); - let tx_options = vec![SendTxOption::GasPriceMultiplier(gas_multiplier)]; + "We have detected latest batch {} but latest on Ethereum is {} This batch is estimated to cost {} Gas / {:.4} ETH to submit", + latest_cosmos_batch_nonce, + latest_ethereum_batch, + cost.gas_price.clone(), + total_cost / one_eth_f32() + ); + + cost.gas_price = ((gas_price_as_f32 * eth_gas_price_multiplier) as u128).into(); let res = send_eth_transaction_batch( current_valset.clone(), oldest_signed_batch, &oldest_signatures, - web3, timeout, gravity_contract_address, gravity_id.clone(), - ethereum_key, - tx_options, + cost, + eth_client.clone(), ) .await; + if res.is_err() { info!("Batch submission failed with {:?}", res); } diff --git a/orchestrator/relayer/src/find_latest_valset.rs b/orchestrator/relayer/src/find_latest_valset.rs index 42f2b1c34..64b59425f 100644 --- a/orchestrator/relayer/src/find_latest_valset.rs +++ b/orchestrator/relayer/src/find_latest_valset.rs @@ -1,10 +1,12 @@ -use clarity::{Address, Uint256}; -use ethereum_gravity::utils::downcast_uint256; +use ethereum_gravity::types::EthClient; +use ethers::prelude::*; +use ethers::types::Address as EthAddress; +use gravity_abi::gravity::*; use gravity_proto::gravity::query_client::QueryClient as GravityQueryClient; -use gravity_utils::types::ValsetUpdatedEvent; -use gravity_utils::{error::GravityError, types::Valset}; +use gravity_utils::types::{FromLog, ValsetUpdatedEvent}; +use gravity_utils::{error::GravityError, ethereum::downcast_to_u64, types::Valset}; +use std::{panic, result::Result}; use tonic::transport::Channel; -use web30::client::Web3; /// This function finds the latest valset on the Gravity contract by looking back through the event /// history and finding the most recent ValsetUpdatedEvent. Most of the time this will be very fast @@ -13,44 +15,46 @@ use web30::client::Web3; /// this will take longer. pub async fn find_latest_valset( grpc_client: &mut GravityQueryClient, - gravity_contract_address: Address, - web3: &Web3, + gravity_contract_address: EthAddress, + eth_client: EthClient, ) -> Result { - const BLOCKS_TO_SEARCH: u128 = 5_000u128; - let latest_block = web3.eth_block_number().await?; - let mut current_block: Uint256 = latest_block.clone(); + // calculate some constant U64 values only once + const BLOCKS_TO_SEARCH: u64 = 5_000u64; - while current_block.clone() > 0u8.into() { - trace!( - "About to submit a Valset or Batch looking back into the history to find the last Valset Update, on block {}", - current_block - ); - let end_search = if current_block.clone() < BLOCKS_TO_SEARCH.into() { - 0u8.into() - } else { - current_block.clone() - BLOCKS_TO_SEARCH.into() - }; - let mut all_valset_events = web3 - .check_for_events( - end_search.clone(), - Some(current_block.clone()), - vec![gravity_contract_address], - vec!["ValsetUpdatedEvent(uint256,uint256,address[],uint256[])"], - ) - .await?; - // by default the lowest found valset goes first, we want the highest. - all_valset_events.reverse(); + let mut filter = Filter::new() + .address(ValueOrArray::Value(gravity_contract_address)) + .event(&ValsetUpdatedEventFilter::abi_signature()); + let mut end_filter_block = eth_client.get_block_number().await?; + + while end_filter_block > 0u64.into() { + debug!("About to submit a Valset or Batch, looking back into the history to find the last Valset Update, on block {}", end_filter_block); + + let start_filter_block = end_filter_block.saturating_sub(BLOCKS_TO_SEARCH.into()); + filter = filter.select(start_filter_block..end_filter_block); - trace!("Found events {:?}", all_valset_events); + let mut filtered_logged_events = eth_client.get_logs(&filter).await?; + filtered_logged_events.reverse(); // we'll process these in reverse order to start from the most recent and work backwards + + // TODO(bolten): the original logic only checked one valset event, even if there may have been multiple within the + // filtered blockspace...need more clarity on how severe an error it is if one of these events is malformed, and if + // we should return early with an error or just log it the way the previous version did + for logged_event in filtered_logged_events { + debug!("Found event {:?}", logged_event); + + match ValsetUpdatedEvent::from_log(&logged_event) { + Ok(valset_updated_event) => { + let downcast_nonce = downcast_to_u64(valset_updated_event.valset_nonce); + if downcast_nonce.is_none() { + error!( + "ValsetUpdatedEvent has nonce larger than u64: {:?}", + valset_updated_event + ); + continue; + } - // we take only the first event if we find any at all. - if !all_valset_events.is_empty() { - let event = &all_valset_events[0]; - match ValsetUpdatedEvent::from_log(event) { - Ok(event) => { let latest_eth_valset = Valset { - nonce: downcast_uint256(event.valset_nonce.clone()).unwrap(), - members: event.members, + nonce: downcast_nonce.unwrap(), + members: valset_updated_event.members, }; let cosmos_chain_valset = cosmos_gravity::query::get_valset(grpc_client, latest_eth_valset.nonce) @@ -61,7 +65,8 @@ pub async fn find_latest_valset( Err(e) => error!("Got valset event that we can't parse {}", e), } } - current_block = end_search; + + end_filter_block = start_filter_block.saturating_sub(1u64.into()); // filter ranges are inclusive, avoid searching same block } panic!("Could not find the last validator set for contract {}, probably not a valid Gravity contract!", gravity_contract_address) @@ -95,12 +100,12 @@ fn check_if_valsets_differ(cosmos_valset: Option, ethereum_valset: &Vals c_valset.sort(); e_valset.sort(); if c_valset == e_valset { - info!( + warn!( "Sorting disagreement between Cosmos and Ethereum on Valset nonce {}", ethereum_valset.nonce ); } else { - info!("Validator sets for nonce {} Cosmos and Ethereum differ. Possible bridge highjacking!", ethereum_valset.nonce) + error!("Validator sets for nonce {} Cosmos and Ethereum differ. Possible bridge highjacking!", ethereum_valset.nonce) } } } diff --git a/orchestrator/relayer/src/logic_call_relaying.rs b/orchestrator/relayer/src/logic_call_relaying.rs index 3ba3c6c86..e82fc4770 100644 --- a/orchestrator/relayer/src/logic_call_relaying.rs +++ b/orchestrator/relayer/src/logic_call_relaying.rs @@ -1,35 +1,29 @@ -use clarity::PrivateKey as EthPrivateKey; -use clarity::{address::Address as EthAddress, utils::bytes_to_hex_str}; use cosmos_gravity::query::{get_latest_logic_calls, get_logic_call_signatures}; -use ethereum_gravity::one_eth; +use ethereum_gravity::one_eth_f32; use ethereum_gravity::{ - logic_call::send_eth_logic_call, - utils::{downcast_to_u128, get_logic_call_nonce}, + logic_call::send_eth_logic_call, types::EthClient, utils::get_logic_call_nonce, }; +use ethers::types::Address as EthAddress; use gravity_proto::gravity::query_client::QueryClient as GravityQueryClient; +use gravity_utils::ethereum::{bytes_to_hex_str, downcast_to_f32}; use gravity_utils::types::{LogicCallConfirmResponse, Valset}; use gravity_utils::{message_signatures::encode_logic_call_confirm_hashed, types::LogicCall}; use std::time::Duration; use tonic::transport::Channel; -use web30::client::Web3; -use web30::types::SendTxOption; #[allow(clippy::too_many_arguments)] pub async fn relay_logic_calls( // the validator set currently in the contract on Ethereum current_valset: Valset, - ethereum_key: EthPrivateKey, - web3: &Web3, + eth_client: EthClient, grpc_client: &mut GravityQueryClient, gravity_contract_address: EthAddress, gravity_id: String, timeout: Duration, - gas_multiplier: f32, + eth_gas_price_multiplier: f32, ) { - let our_ethereum_address = ethereum_key.to_public_key().unwrap(); - let latest_calls = get_latest_logic_calls(grpc_client).await; - trace!("Latest Logic calls {:?}", latest_calls); + debug!("Latest Logic calls {:?}", latest_calls); if latest_calls.is_err() { return; } @@ -43,7 +37,7 @@ pub async fn relay_logic_calls( call.invalidation_nonce, ) .await; - trace!("Got sigs {:?}", sigs); + debug!("Got sigs {:?}", sigs); if let Ok(sigs) = sigs { let hash = encode_logic_call_confirm_hashed(gravity_id.clone(), call.clone()); // this checks that the signatures for the batch are actually possible to submit to the chain @@ -67,7 +61,7 @@ pub async fn relay_logic_calls( } } if oldest_signed_call.is_none() { - trace!("Could not find Call with signatures! exiting"); + debug!("Could not find Call with signatures! exiting"); return; } let oldest_signed_call = oldest_signed_call.unwrap(); @@ -76,8 +70,7 @@ pub async fn relay_logic_calls( let latest_ethereum_call = get_logic_call_nonce( gravity_contract_address, oldest_signed_call.invalidation_id.clone(), - our_ethereum_address, - web3, + eth_client.clone(), ) .await; if latest_ethereum_call.is_err() { @@ -94,39 +87,51 @@ pub async fn relay_logic_calls( current_valset.clone(), oldest_signed_call.clone(), &oldest_signatures, - web3, gravity_contract_address, gravity_id.clone(), - ethereum_key, + eth_client.clone(), ) .await; + if cost.is_err() { error!("LogicCall cost estimate failed with {:?}", cost); return; } - let cost = cost.unwrap(); + + let mut cost = cost.unwrap(); + let total_cost = downcast_to_f32(cost.get_total()); + if total_cost.is_none() { + error!( + "Total gas cost greater than f32 max, skipping logic call submission: {}", + oldest_signed_call.invalidation_nonce + ); + return; + } + let total_cost = total_cost.unwrap(); + let gas_price_as_f32 = downcast_to_f32(cost.gas_price).unwrap(); // if the total cost isn't greater, this isn't + info!( "We have detected latest LogicCall {} but latest on Ethereum is {} This LogicCall is estimated to cost {} Gas / {:.4} ETH to submit", latest_cosmos_call_nonce, latest_ethereum_call, cost.gas_price.clone(), - downcast_to_u128(cost.get_total()).unwrap() as f32 - / downcast_to_u128(one_eth()).unwrap() as f32 + total_cost / one_eth_f32(), ); - let tx_options = vec![SendTxOption::GasPriceMultiplier(gas_multiplier)]; + + cost.gas_price = ((gas_price_as_f32 * eth_gas_price_multiplier) as u128).into(); let res = send_eth_logic_call( current_valset, oldest_signed_call, &oldest_signatures, - web3, timeout, gravity_contract_address, gravity_id.clone(), - ethereum_key, - tx_options, + cost, + eth_client.clone(), ) .await; + if res.is_err() { info!("LogicCall submission failed with {:?}", res); } diff --git a/orchestrator/relayer/src/main.rs b/orchestrator/relayer/src/main.rs index f7ec3c4fe..f0c7f9e82 100644 --- a/orchestrator/relayer/src/main.rs +++ b/orchestrator/relayer/src/main.rs @@ -1,11 +1,15 @@ +use std::sync::Arc; + use crate::main_loop::relayer_main_loop; use crate::main_loop::LOOP_SPEED; -use clarity::Address as EthAddress; -use clarity::PrivateKey as EthPrivateKey; use docopt::Docopt; use env_logger::Env; -use gravity_utils::connection_prep::{ - check_for_eth, create_rpc_connections, wait_for_cosmos_node_ready, +use ethers::prelude::*; +use ethers::signers::LocalWallet as EthWallet; +use ethers::types::Address as EthAddress; +use gravity_utils::{ + connection_prep::{check_for_eth, create_rpc_connections, wait_for_cosmos_node_ready}, + ethereum::{downcast_to_u64, format_eth_address}, }; pub mod batch_relaying; @@ -63,7 +67,7 @@ async fn main() { let args: Args = Docopt::new(USAGE.as_str()) .and_then(|d| d.deserialize()) .unwrap_or_else(|e| e.exit()); - let ethereum_key: EthPrivateKey = args + let ethereum_wallet: EthWallet = args .flag_ethereum_key .parse() .expect("Invalid Ethereum private key!"); @@ -79,25 +83,32 @@ async fn main() { LOOP_SPEED, ) .await; + let provider = connections.eth_provider.clone().unwrap(); + let chain_id = provider + .get_chainid() + .await + .expect("Could not retrieve chain ID during relayer start"); + let chain_id = downcast_to_u64(chain_id).expect("Chain ID overflowed when downcasting to u64"); + let eth_client = SignerMiddleware::new( + provider, + ethereum_wallet.clone().with_chain_id(chain_id), + ); + let eth_client = Arc::new(eth_client); - let public_eth_key = ethereum_key - .to_public_key() - .expect("Invalid Ethereum Private Key!"); + let public_eth_key = eth_client.address(); info!("Starting Gravity Relayer"); - info!("Ethereum Address: {}", public_eth_key); + info!("Ethereum Address: {}", format_eth_address(public_eth_key)); let contact = connections.contact.clone().unwrap(); - let web3 = connections.web3.clone().unwrap(); // check if the cosmos node is syncing, if so wait for it // we can't move any steps above this because they may fail on an incorrect // historic chain state while syncing occurs wait_for_cosmos_node_ready(&contact).await; - check_for_eth(public_eth_key, &web3).await; + check_for_eth(public_eth_key, eth_client.clone()).await; relayer_main_loop( - ethereum_key, - connections.web3.unwrap(), + eth_client, connections.grpc.unwrap(), gravity_contract_address, 1f32, diff --git a/orchestrator/relayer/src/main_loop.rs b/orchestrator/relayer/src/main_loop.rs index b1ffc483c..897fd0a45 100644 --- a/orchestrator/relayer/src/main_loop.rs +++ b/orchestrator/relayer/src/main_loop.rs @@ -2,13 +2,11 @@ use crate::{ batch_relaying::relay_batches, find_latest_valset::find_latest_valset, logic_call_relaying::relay_logic_calls, valset_relaying::relay_valsets, }; -use clarity::address::Address as EthAddress; -use clarity::PrivateKey as EthPrivateKey; -use ethereum_gravity::utils::get_gravity_id; +use ethereum_gravity::{types::EthClient, utils::get_gravity_id}; +use ethers::types::Address as EthAddress; use gravity_proto::gravity::query_client::QueryClient as GravityQueryClient; use std::time::Duration; use tonic::transport::Channel; -use web30::client::Web3; pub const LOOP_SPEED: Duration = Duration::from_secs(17); @@ -16,36 +14,36 @@ pub const LOOP_SPEED: Duration = Duration::from_secs(17); /// it can be called in the test runner for easier orchestration of multi-node tests #[allow(unused_variables)] pub async fn relayer_main_loop( - ethereum_key: EthPrivateKey, - web3: Web3, + eth_client: EthClient, grpc_client: GravityQueryClient, gravity_contract_address: EthAddress, - gas_multiplier: f32, + eth_gas_price_multiplier: f32, ) { let mut grpc_client = grpc_client; + let gravity_id = get_gravity_id(gravity_contract_address, eth_client.clone()).await; + if gravity_id.is_err() { + error!("Failed to get GravityID, check your Eth node"); + return; + } + let gravity_id = gravity_id.unwrap(); loop { let (async_resp, _) = tokio::join!( async { - let our_ethereum_address = ethereum_key.to_public_key().unwrap(); - let current_eth_valset = - find_latest_valset(&mut grpc_client, gravity_contract_address, &web3).await; + let current_eth_valset = find_latest_valset( + &mut grpc_client, + gravity_contract_address, + eth_client.clone(), + ) + .await; if current_eth_valset.is_err() { error!("Could not get current valset! {:?}", current_eth_valset); - } - let current_eth_valset = current_eth_valset.unwrap(); - - let gravity_id = - get_gravity_id(gravity_contract_address, our_ethereum_address, &web3).await; - if gravity_id.is_err() { - error!("Failed to get GravityID, check your Eth node"); return; } - let gravity_id = gravity_id.unwrap(); + let current_eth_valset = current_eth_valset.unwrap(); relay_valsets( current_eth_valset.clone(), - ethereum_key, - &web3, + eth_client.clone(), &mut grpc_client, gravity_contract_address, gravity_id.clone(), @@ -55,25 +53,23 @@ pub async fn relayer_main_loop( relay_batches( current_eth_valset.clone(), - ethereum_key, - &web3, + eth_client.clone(), &mut grpc_client, gravity_contract_address, gravity_id.clone(), LOOP_SPEED, - gas_multiplier, + eth_gas_price_multiplier, ) .await; relay_logic_calls( current_eth_valset, - ethereum_key, - &web3, + eth_client.clone(), &mut grpc_client, gravity_contract_address, gravity_id.clone(), LOOP_SPEED, - gas_multiplier, + eth_gas_price_multiplier, ) .await; }, diff --git a/orchestrator/relayer/src/valset_relaying.rs b/orchestrator/relayer/src/valset_relaying.rs index 9e011a05f..050c87586 100644 --- a/orchestrator/relayer/src/valset_relaying.rs +++ b/orchestrator/relayer/src/valset_relaying.rs @@ -3,24 +3,23 @@ use std::time::Duration; -use clarity::address::Address as EthAddress; -use clarity::utils::bytes_to_hex_str; -use clarity::PrivateKey as EthPrivateKey; use cosmos_gravity::query::get_latest_valset; use cosmos_gravity::query::{get_all_valset_confirms, get_valset}; -use ethereum_gravity::{one_eth, utils::downcast_to_u128, valset_update::send_eth_valset_update}; +use ethereum_gravity::{one_eth_f32, types::EthClient, valset_update::send_eth_valset_update}; +use ethers::types::Address as EthAddress; use gravity_proto::gravity::query_client::QueryClient as GravityQueryClient; -use gravity_utils::{message_signatures::encode_valset_confirm_hashed, types::Valset}; +use gravity_utils::{ + ethereum::bytes_to_hex_str, ethereum::downcast_to_f32, + message_signatures::encode_valset_confirm_hashed, types::Valset, +}; use tonic::transport::Channel; -use web30::client::Web3; /// Check the last validator set on Ethereum, if it's lower than our latest validator /// set then we should package and submit the update as an Ethereum transaction pub async fn relay_valsets( // the validator set currently in the contract on Ethereum current_eth_valset: Valset, - ethereum_key: EthPrivateKey, - web3: &Web3, + eth_client: EthClient, grpc_client: &mut GravityQueryClient, gravity_contract_address: EthAddress, gravity_id: String, @@ -70,12 +69,13 @@ pub async fn relay_valsets( for confirm in confirms.iter() { assert_eq!(cosmos_valset.nonce, confirm.nonce); } + let hash = encode_valset_confirm_hashed(gravity_id.clone(), cosmos_valset.clone()); // there are two possible encoding problems that could cause the very rare sig failure bug, // one of them is that the hash is incorrect, that's not probable considering that // both Geth and Clarity agree on it. but this lets us check - info!("New valset hash {}", bytes_to_hex_str(&hash),); + info!("New valset hash {}", bytes_to_hex_str(&hash)); // order valset sigs prepares signatures for submission, notice we compare // them to the 'current' set in the bridge, this confirms for us that the validator set @@ -132,12 +132,12 @@ pub async fn relay_valsets( &latest_cosmos_valset, ¤t_eth_valset, &latest_cosmos_confirmed, - web3, gravity_contract_address, gravity_id.clone(), - ethereum_key, + eth_client.clone(), ) .await; + if cost.is_err() { error!( "Valset cost estimate for Nonce {} failed with {:?}", @@ -146,24 +146,32 @@ pub async fn relay_valsets( return; } let cost = cost.unwrap(); + let total_cost = downcast_to_f32(cost.get_total()); + if total_cost.is_none() { + error!( + "Total gas cost greater than f32 max, skipping valset submission: {}", + latest_cosmos_valset.nonce + ); + return; + } + let total_cost = total_cost.unwrap(); info!( "We have detected latest valset {} but latest on Ethereum is {} This valset is estimated to cost {} Gas / {:.4} ETH to submit", latest_cosmos_valset.nonce, current_eth_valset.nonce, cost.gas_price.clone(), - downcast_to_u128(cost.get_total()).unwrap() as f32 - / downcast_to_u128(one_eth()).unwrap() as f32 + total_cost / one_eth_f32() ); let relay_response = send_eth_valset_update( latest_cosmos_valset.clone(), current_eth_valset.clone(), &latest_cosmos_confirmed, - web3, timeout, gravity_contract_address, gravity_id, - ethereum_key, + cost, + eth_client.clone(), ) .await; diff --git a/orchestrator/test_runner/Cargo.toml b/orchestrator/test_runner/Cargo.toml index d2fd98715..b44ab38fa 100644 --- a/orchestrator/test_runner/Cargo.toml +++ b/orchestrator/test_runner/Cargo.toml @@ -12,6 +12,7 @@ edition = "2018" [dependencies] ethereum_gravity = {path = "../ethereum_gravity"} cosmos_gravity = {path = "../cosmos_gravity"} +gravity_abi = { path = "../gravity_abi" } gravity_utils = {path = "../gravity_utils"} gravity_proto = {path = "../gravity_proto/"} orchestrator = {path = "../orchestrator/"} @@ -20,17 +21,18 @@ deep_space ={git="https://github.com/iqlusioninc/deep_space/", branch="master"} serde_derive = "1.0" clarity = "0.4.11" docopt = "1" +ethers = "0.6.1" serde = "1.0" -actix = "0.11" +actix = "0.12" actix-web = {version = "3", features=["openssl"]} -actix-rt = "2.2" +actix-rt = "2.5" lazy_static = "1" url = "2" web30 = "0.15.4" -num256 = "0.3" log = "0.4" env_logger = "0.8" tokio = "1.4.0" rand = "0.8" tonic = "0.4" -futures = "0.3" +futures = "0.3.18" +hex = "0.4.3" diff --git a/orchestrator/test_runner/src/arbitrary_logic.rs b/orchestrator/test_runner/src/arbitrary_logic.rs index 21d69bf05..6c071d24c 100644 --- a/orchestrator/test_runner/src/arbitrary_logic.rs +++ b/orchestrator/test_runner/src/arbitrary_logic.rs @@ -3,13 +3,13 @@ use crate::TOTAL_TIMEOUT; use deep_space::Contact; +use ethers::prelude::*; use gravity_proto::gravity::query_client::QueryClient as GravityQueryClient; use tokio::time::sleep as delay_for; use tonic::transport::Channel; -use web30::client::Web3; pub async fn arbitrary_logic_test( - _web30: &Web3, + _eth_provider: &Provider, _grpc_client: GravityQueryClient, _contact: &Contact, ) { diff --git a/orchestrator/test_runner/src/bootstrapping.rs b/orchestrator/test_runner/src/bootstrapping.rs index 15bac577e..1d9e12338 100644 --- a/orchestrator/test_runner/src/bootstrapping.rs +++ b/orchestrator/test_runner/src/bootstrapping.rs @@ -1,11 +1,11 @@ use crate::utils::ValidatorKeys; -use clarity::Address as EthAddress; -use clarity::PrivateKey as EthPrivateKey; use deep_space::private_key::PrivateKey as CosmosPrivateKey; +use ethers::core::k256::ecdsa::SigningKey; +use ethers::types::Address as EthAddress; use std::fs::File; use std::io::{BufRead, BufReader, Read}; -pub fn parse_ethereum_keys() -> Vec { +pub fn parse_ethereum_keys() -> Vec { let filename = "/testdata/validator-eth-keys"; let file = File::open(filename).expect("Failed to find eth keys"); let reader = BufReader::new(file); @@ -13,7 +13,8 @@ pub fn parse_ethereum_keys() -> Vec { for line in reader.lines() { let line = line.expect("Error reading eth-keys file!"); - let key: EthPrivateKey = line.parse().unwrap(); + let key_hex = hex::decode(line.strip_prefix("0x").unwrap()).unwrap(); + let key: SigningKey = SigningKey::from_bytes(&key_hex).unwrap(); ret.push(key); } ret diff --git a/orchestrator/test_runner/src/happy_path.rs b/orchestrator/test_runner/src/happy_path.rs index 0cd60cca0..758b201e8 100644 --- a/orchestrator/test_runner/src/happy_path.rs +++ b/orchestrator/test_runner/src/happy_path.rs @@ -3,29 +3,31 @@ use crate::get_fee; use crate::get_gas_price; use crate::utils::*; use crate::MINER_ADDRESS; -use crate::MINER_PRIVATE_KEY; +use crate::MINER_CLIENT; use crate::OPERATION_TIMEOUT; use crate::TOTAL_TIMEOUT; -use clarity::PrivateKey as EthPrivateKey; -use clarity::{Address as EthAddress, Uint256}; +use clarity::Uint256; use cosmos_gravity::send::{send_request_batch_tx, send_to_eth}; use cosmos_gravity::{build, query::get_oldest_unsigned_transaction_batch, send}; use deep_space::address::Address as CosmosAddress; use deep_space::coin::Coin; use deep_space::private_key::PrivateKey as CosmosPrivateKey; use deep_space::Contact; +use ethereum_gravity::erc20_utils::get_erc20_balance; use ethereum_gravity::utils::get_valset_nonce; use ethereum_gravity::{send_to_cosmos::send_to_cosmos, utils::get_tx_batch_nonce}; +use ethers::core::k256::ecdsa::SigningKey; +use ethers::prelude::*; +use ethers::types::Address as EthAddress; use gravity_proto::gravity::query_client::QueryClient as GravityQueryClient; use gravity_utils::types::SendToCosmosEvent; use rand::Rng; +use std::str::FromStr; use std::time::Duration; use tokio::time::sleep as delay_for; use tonic::transport::Channel; -use web30::client::Web3; pub async fn happy_path_test( - web30: &Web3, grpc_client: GravityQueryClient, contact: &Contact, keys: Vec, @@ -48,10 +50,10 @@ pub async fn happy_path_test( if !validator_out { for _ in 0u32..2 { - test_valset_update(&web30, contact, &keys, gravity_address).await; + test_valset_update(contact, &keys, gravity_address).await; } } else { - wait_for_nonzero_valset(&web30, gravity_address).await; + wait_for_nonzero_valset(gravity_address).await; } // generate an address for coin sending tests, this ensures test imdepotency @@ -61,15 +63,15 @@ pub async fn happy_path_test( let dest_cosmos_address = dest_cosmos_private_key .to_address(CosmosAddress::DEFAULT_PREFIX) .unwrap(); - let dest_eth_private_key = EthPrivateKey::from_slice(&secret).unwrap(); - let dest_eth_address = dest_eth_private_key.to_public_key().unwrap(); + let dest_eth_private_key = SigningKey::from_bytes(&secret).unwrap(); + let dest_eth_wallet = LocalWallet::from(dest_eth_private_key.clone()); + let dest_eth_address = dest_eth_wallet.address(); // the denom and amount of the token bridged from Ethereum -> Cosmos // so the denom is the gravity token name // Send a token 3 times for _ in 0u32..3 { test_erc20_deposit( - &web30, &contact, dest_cosmos_address, gravity_address, @@ -97,7 +99,6 @@ pub async fn happy_path_test( test_batch( &contact, &mut grpc_client, - &web30, dest_eth_address, gravity_address, keys[0].validator_key, @@ -107,15 +108,16 @@ pub async fn happy_path_test( .await; } -pub async fn wait_for_nonzero_valset(web30: &Web3, gravity_address: EthAddress) { +pub async fn wait_for_nonzero_valset(gravity_address: EthAddress) { match tokio::time::timeout(TOTAL_TIMEOUT, async { let mut current_eth_valset_nonce = - get_valset_nonce(gravity_address, *MINER_ADDRESS, &web30) + get_valset_nonce(gravity_address, (*MINER_CLIENT).clone()) .await .expect("Failed to get current eth valset"); + while 0 == current_eth_valset_nonce { info!("Validator set is not yet updated to >0, waiting"); - current_eth_valset_nonce = get_valset_nonce(gravity_address, *MINER_ADDRESS, &web30) + current_eth_valset_nonce = get_valset_nonce(gravity_address, (*MINER_CLIENT).clone()) .await .expect("Failed to get current eth valset"); delay_for(Duration::from_secs(4)).await; @@ -133,14 +135,13 @@ pub async fn wait_for_nonzero_valset(web30: &Web3, gravity_address: EthAddress) } pub async fn test_valset_update( - web30: &Web3, contact: &Contact, keys: &[ValidatorKeys], gravity_address: EthAddress, ) { // if we don't do this the orchestrators may run ahead of us and we'll be stuck here after // getting credit for two loops when we did one - let starting_eth_valset_nonce = get_valset_nonce(gravity_address, *MINER_ADDRESS, &web30) + let starting_eth_valset_nonce = get_valset_nonce(gravity_address, (*MINER_CLIENT).clone()) .await .expect("Failed to get starting eth valset"); // this is hacky and not really a good way to test validator set updates in a highly @@ -206,7 +207,7 @@ pub async fn test_valset_update( } } - let mut current_eth_valset_nonce = get_valset_nonce(gravity_address, *MINER_ADDRESS, &web30) + let mut current_eth_valset_nonce = get_valset_nonce(gravity_address, (*MINER_CLIENT).clone()) .await .expect("Failed to get current eth valset"); @@ -216,7 +217,7 @@ pub async fn test_valset_update( "Validator set is not yet updated to >{}, waiting", starting_eth_valset_nonce ); - current_eth_valset_nonce = get_valset_nonce(gravity_address, *MINER_ADDRESS, &web30) + current_eth_valset_nonce = get_valset_nonce(gravity_address, (*MINER_CLIENT).clone()) .await .expect("Failed to get current eth valset"); delay_for(Duration::from_secs(4)).await; @@ -236,13 +237,13 @@ pub async fn test_valset_update( /// this function tests Ethereum -> Cosmos async fn test_erc20_deposit( - web30: &Web3, contact: &Contact, dest: CosmosAddress, gravity_address: EthAddress, erc20_address: EthAddress, - amount: Uint256, + amount: U256, ) { + let amount_uint256 = Uint256::from_str(amount.to_string().as_str()).unwrap(); let start_coin = check_cosmos_balance("gravity", dest, &contact).await; info!( "Sending to Cosmos from {} to {} with amount {}", @@ -254,10 +255,8 @@ async fn test_erc20_deposit( gravity_address, amount.clone(), dest, - *MINER_PRIVATE_KEY, Some(TOTAL_TIMEOUT), - &web30, - vec![], + (*MINER_CLIENT).clone(), ) .await .expect("Failed to send tokens to Cosmos"); @@ -269,7 +268,7 @@ async fn test_erc20_deposit( check_cosmos_balance("gravity", dest, &contact).await, ) { (Some(start_coin), Some(end_coin)) => { - if start_coin.amount + amount.clone() == end_coin.amount + if start_coin.amount + amount_uint256.clone() == end_coin.amount && start_coin.denom == end_coin.denom { info!( @@ -280,7 +279,7 @@ async fn test_erc20_deposit( } } (None, Some(end_coin)) => { - if amount == end_coin.amount { + if amount_uint256 == end_coin.amount { info!( "Successfully bridged ERC20 {}{} to Cosmos! Balance is now {}{}", amount, end_coin.denom, end_coin.amount, end_coin.denom @@ -309,7 +308,6 @@ async fn test_erc20_deposit( async fn test_batch( contact: &Contact, grpc_client: &mut GravityQueryClient, - web30: &Web3, dest_eth_address: EthAddress, gravity_address: EthAddress, requester_cosmos_private_key: CosmosPrivateKey, @@ -370,7 +368,7 @@ async fn test_batch( .expect("Failed to get batch to sign"); let mut current_eth_batch_nonce = - get_tx_batch_nonce(gravity_address, erc20_contract, *MINER_ADDRESS, &web30) + get_tx_batch_nonce(gravity_address, erc20_contract, (*MINER_CLIENT).clone()) .await .expect("Failed to get current eth valset"); let starting_batch_nonce = current_eth_batch_nonce; @@ -382,7 +380,7 @@ async fn test_batch( starting_batch_nonce ); current_eth_batch_nonce = - get_tx_batch_nonce(gravity_address, erc20_contract, *MINER_ADDRESS, &web30) + get_tx_batch_nonce(gravity_address, erc20_contract, (*MINER_CLIENT).clone()) .await .expect("Failed to get current eth tx batch nonce"); delay_for(Duration::from_secs(4)).await; @@ -398,30 +396,29 @@ async fn test_batch( } } - let txid = web30 - .send_transaction( - dest_eth_address, - Vec::new(), - 1_000_000_000_000_000_000u128.into(), - *MINER_ADDRESS, - *MINER_PRIVATE_KEY, - vec![], - ) - .await - .expect("Failed to send Eth to validator {}"); - web30 - .wait_for_transaction(txid, TOTAL_TIMEOUT, None) - .await - .unwrap(); + let eth_client = (*MINER_CLIENT).clone(); + let tx = TransactionRequest { + from: Some(eth_client.address()), + to: Some(NameOrAddress::Address(dest_eth_address)), + gas: None, + gas_price: None, + value: Some(1_000_000_000_000_000_000u128.into()), + data: Some(Vec::new().into()), + nonce: None, + }; + + let pending_tx = eth_client.send_transaction(tx, None).await.unwrap(); + pending_tx.await.unwrap(); + + let amount_u256 = U256::from_str(amount.to_string().as_str()).unwrap(); // we have to send this address one eth so that it can perform contract calls - send_one_eth(dest_eth_address, web30).await; + send_one_eth(dest_eth_address, eth_client.clone()).await; assert_eq!( - web30 - .get_erc20_balance(erc20_contract, dest_eth_address) + get_erc20_balance(erc20_contract, dest_eth_address, eth_client.clone()) .await .unwrap(), - amount + amount_u256 ); info!( "Successfully updated txbatch nonce to {} and sent {}{} tokens to Ethereum!", @@ -433,10 +430,10 @@ async fn test_batch( // already been submitted to test the nonce functionality. #[allow(clippy::too_many_arguments)] async fn submit_duplicate_erc20_send( - nonce: Uint256, + nonce: U256, contact: &Contact, erc20_address: EthAddress, - amount: Uint256, + amount: U256, receiver: CosmosAddress, keys: &[ValidatorKeys], ) { diff --git a/orchestrator/test_runner/src/happy_path_v2.rs b/orchestrator/test_runner/src/happy_path_v2.rs index e5eb586dc..caa42d6d6 100644 --- a/orchestrator/test_runner/src/happy_path_v2.rs +++ b/orchestrator/test_runner/src/happy_path_v2.rs @@ -1,35 +1,47 @@ //! This is the happy path test for Cosmos to Ethereum asset transfers, meaning assets originated on Cosmos use crate::utils::get_user_key; use crate::utils::send_one_eth; +use crate::MINER_CLIENT; use crate::TOTAL_TIMEOUT; use crate::{get_fee, utils::ValidatorKeys}; -use clarity::Address as EthAddress; use clarity::Uint256; use cosmos_gravity::send::{send_request_batch_tx, send_to_eth}; use deep_space::coin::Coin; use deep_space::Contact; +use ethereum_gravity::erc20_utils::get_erc20_balance; use ethereum_gravity::{deploy_erc20::deploy_erc20, utils::get_event_nonce}; +use ethers::prelude::*; +use ethers::types::Address as EthAddress; use gravity_proto::gravity::{ query_client::QueryClient as GravityQueryClient, DenomToErc20Request, }; +use gravity_utils::ethereum::downcast_to_u64; +use std::str::FromStr; +use std::sync::Arc; use tonic::transport::Channel; -use web30::client::Web3; -#[allow(unused_assignments)] + pub async fn happy_path_test_v2( - web30: &Web3, + eth_provider: &Provider, grpc_client: GravityQueryClient, contact: &Contact, keys: Vec, gravity_address: EthAddress, ) { let mut grpc_client = grpc_client; - let starting_event_nonce = get_event_nonce( - gravity_address, - keys[0].eth_key.to_public_key().unwrap(), - web30, - ) - .await - .unwrap(); + let eth_wallet = LocalWallet::from(keys[0].eth_key.clone()); + let provider = eth_provider.clone(); + let chain_id = provider + .get_chainid() + .await + .expect("Could not retrieve chain ID"); + let chain_id = downcast_to_u64(chain_id).expect("Chain ID overflowed when downcasting to u64"); + let eth_client = Arc::new(SignerMiddleware::new( + provider, + eth_wallet.with_chain_id(chain_id), + )); + let starting_event_nonce = get_event_nonce(gravity_address, eth_client.clone()) + .await + .unwrap(); let token_to_send_to_eth = "footoken".to_string(); let token_to_send_to_eth_display_name = "mfootoken".to_string(); @@ -40,20 +52,14 @@ pub async fn happy_path_test_v2( token_to_send_to_eth_display_name.clone(), 6, gravity_address, - web30, Some(TOTAL_TIMEOUT), - keys[0].eth_key, - vec![], - ) - .await - .unwrap(); - let ending_event_nonce = get_event_nonce( - gravity_address, - keys[0].eth_key.to_public_key().unwrap(), - web30, + eth_client.clone(), ) .await .unwrap(); + let ending_event_nonce = get_event_nonce(gravity_address, eth_client.clone()) + .await + .unwrap(); assert!(starting_event_nonce != ending_event_nonce); info!( @@ -61,8 +67,7 @@ pub async fn happy_path_test_v2( ending_event_nonce ); // the erc20 representing the cosmos asset on Ethereum - let mut erc20_contract = None; - match tokio::time::timeout(TOTAL_TIMEOUT, async { + let erc20_contract = match tokio::time::timeout(TOTAL_TIMEOUT, async { loop { let res = grpc_client .denom_to_erc20(DenomToErc20Request { @@ -80,11 +85,11 @@ pub async fn happy_path_test_v2( { Ok(res) => { let erc20 = res.into_inner().erc20; - erc20_contract = Some(erc20.clone()); info!( "Successfully adopted {} token contract of {}", token_to_send_to_eth, erc20 ); + Some(erc20.clone()) } Err(_) => { panic!( @@ -92,7 +97,7 @@ pub async fn happy_path_test_v2( token_to_send_to_eth ); } - } + }; let erc20_contract: EthAddress = erc20_contract.unwrap().parse().unwrap(); @@ -140,7 +145,7 @@ pub async fn happy_path_test_v2( ); // send the user some eth, they only need this to check their // erc20 balance, so a pretty minor usecase - send_one_eth(user.eth_address, web30).await; + send_one_eth(user.eth_address, (*MINER_CLIENT).clone()).await; info!("Sent 1 eth to user address {}", user.eth_address); let res = send_to_eth( @@ -173,16 +178,15 @@ pub async fn happy_path_test_v2( info!("Sent batch request to move things along"); info!("Waiting for batch to be signed and relayed to Ethereum"); - match tokio::time::timeout(TOTAL_TIMEOUT, async { loop { - let balance = web30 - .get_erc20_balance(erc20_contract, user.eth_address) - .await; + let balance = + get_erc20_balance(erc20_contract, user.eth_address, (*MINER_CLIENT).clone()).await; if balance.is_err() { continue; } let balance = balance.unwrap(); + let balance = Uint256::from_str(balance.to_string().as_str()).unwrap(); if balance == amount_to_bridge { break; } else if balance != 0u8.into() { diff --git a/orchestrator/test_runner/src/main.rs b/orchestrator/test_runner/src/main.rs index 5579ef160..8f75ff2ac 100644 --- a/orchestrator/test_runner/src/main.rs +++ b/orchestrator/test_runner/src/main.rs @@ -11,17 +11,23 @@ extern crate lazy_static; use crate::bootstrapping::*; use crate::utils::*; use arbitrary_logic::arbitrary_logic_test; -use clarity::PrivateKey as EthPrivateKey; -use clarity::{Address as EthAddress, Uint256}; +use clarity::Uint256; use cosmos_gravity::utils::wait_for_cosmos_online; use deep_space::coin::Coin; use deep_space::Address as CosmosAddress; use deep_space::Contact; +use ethereum_gravity::types::EthClient; +use ethers::core::k256::ecdsa::SigningKey; +use ethers::prelude::*; +use ethers::providers::Provider; +use ethers::types::Address as EthAddress; use gravity_proto::gravity::query_client::QueryClient as GravityQueryClient; +use gravity_utils::ethereum::hex_str_to_bytes; use happy_path::happy_path_test; use happy_path_v2::happy_path_test_v2; use orch_keys_update::orch_keys_update; -use std::{env, time::Duration}; +use std::convert::TryFrom; +use std::{env, sync::Arc, time::Duration}; use transaction_stress_test::transaction_stress_test; use valset_stress::validator_set_stress_test; @@ -59,11 +65,17 @@ lazy_static! { // this key is the private key for the public key defined in tests/assets/ETHGenesis.json // where the full node / miner sends its rewards. Therefore it's always going // to have a lot of ETH to pay for things like contract deployments - static ref MINER_PRIVATE_KEY: EthPrivateKey = - "0xb1bab011e03a9862664706fc3bbaa1b16651528e5f0e7fbfcbfdd8be302a13e7" - .parse() - .unwrap(); - static ref MINER_ADDRESS: EthAddress = MINER_PRIVATE_KEY.to_public_key().unwrap(); + static ref MINER_PRIVATE_KEY: SigningKey = + SigningKey::from_bytes(hex_str_to_bytes( + "0xb1bab011e03a9862664706fc3bbaa1b16651528e5f0e7fbfcbfdd8be302a13e7").unwrap().as_slice() + ).unwrap(); + static ref MINER_WALLET: LocalWallet = LocalWallet::from((*MINER_PRIVATE_KEY).clone()); + static ref MINER_ADDRESS: EthAddress = (*MINER_WALLET).address(); + static ref MINER_PROVIDER: Provider = Provider::::try_from((*ETH_NODE).clone()).unwrap(); + static ref MINER_SIGNER: SignerMiddleware, LocalWallet> = + SignerMiddleware::new((*MINER_PROVIDER).clone(), (*MINER_WALLET).clone()); + static ref MINER_CLIENT: EthClient = Arc::new((*MINER_SIGNER).clone()); + } /// Gets the standard non-token fee for the testnet. We deploy the test chain with STAKE @@ -89,11 +101,15 @@ pub fn get_chain_id() -> String { "gravity-test".to_string() } -pub fn one_eth() -> Uint256 { +pub fn one_eth() -> U256 { 1000000000000000000u128.into() } -pub fn one_hundred_eth() -> Uint256 { +pub fn one_hundred_eth() -> U256 { + (1000000000000000000u128 * 100).into() +} + +pub fn one_hundred_eth_uint256() -> Uint256 { (1000000000000000000u128 * 100).into() } @@ -123,7 +139,7 @@ pub async fn main() { let grpc_client = GravityQueryClient::connect(COSMOS_NODE_GRPC.as_str()) .await .unwrap(); - let web30 = web30::client::Web3::new(ETH_NODE.as_str(), OPERATION_TIMEOUT); + let eth_provider = Provider::::try_from((*ETH_NODE).clone()).unwrap(); let keys = get_keys(); // // if we detect this env var we are only deploying contracts, do that then exit. @@ -164,7 +180,6 @@ pub async fn main() { if test_type == "VALIDATOR_OUT" { info!("Starting Validator out test"); happy_path_test( - &web30, grpc_client, &contact, keys, @@ -182,19 +197,26 @@ pub async fn main() { CosmosAddress::DEFAULT_PREFIX, ) .unwrap(); - transaction_stress_test(&web30, &contact, keys, gravity_address, erc20_addresses).await; + transaction_stress_test( + ð_provider, + &contact, + keys, + gravity_address, + erc20_addresses, + ) + .await; return; } else if test_type == "VALSET_STRESS" { info!("Starting valset stress test"); - validator_set_stress_test(&web30, &contact, keys, gravity_address).await; + validator_set_stress_test(&contact, keys, gravity_address).await; return; } else if test_type == "V2_HAPPY_PATH" { info!("Starting happy path for Gravity v2"); - happy_path_test_v2(&web30, grpc_client, &contact, keys, gravity_address).await; + happy_path_test_v2(ð_provider, grpc_client, &contact, keys, gravity_address).await; return; } else if test_type == "ARBITRARY_LOGIC" { info!("Starting arbitrary logic tests!"); - arbitrary_logic_test(&web30, grpc_client, &contact).await; + arbitrary_logic_test(ð_provider, grpc_client, &contact).await; return; } else if test_type == "ORCHESTRATOR_KEYS" { info!("Starting orchestrator key update tests!"); @@ -204,7 +226,6 @@ pub async fn main() { } info!("Starting Happy path test"); happy_path_test( - &web30, grpc_client, &contact, keys, diff --git a/orchestrator/test_runner/src/orch_keys_update.rs b/orchestrator/test_runner/src/orch_keys_update.rs index 6556a3f0a..c29cb2d23 100644 --- a/orchestrator/test_runner/src/orch_keys_update.rs +++ b/orchestrator/test_runner/src/orch_keys_update.rs @@ -1,16 +1,17 @@ //! This test verifies that live updating of orchestrator keys works correctly use crate::utils::ValidatorKeys; -use clarity::Address as EthAddress; -use clarity::PrivateKey as EthPrivateKey; use cosmos_gravity::send::update_gravity_delegate_addresses; use deep_space::address::Address as CosmosAddress; use deep_space::private_key::PrivateKey as CosmosPrivateKey; use deep_space::Contact; +use ethers::types::Address as EthAddress; +use ethers::{core::k256::ecdsa::SigningKey, prelude::*}; use gravity_proto::gravity::{ query_client::QueryClient as GravityQueryClient, DelegateKeysByEthereumSignerRequest, DelegateKeysByOrchestratorRequest, }; +use gravity_utils::ethereum::format_eth_address; use rand::Rng; use std::time::Duration; use tonic::transport::Channel; @@ -27,11 +28,11 @@ pub async fn orch_keys_update( // just to test that we have the right keys from the gentx info!("About to check already set delegate addresses"); for k in keys.iter() { - let eth_address = k.eth_key.to_public_key().unwrap(); + let eth_address = LocalWallet::from(k.eth_key.clone()).address(); let orch_address = k.orch_key.to_address(&contact.get_prefix()).unwrap(); let eth_response = grpc_client .delegate_keys_by_ethereum_signer(DelegateKeysByEthereumSignerRequest { - ethereum_signer: eth_address.to_string(), + ethereum_signer: format_eth_address(eth_address), }) .await .unwrap() @@ -61,7 +62,8 @@ pub async fn orch_keys_update( let mut rng = rand::thread_rng(); let secret: [u8; 32] = rng.gen(); // generate some new keys to replace the old ones - let ethereum_key = EthPrivateKey::from_slice(&secret).unwrap(); + let ethereum_key = SigningKey::from_bytes(&secret).unwrap(); + let ethereum_wallet = LocalWallet::from(ethereum_key.clone()); let cosmos_key = CosmosPrivateKey::from_secret(&secret); // update the keys in the key list k.eth_key = ethereum_key; @@ -70,16 +72,16 @@ pub async fn orch_keys_update( info!( "Signing and submitting Delegate addresses {} for validator {}", - ethereum_key.to_public_key().unwrap(), + format_eth_address(ethereum_wallet.address()), cosmos_address, ); // send in the new delegate keys signed by the validator address update_gravity_delegate_addresses( - contact, - ethereum_key.to_public_key().unwrap(), + &contact, + ethereum_wallet.address(), cosmos_address, k.validator_key, - k.eth_key, + ethereum_wallet, (0f64, "".to_string()), 2.0, ) diff --git a/orchestrator/test_runner/src/transaction_stress_test.rs b/orchestrator/test_runner/src/transaction_stress_test.rs index 1c3c0dc46..863b00f45 100644 --- a/orchestrator/test_runner/src/transaction_stress_test.rs +++ b/orchestrator/test_runner/src/transaction_stress_test.rs @@ -1,12 +1,18 @@ -use crate::{one_eth, one_hundred_eth, utils::*, TOTAL_TIMEOUT}; -use clarity::Address as EthAddress; +use crate::{ + one_eth, one_hundred_eth, one_hundred_eth_uint256, utils::*, MINER_CLIENT, TOTAL_TIMEOUT, +}; +use clarity::Uint256; use cosmos_gravity::send::{send_request_batch_tx, send_to_eth}; use deep_space::coin::Coin; use deep_space::Contact; -use ethereum_gravity::{send_to_cosmos::send_to_cosmos, utils::get_tx_batch_nonce}; +use ethereum_gravity::{ + erc20_utils::get_erc20_balance, send_to_cosmos::send_to_cosmos, utils::get_tx_batch_nonce, +}; +use ethers::prelude::*; +use ethers::types::Address as EthAddress; use futures::future::join_all; -use std::{collections::HashSet, time::Duration}; -use web30::client::Web3; +use gravity_utils::ethereum::downcast_to_u64; +use std::{collections::HashSet, str::FromStr, sync::Arc, time::Duration}; const TIMEOUT: Duration = Duration::from_secs(120); @@ -25,7 +31,7 @@ const NUM_USERS: usize = 100; /// transactions and producing large batches #[allow(clippy::too_many_arguments)] pub async fn transaction_stress_test( - web30: &Web3, + eth_provider: &Provider, contact: &Contact, keys: Vec, gravity_address: EthAddress, @@ -44,39 +50,48 @@ pub async fn transaction_stress_test( let mut eth_destinations = Vec::new(); eth_destinations.extend(sending_eth_addresses.clone()); eth_destinations.extend(dest_eth_addresses); - send_eth_bulk(one_eth(), ð_destinations, web30).await; + send_eth_bulk(one_eth(), ð_destinations, (*MINER_CLIENT).clone()).await; info!("Sent {} addresses 1 ETH", NUM_USERS); // now we need to send all the sending eth addresses erc20's to send for token in erc20_addresses.iter() { - send_erc20_bulk(one_hundred_eth(), *token, &sending_eth_addresses, web30).await; + send_erc20_bulk( + one_hundred_eth(), + *token, + &sending_eth_addresses, + (*MINER_CLIENT).clone(), + ) + .await; info!("Sent {} addresses 100 {}", NUM_USERS, token); } for token in erc20_addresses.iter() { let mut sends = Vec::new(); for keys in user_keys.iter() { + let eth_wallet = LocalWallet::from(keys.eth_key.clone()); + let provider = eth_provider.clone(); + let chain_id = provider + .get_chainid() + .await + .expect("Could not retrieve chain ID"); + let chain_id = + downcast_to_u64(chain_id).expect("Chain ID overflowed when downcasting to u64"); + let eth_client = Arc::new(SignerMiddleware::new( + provider, + eth_wallet.with_chain_id(chain_id), + )); let fut = send_to_cosmos( *token, gravity_address, one_hundred_eth(), keys.cosmos_address, - keys.eth_key, Some(TIMEOUT), - web30, - Vec::new(), + eth_client.clone(), ); sends.push(fut); } - let txids = join_all(sends).await; - let mut wait_for_txid = Vec::new(); - for txid in txids { - let wait = web30.wait_for_transaction(txid.unwrap(), TIMEOUT, None); - wait_for_txid.push(wait); - } - let results = join_all(wait_for_txid).await; + let results = join_all(sends).await; for result in results { - let result = result.unwrap(); - result.block_number.unwrap(); + result.unwrap(); } info!( "Locked 100 {} from {} into the Gravity Ethereum Contract", @@ -86,8 +101,8 @@ pub async fn transaction_stress_test( let mut good = true; match tokio::time::timeout(TOTAL_TIMEOUT, async { - good = true; loop { + good = true; for keys in user_keys.iter() { let c_addr = keys.cosmos_address; let balances = contact.get_balances(c_addr).await.unwrap(); @@ -95,7 +110,7 @@ pub async fn transaction_stress_test( let mut found = false; for balance in balances.iter() { if balance.denom.contains(&token.to_string()) - && balance.amount == one_hundred_eth() + && balance.amount == one_hundred_eth_uint256() { found = true; } @@ -127,7 +142,7 @@ pub async fn transaction_stress_test( } } - let send_amount = one_hundred_eth() - 500u16.into(); + let send_amount = one_hundred_eth_uint256() - 500u16.into(); let mut denoms = HashSet::new(); for token in erc20_addresses.iter() { @@ -194,7 +209,10 @@ pub async fn transaction_stress_test( for keys in user_keys.iter() { let e_dest_addr = keys.eth_dest_address; for token in erc20_addresses.iter() { - let bal = web30.get_erc20_balance(*token, e_dest_addr).await.unwrap(); + let bal = get_erc20_balance(*token, e_dest_addr, (*MINER_CLIENT).clone()) + .await + .unwrap(); + let bal = Uint256::from_str(bal.to_string().as_str()).unwrap(); if bal != send_amount.clone() { good = false; } @@ -224,16 +242,13 @@ pub async fn transaction_stress_test( // we should find a batch nonce greater than zero since all the batches // executed + let eth_wallet = LocalWallet::from(keys[0].eth_key.clone()); + let eth_client = Arc::new(SignerMiddleware::new(eth_provider.clone(), eth_wallet)); for token in erc20_addresses { assert!( - get_tx_batch_nonce( - gravity_address, - token, - keys[0].eth_key.to_public_key().unwrap(), - web30 - ) - .await - .unwrap() + get_tx_batch_nonce(gravity_address, token, eth_client.clone()) + .await + .unwrap() > 0 ) } diff --git a/orchestrator/test_runner/src/utils.rs b/orchestrator/test_runner/src/utils.rs index c53fff330..338250c35 100644 --- a/orchestrator/test_runner/src/utils.rs +++ b/orchestrator/test_runner/src/utils.rs @@ -1,32 +1,29 @@ -use crate::MINER_PRIVATE_KEY; -use crate::TOTAL_TIMEOUT; -use crate::{MINER_ADDRESS, OPERATION_TIMEOUT}; -use clarity::{Address as EthAddress, Uint256}; -use clarity::{PrivateKey as EthPrivateKey, Transaction}; +use crate::one_eth; use deep_space::address::Address as CosmosAddress; use deep_space::coin::Coin; use deep_space::private_key::PrivateKey as CosmosPrivateKey; use deep_space::Contact; +use ethereum_gravity::{erc20_utils::get_erc20_balance, types::EthClient}; +use ethers::core::k256::ecdsa::SigningKey; +use ethers::prelude::*; +use ethers::types::Address as EthAddress; use futures::future::join_all; +use gravity_abi::erc20::ERC20; use rand::Rng; -use web30::{client::Web3, types::SendTxOption}; - -pub async fn send_one_eth(dest: EthAddress, web30: &Web3) { - let txid = web30 - .send_transaction( - dest, - Vec::new(), - 1_000_000_000_000_000_000u128.into(), - *MINER_ADDRESS, - *MINER_PRIVATE_KEY, - vec![], - ) - .await - .expect("Failed to send Eth to validator {}"); - web30 - .wait_for_transaction(txid, TOTAL_TIMEOUT, None) - .await - .unwrap(); + +pub async fn send_one_eth(dest: EthAddress, eth_client: EthClient) { + let tx = TransactionRequest { + from: Some(eth_client.address()), + to: Some(NameOrAddress::Address(dest)), + gas: None, + gas_price: None, + value: Some(one_eth()), + data: Some(Vec::new().into()), + nonce: None, + }; + + let pending_tx = eth_client.send_transaction(tx, None).await.unwrap(); + pending_tx.await.unwrap(); } pub async fn check_cosmos_balance( @@ -50,36 +47,61 @@ pub async fn check_cosmos_balance( /// single address without your sequence getting out of whack. By manually setting the nonce /// here we can send thousands of transactions in only a few blocks pub async fn send_erc20_bulk( - amount: Uint256, + amount: U256, erc20: EthAddress, destinations: &[EthAddress], - web3: &Web3, + eth_client: EthClient, ) { - let miner_balance = web3.get_erc20_balance(erc20, *MINER_ADDRESS).await.unwrap(); - assert!(miner_balance > amount.clone() * destinations.len().into()); - let mut nonce = web3 - .eth_get_transaction_count(*MINER_ADDRESS) + let miner_balance = get_erc20_balance(erc20, eth_client.address(), eth_client.clone()) + .await + .unwrap(); + assert!( + miner_balance + > amount + .clone() + .checked_mul(destinations.len().into()) + .unwrap() + ); + + let mut nonce = eth_client + .get_transaction_count(eth_client.address(), None) .await .unwrap(); let mut transactions = Vec::new(); + for address in destinations { - let send = web3.erc20_send( - amount.clone(), - *address, - erc20, - *MINER_PRIVATE_KEY, - Some(OPERATION_TIMEOUT), - vec![ - SendTxOption::Nonce(nonce.clone()), - SendTxOption::GasLimit(100_000u32.into()), - ], - ); - transactions.push(send); + let data = ERC20::new(erc20, eth_client.clone()) + .transfer(*address, amount) + .calldata() + .unwrap(); + + let tx = TransactionRequest { + from: Some(eth_client.address()), + to: Some(NameOrAddress::Address(erc20)), + gas: Some(100_000u32.into()), + gas_price: None, + value: Some(0u32.into()), + data: Some(data), + nonce: Some(nonce.clone()), + }; + + let tx = eth_client.send_transaction(tx, None); + transactions.push(tx); nonce += 1u64.into(); } - let _txids = join_all(transactions).await; + + let pending_tx_results = join_all(transactions).await; + let mut pending_txs = Vec::new(); + for pending_tx_result in pending_tx_results { + let pending_tx = pending_tx_result.unwrap(); + pending_txs.push(pending_tx); + } + join_all(pending_txs).await; + for address in destinations { - let new_balance = web3.get_erc20_balance(erc20, *address).await.unwrap(); + let new_balance = get_erc20_balance(erc20, *address, eth_client.clone()) + .await + .unwrap(); assert!(new_balance >= amount.clone()); } } @@ -88,46 +110,44 @@ pub async fn send_erc20_bulk( /// the real problem here is that you can't do more than one send operation at a time from a /// single address without your sequence getting out of whack. By manually setting the nonce /// here we can quickly send thousands of transactions in only a few blocks -pub async fn send_eth_bulk(amount: Uint256, destinations: &[EthAddress], web3: &Web3) { - let net_version = web3.net_version().await.unwrap(); - let mut nonce = web3 - .eth_get_transaction_count(*MINER_ADDRESS) +pub async fn send_eth_bulk(amount: U256, destinations: &[EthAddress], eth_client: EthClient) { + let mut nonce = eth_client + .get_transaction_count(eth_client.address(), None) .await .unwrap(); let mut transactions = Vec::new(); + for address in destinations { - let t = Transaction { - to: *address, - nonce: nonce.clone(), - gas_price: 1_000_000_000u64.into(), - gas_limit: 24000u64.into(), - value: amount.clone(), - data: Vec::new(), - signature: None, + let tx = TransactionRequest { + from: Some(eth_client.address()), + to: Some(NameOrAddress::Address(*address)), + gas: Some(24_000u64.into()), + gas_price: Some(1_000_000_000u64.into()), + value: Some(amount.clone()), + data: Some(Vec::new().into()), + nonce: Some(nonce.clone()), }; - let t = t.sign(&*MINER_PRIVATE_KEY, Some(net_version)); - transactions.push(t); + + let tx = eth_client.send_transaction(tx, None); + transactions.push(tx); nonce += 1u64.into(); } - let mut sends = Vec::new(); - for tx in transactions { - sends.push(web3.eth_send_raw_transaction(tx.to_bytes().unwrap())); - } - let txids = join_all(sends).await; - let mut wait_for_txid = Vec::new(); - for txid in txids { - let wait = web3.wait_for_transaction(txid.unwrap(), TOTAL_TIMEOUT, None); - wait_for_txid.push(wait); + + let pending_tx_results = join_all(transactions).await; + let mut pending_txs = Vec::new(); + for pending_tx_result in pending_tx_results { + let pending_tx = pending_tx_result.unwrap(); + pending_txs.push(pending_tx); } - join_all(wait_for_txid).await; + join_all(pending_txs).await; } pub fn get_user_key() -> BridgeUserKey { let mut rng = rand::thread_rng(); let secret: [u8; 32] = rng.gen(); // the starting location of the funds - let eth_key = EthPrivateKey::from_slice(&secret).unwrap(); - let eth_address = eth_key.to_public_key().unwrap(); + let eth_key = SigningKey::from_bytes(&secret).unwrap(); + let eth_address = LocalWallet::from(eth_key.clone()).address(); // the destination on cosmos that sends along to the final ethereum destination let cosmos_key = CosmosPrivateKey::from_secret(&secret); let cosmos_address = cosmos_key @@ -136,8 +156,8 @@ pub fn get_user_key() -> BridgeUserKey { let mut rng = rand::thread_rng(); let secret: [u8; 32] = rng.gen(); // the final destination of the tokens back on Ethereum - let eth_dest_key = EthPrivateKey::from_slice(&secret).unwrap(); - let eth_dest_address = eth_key.to_public_key().unwrap(); + let eth_dest_key = SigningKey::from_bytes(&secret).unwrap(); + let eth_dest_address = LocalWallet::from(eth_dest_key.clone()).address(); BridgeUserKey { eth_address, eth_key, @@ -151,19 +171,19 @@ pub fn get_user_key() -> BridgeUserKey { pub struct BridgeUserKey { // the starting addresses that get Eth balances to send across the bridge pub eth_address: EthAddress, - pub eth_key: EthPrivateKey, + pub eth_key: SigningKey, // the cosmos addresses that get the funds and send them on to the dest eth addresses pub cosmos_address: CosmosAddress, pub cosmos_key: CosmosPrivateKey, // the location tokens are sent back to on Ethereum pub eth_dest_address: EthAddress, - pub eth_dest_key: EthPrivateKey, + pub eth_dest_key: SigningKey, } #[derive(Debug, Clone)] pub struct ValidatorKeys { /// The Ethereum key used by this validator to sign Gravity bridge messages - pub eth_key: EthPrivateKey, + pub eth_key: SigningKey, /// The Orchestrator key used by this validator to submit oracle messages and signatures /// to the cosmos chain pub orch_key: CosmosPrivateKey, diff --git a/orchestrator/test_runner/src/valset_stress.rs b/orchestrator/test_runner/src/valset_stress.rs index 33bbf3ac8..215b5e28e 100644 --- a/orchestrator/test_runner/src/valset_stress.rs +++ b/orchestrator/test_runner/src/valset_stress.rs @@ -1,16 +1,14 @@ use crate::happy_path::test_valset_update; use crate::utils::ValidatorKeys; -use clarity::Address as EthAddress; use deep_space::Contact; -use web30::client::Web3; +use ethers::types::Address as EthAddress; pub async fn validator_set_stress_test( - web30: &Web3, contact: &Contact, keys: Vec, gravity_address: EthAddress, ) { for _ in 0u32..10 { - test_valset_update(web30, contact, &keys, gravity_address).await; + test_valset_update(contact, &keys, gravity_address).await; } }