From 28043ae8342fdae090ac9fb6c9c6f5987d1ed5c2 Mon Sep 17 00:00:00 2001 From: bitful-pannul Date: Thu, 1 Aug 2024 22:15:20 +0300 Subject: [PATCH 01/17] kit publish: init --- Cargo.lock | 963 +++++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 18 + src/lib.rs | 1 + src/main.rs | 84 ++-- src/publish/mod.rs | 231 +++++++++++ 5 files changed, 1227 insertions(+), 70 deletions(-) create mode 100644 src/publish/mod.rs diff --git a/Cargo.lock b/Cargo.lock index ff27fdf4..87a1455c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,6 +28,18 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -37,20 +49,124 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + +[[package]] +name = "alloy" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ba1c79677c9ce51c8d45e20845b05e6fb070ea2c863fba03ad6af2c778474bd" +dependencies = [ + "alloy-consensus 0.1.4", + "alloy-contract", + "alloy-core", + "alloy-eips 0.1.4", + "alloy-genesis 0.1.4", + "alloy-json-rpc 0.1.4", + "alloy-network", + "alloy-provider", + "alloy-pubsub", + "alloy-rpc-client", + "alloy-rpc-types 0.1.4", + "alloy-serde 0.1.4", + "alloy-signer", + "alloy-signer-local", + "alloy-transport 0.1.4", + "alloy-transport-http", + "alloy-transport-ws", +] + +[[package]] +name = "alloy-chains" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47ff94ce0f141c2671c23d02c7b88990dd432856639595c5d010663d017c2c58" +dependencies = [ + "num_enum", + "strum", +] + [[package]] name = "alloy-consensus" version = "0.1.0" source = "git+https://github.com/alloy-rs/alloy.git?rev=cad7935#cad7935d69f433e45d190902e58b1c996b35adfa" dependencies = [ - "alloy-eips", + "alloy-eips 0.1.0", "alloy-primitives", "alloy-rlp", - "alloy-serde", + "alloy-serde 0.1.0", "c-kzg", "serde", "sha2", ] +[[package]] +name = "alloy-consensus" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da374e868f54c7f4ad2ad56829827badca388efd645f8cf5fccc61c2b5343504" +dependencies = [ + "alloy-eips 0.1.4", + "alloy-primitives", + "alloy-rlp", + "alloy-serde 0.1.4", + "c-kzg", + "serde", +] + +[[package]] +name = "alloy-contract" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dc6957ff706f9e5f6fd42f52a93e4bce476b726c92d077b348de28c4a76730c" +dependencies = [ + "alloy-dyn-abi", + "alloy-json-abi", + "alloy-network", + "alloy-primitives", + "alloy-provider", + "alloy-pubsub", + "alloy-rpc-types-eth", + "alloy-sol-types", + "alloy-transport 0.1.4", + "futures", + "futures-util", + "thiserror", +] + +[[package]] +name = "alloy-core" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "529fc6310dc1126c8de51c376cbc59c79c7f662bd742be7dc67055d5421a81b4" +dependencies = [ + "alloy-dyn-abi", + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-types", +] + +[[package]] +name = "alloy-dyn-abi" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413902aa18a97569e60f679c23f46a18db1656d87ab4d4e49d0e1e52042f66df" +dependencies = [ + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-type-parser", + "alloy-sol-types", + "const-hex", + "itoa", + "serde", + "serde_json", + "winnow 0.6.13", +] + [[package]] name = "alloy-eips" version = "0.1.0" @@ -58,10 +174,25 @@ source = "git+https://github.com/alloy-rs/alloy.git?rev=cad7935#cad7935d69f433e4 dependencies = [ "alloy-primitives", "alloy-rlp", - "alloy-serde", + "alloy-serde 0.1.0", + "c-kzg", + "once_cell", + "serde", +] + +[[package]] +name = "alloy-eips" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f76ecab54890cdea1e4808fc0891c7e6cfcf71fe1a9fe26810c7280ef768f4ed" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "alloy-serde 0.1.4", "c-kzg", "once_cell", "serde", + "sha2", ] [[package]] @@ -70,10 +201,33 @@ version = "0.1.0" source = "git+https://github.com/alloy-rs/alloy.git?rev=cad7935#cad7935d69f433e45d190902e58b1c996b35adfa" dependencies = [ "alloy-primitives", - "alloy-serde", + "alloy-serde 0.1.0", + "serde", +] + +[[package]] +name = "alloy-genesis" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bca15afde1b6d15e3fc1c97421262b1bbb37aee45752e3c8b6d6f13f776554ff" +dependencies = [ + "alloy-primitives", + "alloy-serde 0.1.4", "serde", ] +[[package]] +name = "alloy-json-abi" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc05b04ac331a9f07e3a4036ef7926e49a8bf84a99a1ccfc7e2ab55a5fcbb372" +dependencies = [ + "alloy-primitives", + "alloy-sol-type-parser", + "serde", + "serde_json", +] + [[package]] name = "alloy-json-rpc" version = "0.1.0" @@ -86,11 +240,44 @@ dependencies = [ "tracing", ] +[[package]] +name = "alloy-json-rpc" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d6f34930b7e3e2744bcc79056c217f00cb2abb33bc5d4ff88da7623c5bb078b" +dependencies = [ + "alloy-primitives", + "serde", + "serde_json", + "thiserror", + "tracing", +] + +[[package]] +name = "alloy-network" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25f6895fc31b48fa12306ef9b4f78b7764f8bd6d7d91cdb0a40e233704a0f23f" +dependencies = [ + "alloy-consensus 0.1.4", + "alloy-eips 0.1.4", + "alloy-json-rpc 0.1.4", + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-serde 0.1.4", + "alloy-signer", + "alloy-sol-types", + "async-trait", + "auto_impl", + "futures-utils-wasm", + "thiserror", +] + [[package]] name = "alloy-primitives" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f783611babedbbe90db3478c120fb5f5daacceffc210b39adc0af4fe0da70bad" +checksum = "ccb3ead547f4532bc8af961649942f0b9c16ee9226e26caa3f38420651cc0bf4" dependencies = [ "alloy-rlp", "bytes", @@ -108,6 +295,59 @@ dependencies = [ "tiny-keccak", ] +[[package]] +name = "alloy-provider" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c538bfa893d07e27cb4f3c1ab5f451592b7c526d511d62b576a2ce59e146e4a" +dependencies = [ + "alloy-chains", + "alloy-consensus 0.1.4", + "alloy-eips 0.1.4", + "alloy-json-rpc 0.1.4", + "alloy-network", + "alloy-primitives", + "alloy-pubsub", + "alloy-rpc-client", + "alloy-rpc-types-eth", + "alloy-transport 0.1.4", + "alloy-transport-http", + "alloy-transport-ws", + "async-stream", + "async-trait", + "auto_impl", + "dashmap", + "futures", + "futures-utils-wasm", + "lru", + "pin-project", + "reqwest 0.12.5", + "serde", + "serde_json", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "alloy-pubsub" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a7341322d9bc0e49f6e9fd9f2eb8e30f73806f2dd12cbb3d6bab2694c921f87" +dependencies = [ + "alloy-json-rpc 0.1.4", + "alloy-primitives", + "alloy-transport 0.1.4", + "bimap", + "futures", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tower", + "tracing", +] + [[package]] name = "alloy-rlp" version = "0.3.5" @@ -130,17 +370,41 @@ dependencies = [ "syn 2.0.66", ] +[[package]] +name = "alloy-rpc-client" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ba31bae67773fd5a60020bea900231f8396202b7feca4d0c70c6b59308ab4a8" +dependencies = [ + "alloy-json-rpc 0.1.4", + "alloy-primitives", + "alloy-pubsub", + "alloy-transport 0.1.4", + "alloy-transport-http", + "alloy-transport-ws", + "futures", + "pin-project", + "reqwest 0.12.5", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tower", + "tracing", + "url", +] + [[package]] name = "alloy-rpc-types" version = "0.1.0" source = "git+https://github.com/alloy-rs/alloy.git?rev=cad7935#cad7935d69f433e45d190902e58b1c996b35adfa" dependencies = [ - "alloy-consensus", - "alloy-eips", - "alloy-genesis", + "alloy-consensus 0.1.0", + "alloy-eips 0.1.0", + "alloy-genesis 0.1.0", "alloy-primitives", "alloy-rlp", - "alloy-serde", + "alloy-serde 0.1.0", "alloy-sol-types", "itertools 0.12.1", "serde", @@ -148,6 +412,34 @@ dependencies = [ "thiserror", ] +[[package]] +name = "alloy-rpc-types" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "184a7a42c7ba9141cc9e76368356168c282c3bc3d9e5d78f3556bdfe39343447" +dependencies = [ + "alloy-rpc-types-eth", + "alloy-serde 0.1.4", +] + +[[package]] +name = "alloy-rpc-types-eth" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab4123ee21f99ba4bd31bfa36ba89112a18a500f8b452f02b35708b1b951e2b9" +dependencies = [ + "alloy-consensus 0.1.4", + "alloy-eips 0.1.4", + "alloy-primitives", + "alloy-rlp", + "alloy-serde 0.1.4", + "alloy-sol-types", + "itertools 0.13.0", + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "alloy-serde" version = "0.1.0" @@ -158,11 +450,52 @@ dependencies = [ "serde_json", ] +[[package]] +name = "alloy-serde" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9416c52959e66ead795a11f4a86c248410e9e368a0765710e57055b8a1774dd6" +dependencies = [ + "alloy-primitives", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-signer" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b33753c09fa1ad85e5b092b8dc2372f1e337a42e84b9b4cff9fede75ba4adb32" +dependencies = [ + "alloy-primitives", + "async-trait", + "auto_impl", + "elliptic-curve", + "k256", + "thiserror", +] + +[[package]] +name = "alloy-signer-local" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dfc9c26fe6c6f1bad818c9a976de9044dd12e1f75f1f156a801ee3e8148c1b6" +dependencies = [ + "alloy-consensus 0.1.4", + "alloy-network", + "alloy-primitives", + "alloy-signer", + "async-trait", + "k256", + "rand", + "thiserror", +] + [[package]] name = "alloy-sol-macro" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bad41a7c19498e3f6079f7744656328699f8ea3e783bdd10d85788cd439f572" +checksum = "2b40397ddcdcc266f59f959770f601ce1280e699a91fc1862f29cef91707cd09" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", @@ -174,10 +507,11 @@ dependencies = [ [[package]] name = "alloy-sol-macro-expander" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd9899da7d011b4fe4c406a524ed3e3f963797dbc93b45479d60341d3a27b252" +checksum = "867a5469d61480fea08c7333ffeca52d5b621f5ca2e44f271b117ec1fc9a0525" dependencies = [ + "alloy-json-abi", "alloy-sol-macro-input", "const-hex", "heck 0.5.0", @@ -192,28 +526,42 @@ dependencies = [ [[package]] name = "alloy-sol-macro-input" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32d595768fdc61331a132b6f65db41afae41b9b97d36c21eb1b955c422a7e60" +checksum = "2e482dc33a32b6fadbc0f599adea520bd3aaa585c141a80b404d0a3e3fa72528" dependencies = [ + "alloy-json-abi", "const-hex", "dunce", "heck 0.5.0", "proc-macro2", "quote", + "serde_json", "syn 2.0.66", "syn-solidity", ] +[[package]] +name = "alloy-sol-type-parser" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbcba3ca07cf7975f15d871b721fb18031eec8bce51103907f6dcce00b255d98" +dependencies = [ + "serde", + "winnow 0.6.13", +] + [[package]] name = "alloy-sol-types" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a49042c6d3b66a9fe6b2b5a8bf0d39fc2ae1ee0310a2a26ffedd79fb097878dd" +checksum = "a91ca40fa20793ae9c3841b83e74569d1cc9af29a2f5237314fd3452d51e38c7" dependencies = [ + "alloy-json-abi", "alloy-primitives", "alloy-sol-macro", "const-hex", + "serde", ] [[package]] @@ -221,7 +569,7 @@ name = "alloy-transport" version = "0.1.0" source = "git+https://github.com/alloy-rs/alloy.git?rev=cad7935#cad7935d69f433e45d190902e58b1c996b35adfa" dependencies = [ - "alloy-json-rpc", + "alloy-json-rpc 0.1.0", "base64 0.22.1", "futures-util", "futures-utils-wasm", @@ -234,6 +582,58 @@ dependencies = [ "wasm-bindgen-futures", ] +[[package]] +name = "alloy-transport" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01b51a291f949f755e6165c3ed562883175c97423703703355f4faa4b7d0a57c" +dependencies = [ + "alloy-json-rpc 0.1.4", + "base64 0.22.1", + "futures-util", + "futures-utils-wasm", + "serde", + "serde_json", + "thiserror", + "tokio", + "tower", + "tracing", + "url", +] + +[[package]] +name = "alloy-transport-http" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86d65871f9f1cafe1ed25cde2f1303be83e6473e995a2d56c275ae4fcce6119c" +dependencies = [ + "alloy-json-rpc 0.1.4", + "alloy-transport 0.1.4", + "reqwest 0.12.5", + "serde_json", + "tower", + "tracing", + "url", +] + +[[package]] +name = "alloy-transport-ws" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aec83fd052684556c78c54df111433493267234d82321c2236560c752f595f20" +dependencies = [ + "alloy-pubsub", + "alloy-transport 0.1.4", + "futures", + "http 1.1.0", + "rustls", + "serde_json", + "tokio", + "tokio-tungstenite", + "tracing", + "ws_stream_wasm", +] + [[package]] name = "anstream" version = "0.6.14" @@ -419,6 +819,50 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "async-trait" +version = "0.1.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[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 = "auto_impl" version = "1.2.0" @@ -475,6 +919,12 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bimap" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "230c5f1ca6a325a32553f8640d31ac9b49f2411e901e427570154868b46da4f7" + [[package]] name = "bincode" version = "1.3.3" @@ -801,6 +1251,19 @@ dependencies = [ "typenum", ] +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core 0.9.10", +] + [[package]] name = "data-encoding" version = "2.6.0" @@ -1077,6 +1540,21 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.30" @@ -1084,6 +1562,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -1092,6 +1571,23 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + [[package]] name = "futures-macro" version = "0.3.30" @@ -1121,10 +1617,13 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ + "futures-channel", "futures-core", + "futures-io", "futures-macro", "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", "slab", @@ -1220,6 +1719,10 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] [[package]] name = "heck" @@ -1292,10 +1795,33 @@ dependencies = [ name = "http-body" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.1.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", - "http 0.2.12", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", "pin-project-lite", ] @@ -1323,7 +1849,7 @@ dependencies = [ "futures-util", "h2", "http 0.2.12", - "http-body", + "http-body 0.4.6", "httparse", "httpdate", "itoa", @@ -1335,6 +1861,25 @@ dependencies = [ "want", ] +[[package]] +name = "hyper" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + [[package]] name = "hyper-tls" version = "0.5.0" @@ -1342,10 +1887,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes", - "hyper", + "hyper 0.14.29", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper 1.4.1", + "hyper-util", "native-tls", "tokio", "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "hyper 1.4.1", + "pin-project-lite", + "socket2", + "tokio", + "tower", + "tower-service", + "tracing", ] [[package]] @@ -1569,6 +2150,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -1621,10 +2211,10 @@ name = "kinode_process_lib" version = "0.8.0" source = "git+https://github.com/kinode-dao/process_lib.git?rev=7eb3a04#7eb3a04f9be79d1cc3a52fa460faeea7ba3008ed" dependencies = [ - "alloy-json-rpc", + "alloy-json-rpc 0.1.0", "alloy-primitives", - "alloy-rpc-types", - "alloy-transport", + "alloy-rpc-types 0.1.0", + "alloy-transport 0.1.0", "anyhow", "bincode", "http 1.1.0", @@ -1642,6 +2232,9 @@ dependencies = [ name = "kit" version = "0.6.2" dependencies = [ + "alloy", + "alloy-sol-macro", + "alloy-sol-types", "anyhow", "base64 0.21.7", "clap", @@ -1654,7 +2247,7 @@ dependencies = [ "kinode_process_lib", "nix", "regex", - "reqwest", + "reqwest 0.11.27", "rmp-serde", "semver 1.0.23", "serde", @@ -1776,6 +2369,15 @@ version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +[[package]] +name = "lru" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" +dependencies = [ + "hashbrown", +] + [[package]] name = "matchers" version = "0.1.0" @@ -1910,6 +2512,26 @@ dependencies = [ "libc", ] +[[package]] +name = "num_enum" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "object" version = "0.32.2" @@ -2021,7 +2643,7 @@ checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ "instant", "lock_api", - "parking_lot_core", + "parking_lot_core 0.8.6", ] [[package]] @@ -2033,11 +2655,24 @@ dependencies = [ "cfg-if", "instant", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", "winapi", ] +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.3", + "smallvec", + "windows-targets 0.52.5", +] + [[package]] name = "password-hash" version = "0.4.2" @@ -2084,6 +2719,16 @@ dependencies = [ "ucd-trie", ] +[[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 = "1.1.5" @@ -2286,6 +2931,15 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +dependencies = [ + "bitflags 2.5.0", +] + [[package]] name = "redox_users" version = "0.4.5" @@ -2354,9 +3008,9 @@ dependencies = [ "futures-util", "h2", "http 0.2.12", - "http-body", - "hyper", - "hyper-tls", + "http-body 0.4.6", + "hyper 0.14.29", + "hyper-tls 0.5.0", "ipnet", "js-sys", "log", @@ -2365,11 +3019,11 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls-pemfile", + "rustls-pemfile 1.0.4", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 0.1.2", "system-configuration", "tokio", "tokio-native-tls", @@ -2378,7 +3032,46 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "winreg", + "winreg 0.50.0", +] + +[[package]] +name = "reqwest" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-core", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.4.1", + "hyper-tls 0.6.0", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile 2.1.2", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 1.0.1", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg 0.52.0", ] [[package]] @@ -2391,6 +3084,21 @@ dependencies = [ "subtle", ] +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rlp" version = "0.5.2" @@ -2496,6 +3204,20 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustls" +version = "0.23.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -2505,6 +3227,39 @@ dependencies = [ "base64 0.21.7", ] +[[package]] +name = "rustls-pemfile" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +dependencies = [ + "base64 0.22.1", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" + +[[package]] +name = "rustls-webpki" +version = "0.102.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + [[package]] name = "rusty-fork" version = "0.3.0" @@ -2608,6 +3363,12 @@ dependencies = [ "pest", ] +[[package]] +name = "send_wrapper" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" + [[package]] name = "serde" version = "1.0.203" @@ -2754,6 +3515,12 @@ dependencies = [ "smallvec", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "spki" version = "0.7.3" @@ -2794,6 +3561,28 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.66", +] + [[package]] name = "subtle" version = "2.5.0" @@ -2824,9 +3613,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d71e19bca02c807c9faa67b5a47673ff231b6e7449b251695188522f1dc44b2" +checksum = "c837dc8852cb7074e46b444afb81783140dab12c58867b49fb3898fbafedf7ea" dependencies = [ "paste", "proc-macro2", @@ -2840,6 +3629,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" + [[package]] name = "synstructure" version = "0.13.1" @@ -3018,6 +3813,29 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls", + "rustls-pki-types", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util", +] + [[package]] name = "tokio-tungstenite" version = "0.23.0" @@ -3026,8 +3844,12 @@ checksum = "becd34a233e7e31a3dbf7c7241b38320f57393dcae8e7324b0167d21b8e320b0" dependencies = [ "futures-util", "log", + "rustls", + "rustls-pki-types", "tokio", + "tokio-rustls", "tungstenite", + "webpki-roots", ] [[package]] @@ -3098,6 +3920,7 @@ dependencies = [ "futures-util", "pin-project", "pin-project-lite", + "tokio", "tower-layer", "tower-service", "tracing", @@ -3231,6 +4054,8 @@ dependencies = [ "httparse", "log", "rand", + "rustls", + "rustls-pki-types", "sha1", "thiserror", "utf-8", @@ -3293,6 +4118,12 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.5.1" @@ -3492,6 +4323,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki-roots" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "winapi" version = "0.3.9" @@ -3690,6 +4530,16 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "winreg" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "wit-bindgen" version = "0.24.0" @@ -3796,6 +4646,25 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +[[package]] +name = "ws_stream_wasm" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7999f5f4217fe3818726b66257a4475f71e74ffd190776ad053fa159e50737f5" +dependencies = [ + "async_io_stream", + "futures", + "js-sys", + "log", + "pharos", + "rustc_version 0.4.0", + "send_wrapper", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "wyz" version = "0.5.1" @@ -3829,6 +4698,26 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "zerofrom" version = "0.1.4" diff --git a/Cargo.toml b/Cargo.toml index c60558f7..9664eecd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,24 @@ anyhow = "1.0" git2 = "0.18" [dependencies] +alloy = { version = "0.1.3", features = [ + "consensus", + "contract", + "json-rpc", + "network", + "provider-ws", + "providers", + "pubsub", + "rpc", + "rpc-client", + "rpc-client-ws", + "rpc-types", + "rpc-types-eth", + "signers", + "signer-local", +] } +alloy-sol-macro = "0.7.6" +alloy-sol-types = "0.7.6" base64 = "0.21" clap = { version = "4.4", features = ["cargo", "string"] } color-eyre = { version = "0.6", features = ["capture-spantrace"] } diff --git a/src/lib.rs b/src/lib.rs index 0edeccdf..dac74098 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,7 @@ pub mod connect; pub mod dev_ui; pub mod inject_message; pub mod new; +pub mod publish; pub mod remove_package; pub mod reset_cache; pub mod run_tests; diff --git a/src/main.rs b/src/main.rs index b767eb51..76d48a6b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,10 @@ use std::env; use std::path::PathBuf; use std::str::FromStr; -use color_eyre::{eyre::{eyre, Result}, Section}; +use color_eyre::{ + eyre::{eyre, Result}, + Section, +}; use fs_err as fs; use serde::Deserialize; use tracing::{error, warn, Level}; @@ -13,23 +16,9 @@ use tracing_subscriber::{ }; use kit::{ - boot_fake_node, - boot_real_node, - build, - build_start_package, - chain, - connect, - dev_ui, - inject_message, - new, - remove_package, - reset_cache, - run_tests, - setup, - start_package, - update, - view_api, - KIT_LOG_PATH_DEFAULT, + boot_fake_node, boot_real_node, build, build_start_package, chain, connect, dev_ui, + inject_message, new, publish, remove_package, reset_cache, run_tests, setup, start_package, + update, view_api, KIT_LOG_PATH_DEFAULT, }; const MAX_REMOTE_VALUES: usize = 3; @@ -61,13 +50,8 @@ async fn get_latest_commit_sha_from_branch( fn init_tracing(log_path: PathBuf) -> tracing_appender::non_blocking::WorkerGuard { // Define a fixed log file name with rolling based on size or execution instance. - let log_parent_path = log_path - .parent() - .unwrap(); - let log_file_name = log_path - .file_name() - .and_then(|f| f.to_str()) - .unwrap(); + let log_parent_path = log_path.parent().unwrap(); + let log_file_name = log_path.file_name().and_then(|f| f.to_str()).unwrap(); if !log_parent_path.exists() { fs::create_dir_all(log_parent_path).unwrap(); } @@ -205,10 +189,9 @@ async fn execute( }; let url: Option = match build_matches.get_one::("URL") { Some(url) => Some(url.clone()), - None => { - build_matches.get_one::("NODE_PORT") - .map(|p| format!("http://localhost:{}", p)) - } + None => build_matches + .get_one::("NODE_PORT") + .map(|p| format!("http://localhost:{}", p)), }; let default_world = build_matches.get_one::("WORLD"); let verbose = build_matches.get_one::("VERBOSE").unwrap(); @@ -222,7 +205,8 @@ async fn execute( url, default_world.cloned(), *verbose, - ).await + ) + .await } Some(("build-start-package", build_start_matches)) => { let package_dir = PathBuf::from(build_start_matches.get_one::("DIR").unwrap()); @@ -267,9 +251,11 @@ async fn execute( Some(("connect", connect_matches)) => { let local_port = connect_matches.get_one::("LOCAL_PORT").unwrap(); let disconnect = connect_matches.get_one::("IS_DISCONNECT").unwrap(); - let host = connect_matches.get_one::("HOST") + let host = connect_matches + .get_one::("HOST") .map(|s| s.as_ref()); - let host_port = connect_matches.get_one::("HOST_PORT") + let host_port = connect_matches + .get_one::("HOST_PORT") .map(|hp| hp.clone()); connect::execute(*local_port, *disconnect, host, host_port) } @@ -327,6 +313,14 @@ async fn execute( *ui, ) } + Some(("publish", publish_matches)) => { + let private_key = publish_matches.get_one::("PRIVATE_KEY").cloned(); + let app_name = publish_matches.get_one::("APP_NAME").cloned(); + let fakechain_port = *publish_matches.get_one::("FAKECHAIN_PORT").unwrap(); + let manifest_path = publish_matches.get_one::("MANIFEST").unwrap(); + + publish::execute(private_key, app_name, fakechain_port, manifest_path).await + } Some(("remove-package", remove_package_matches)) => { let package_name = remove_package_matches .get_one::("PACKAGE") @@ -892,6 +886,28 @@ async fn make_app(current_dir: &std::ffi::OsString) -> Result { .required(false) ) ) + .subcommand(Command::new("publish") + .about("Publish or update an app") + .visible_alias("p") + .arg(Arg::new("PRIVATE_KEY") + .short('k') + .long("private-key") + .help("Private key for transaction signing") + .required(false) + ) + .arg(Arg::new("APP_NAME") + .short('n') + .long("app-name") + .help("Name of the app to publish") + .required(false) + ) + .arg(Arg::new("MANIFEST") + .short('m') + .long("manifest") + .help("Path to the manifest file") + .default_value("manifest.json") + ) + ) .subcommand(Command::new("remove-package") .about("Remove a running package from a node") .visible_alias("r") @@ -1054,7 +1070,9 @@ async fn main() -> Result<()> { boot_fake_node::KINODE_OWNER, KIT_REPO, KIT_MASTER_BRANCH, - ).await? { + ) + .await? + { if GIT_COMMIT_HASH != latest.sha { warn!("kit is out of date! Run:\n```\nkit update\n```\nto update to the latest version."); } diff --git a/src/publish/mod.rs b/src/publish/mod.rs new file mode 100644 index 00000000..97cade93 --- /dev/null +++ b/src/publish/mod.rs @@ -0,0 +1,231 @@ +use color_eyre::eyre::{self, Result}; +use reqwest::Client; +use serde_json::Value; +use std::fs; +use std::path::Path; +use std::str::FromStr; + +use alloy::{ + network::{eip2718::Encodable2718, EthereumWallet, TransactionBuilder}, + primitives::{keccak256, Address, Bytes, U256}, + providers::{Provider, ProviderBuilder, RootProvider}, + pubsub::PubSubFrontend, + rpc::client::WsConnect, + rpc::types::eth::{TransactionInput, TransactionRequest}, + signers::local::PrivateKeySigner, +}; +use alloy_sol_macro::sol; +use alloy_sol_types::SolCall; + +sol! { + function mint ( + address who, + bytes calldata label, + bytes calldata initialization, + bytes calldata erc721Data, + address implementation + ) external returns ( + address tba + ); + + function get ( + bytes32 node + ) external view returns ( + address tba, + address owner, + bytes data, + ); + + function note ( + bytes calldata note, + bytes calldata data + ) external returns ( + bytes32 notenode + ); + + // tba account + function execute( + address to, + uint256 value, + bytes calldata data, + uint8 operation + ) external payable returns (bytes memory returnData); + + + struct Call { + address target; + bytes callData; + } + + function aggregate( + Call[] calldata calls + ) external payable returns (uint256 blockNumber, bytes[] memory returnData); +} + +const KIMAP_ADDRESS: &str = "0x0165878A594ca255338adfa4d48449f69242Eb8F"; +const MULTICALL_ADDRESS: &str = "0xcA11bde05977b3631167028862bE2a173976CA11"; +const KINO_ACCOUNT_IMPL: &str = "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9"; +const FAKE_DOTDEV_TBA: &str = "0x69C30C0Cf0e9726f9eEF50bb74FA32711fA0B02D"; + +pub async fn execute( + private_key: Option, + app_name: Option, + fakechain_port: u16, + manifest_path: &str, +) -> Result<()> { + let private_key = get_private_key(private_key)?; + let app_name = get_app_name(app_name, manifest_path)?; + + let privkey_signer = + PrivateKeySigner::from_str(&private_key).expect("Failed to create signer from private key"); + + let wallet_address = privkey_signer.address(); + let wallet: EthereumWallet = privkey_signer.into(); + + let endpoint = format!("ws://localhost:{}", fakechain_port); + let ws = WsConnect::new(endpoint); + let provider: RootProvider = ProviderBuilder::default().on_ws(ws).await?; + + let kimap = Address::from_str(KIMAP_ADDRESS)?; + let multicall_address = Address::from_str(MULTICALL_ADDRESS)?; + let fakedotdev_tba = Address::from_str(FAKE_DOTDEV_TBA)?; + let kino_account_impl = Address::from_str(KINO_ACCOUNT_IMPL)?; + + // Create metadata calls + let metadata_uri = get_metadata_uri(manifest_path)?; + let metadata_hash = calculate_metadata_hash(&metadata_uri).await?; + + let metadata_uri_call = noteCall { + note: "~metadata-uri".into(), + data: metadata_uri.into(), + } + .abi_encode(); + + let metadata_hash_call = noteCall { + note: "~metadata-hash".into(), + data: metadata_hash.into(), + } + .abi_encode(); + + let calls = vec![ + Call { + target: kimap, + callData: metadata_uri_call.into(), + }, + Call { + target: kimap, + callData: metadata_hash_call.into(), + }, + ]; + + let notes_multicall = aggregateCall { calls }.abi_encode(); + + let init_call = executeCall { + to: multicall_address, + value: U256::from(0), + data: notes_multicall.into(), + operation: 1, + } + .abi_encode(); + + let create_app_tba_call = mintCall { + who: wallet_address, + label: app_name.clone().into(), + initialization: init_call.into(), + erc721Data: Bytes::default(), + implementation: kino_account_impl, + } + .abi_encode(); + + let create_app_call = executeCall { + to: kimap, + value: U256::from(0), + data: create_app_tba_call.into(), + operation: 0, + } + .abi_encode(); + + let nonce = provider.get_transaction_count(wallet_address).await?; + + let tx = TransactionRequest::default() + .to(fakedotdev_tba) + .input(TransactionInput::new(create_app_call.into())) + .nonce(nonce) + .with_chain_id(31337) + .with_gas_limit(1_000_000) + .with_max_priority_fee_per_gas(200_000_000_000) + .with_max_fee_per_gas(300_000_000_000); + + let tx_envelope = tx.build(&wallet).await?; + let tx_encoded = tx_envelope.encoded_2718(); + let tx_hash = provider.send_raw_transaction(&tx_encoded).await?; + + println!("App '{}' published successfully!", app_name); + println!("Transaction hash: {:?}", tx_hash); + Ok(()) +} + +fn get_metadata_uri(manifest_path: &str) -> Result { + let manifest_path = Path::new(manifest_path); + if manifest_path.exists() { + let contents = fs::read_to_string(manifest_path)?; + let manifest: Value = serde_json::from_str(&contents)?; + if let Some(uri) = manifest["metadata_uri"].as_str() { + return Ok(uri.to_string()); + } + } + Err(eyre::eyre!("Metadata URI not found in the manifest file.")) +} + +async fn calculate_metadata_hash(metadata_uri: &str) -> Result { + let client = Client::new(); + let response = client.get(metadata_uri).send().await?; + let metadata_text = response.text().await?; + + let _: Value = serde_json::from_str(&metadata_text)?; + + let hash = keccak256(metadata_text.as_bytes()); + + Ok(format!("0x{}", hex::encode(hash))) +} + +fn get_private_key(cli_key: Option) -> Result { + if let Some(key) = cli_key { + return Ok(key); + } + + if let Ok(key) = std::env::var("PRIVATE_KEY") { + return Ok(key); + } + + let env_path = Path::new(".env"); + if env_path.exists() { + let contents = fs::read_to_string(env_path)?; + for line in contents.lines() { + if line.starts_with("PRIVATE_KEY=") { + return Ok(line.trim_start_matches("PRIVATE_KEY=").to_string()); + } + } + } + + Err(eyre::eyre!("Private key not found. Please provide it as an argument, set the PRIVATE_KEY environment variable, or include it in a .env file.")) +} + +fn get_app_name(cli_name: Option, manifest_path: &str) -> Result { + if let Some(name) = cli_name { + return Ok(name); + } + + let manifest_path = Path::new(manifest_path); + if manifest_path.exists() { + let contents = fs::read_to_string(manifest_path)?; + let manifest: Value = serde_json::from_str(&contents)?; + if let Some(name) = manifest["name"].as_str() { + return Ok(name.to_string()); + } + } + + Err(eyre::eyre!( + "App name not found. Please provide it as an argument or include it in the manifest file." + )) +} From b045672b9f76f23e7893cf3ecd8053a190e51d4a Mon Sep 17 00:00:00 2001 From: hosted-fornet Date: Thu, 1 Aug 2024 14:54:47 -0700 Subject: [PATCH 02/17] publish: tweak whitespace --- src/publish/mod.rs | 82 +++++++++++++++++++++++----------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/src/publish/mod.rs b/src/publish/mod.rs index 97cade93..dc744a3b 100644 --- a/src/publish/mod.rs +++ b/src/publish/mod.rs @@ -18,48 +18,48 @@ use alloy_sol_macro::sol; use alloy_sol_types::SolCall; sol! { - function mint ( - address who, - bytes calldata label, - bytes calldata initialization, - bytes calldata erc721Data, - address implementation - ) external returns ( - address tba - ); - - function get ( - bytes32 node - ) external view returns ( - address tba, - address owner, - bytes data, - ); - - function note ( - bytes calldata note, - bytes calldata data - ) external returns ( - bytes32 notenode - ); - - // tba account - function execute( - address to, - uint256 value, - bytes calldata data, - uint8 operation - ) external payable returns (bytes memory returnData); - - - struct Call { - address target; - bytes callData; - } + function mint ( + address who, + bytes calldata label, + bytes calldata initialization, + bytes calldata erc721Data, + address implementation + ) external returns ( + address tba + ); + + function get ( + bytes32 node + ) external view returns ( + address tba, + address owner, + bytes data, + ); + + function note ( + bytes calldata note, + bytes calldata data + ) external returns ( + bytes32 notenode + ); + + // tba account + function execute( + address to, + uint256 value, + bytes calldata data, + uint8 operation + ) external payable returns (bytes memory returnData); + + + struct Call { + address target; + bytes callData; + } - function aggregate( - Call[] calldata calls - ) external payable returns (uint256 blockNumber, bytes[] memory returnData); + function aggregate( + Call[] calldata calls + ) external payable returns (uint256 blockNumber, bytes[] memory returnData); } const KIMAP_ADDRESS: &str = "0x0165878A594ca255338adfa4d48449f69242Eb8F"; From 85cd32a43065a02440a1c88f9743e1ce61940306 Mon Sep 17 00:00:00 2001 From: hosted-fornet Date: Thu, 1 Aug 2024 15:43:27 -0700 Subject: [PATCH 03/17] publish: execute() -> end of file --- src/publish/mod.rs | 130 ++++++++++++++++++++++----------------------- 1 file changed, 65 insertions(+), 65 deletions(-) diff --git a/src/publish/mod.rs b/src/publish/mod.rs index dc744a3b..5c1060f1 100644 --- a/src/publish/mod.rs +++ b/src/publish/mod.rs @@ -67,6 +67,71 @@ const MULTICALL_ADDRESS: &str = "0xcA11bde05977b3631167028862bE2a173976CA11"; const KINO_ACCOUNT_IMPL: &str = "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9"; const FAKE_DOTDEV_TBA: &str = "0x69C30C0Cf0e9726f9eEF50bb74FA32711fA0B02D"; +fn get_metadata_uri(manifest_path: &str) -> Result { + let manifest_path = Path::new(manifest_path); + if manifest_path.exists() { + let contents = fs::read_to_string(manifest_path)?; + let manifest: Value = serde_json::from_str(&contents)?; + if let Some(uri) = manifest["metadata_uri"].as_str() { + return Ok(uri.to_string()); + } + } + Err(eyre::eyre!("Metadata URI not found in the manifest file.")) +} + +async fn calculate_metadata_hash(metadata_uri: &str) -> Result { + let client = Client::new(); + let response = client.get(metadata_uri).send().await?; + let metadata_text = response.text().await?; + + let _: Value = serde_json::from_str(&metadata_text)?; + + let hash = keccak256(metadata_text.as_bytes()); + + Ok(format!("0x{}", hex::encode(hash))) +} + +fn get_private_key(cli_key: Option) -> Result { + if let Some(key) = cli_key { + return Ok(key); + } + + if let Ok(key) = std::env::var("PRIVATE_KEY") { + return Ok(key); + } + + let env_path = Path::new(".env"); + if env_path.exists() { + let contents = fs::read_to_string(env_path)?; + for line in contents.lines() { + if line.starts_with("PRIVATE_KEY=") { + return Ok(line.trim_start_matches("PRIVATE_KEY=").to_string()); + } + } + } + + Err(eyre::eyre!("Private key not found. Please provide it as an argument, set the PRIVATE_KEY environment variable, or include it in a .env file.")) +} + +fn get_app_name(cli_name: Option, manifest_path: &str) -> Result { + if let Some(name) = cli_name { + return Ok(name); + } + + let manifest_path = Path::new(manifest_path); + if manifest_path.exists() { + let contents = fs::read_to_string(manifest_path)?; + let manifest: Value = serde_json::from_str(&contents)?; + if let Some(name) = manifest["name"].as_str() { + return Ok(name.to_string()); + } + } + + Err(eyre::eyre!( + "App name not found. Please provide it as an argument or include it in the manifest file." + )) +} + pub async fn execute( private_key: Option, app_name: Option, @@ -164,68 +229,3 @@ pub async fn execute( println!("Transaction hash: {:?}", tx_hash); Ok(()) } - -fn get_metadata_uri(manifest_path: &str) -> Result { - let manifest_path = Path::new(manifest_path); - if manifest_path.exists() { - let contents = fs::read_to_string(manifest_path)?; - let manifest: Value = serde_json::from_str(&contents)?; - if let Some(uri) = manifest["metadata_uri"].as_str() { - return Ok(uri.to_string()); - } - } - Err(eyre::eyre!("Metadata URI not found in the manifest file.")) -} - -async fn calculate_metadata_hash(metadata_uri: &str) -> Result { - let client = Client::new(); - let response = client.get(metadata_uri).send().await?; - let metadata_text = response.text().await?; - - let _: Value = serde_json::from_str(&metadata_text)?; - - let hash = keccak256(metadata_text.as_bytes()); - - Ok(format!("0x{}", hex::encode(hash))) -} - -fn get_private_key(cli_key: Option) -> Result { - if let Some(key) = cli_key { - return Ok(key); - } - - if let Ok(key) = std::env::var("PRIVATE_KEY") { - return Ok(key); - } - - let env_path = Path::new(".env"); - if env_path.exists() { - let contents = fs::read_to_string(env_path)?; - for line in contents.lines() { - if line.starts_with("PRIVATE_KEY=") { - return Ok(line.trim_start_matches("PRIVATE_KEY=").to_string()); - } - } - } - - Err(eyre::eyre!("Private key not found. Please provide it as an argument, set the PRIVATE_KEY environment variable, or include it in a .env file.")) -} - -fn get_app_name(cli_name: Option, manifest_path: &str) -> Result { - if let Some(name) = cli_name { - return Ok(name); - } - - let manifest_path = Path::new(manifest_path); - if manifest_path.exists() { - let contents = fs::read_to_string(manifest_path)?; - let manifest: Value = serde_json::from_str(&contents)?; - if let Some(name) = manifest["name"].as_str() { - return Ok(name.to_string()); - } - } - - Err(eyre::eyre!( - "App name not found. Please provide it as an argument or include it in the manifest file." - )) -} From 181b929c154f4d094f8f34332260a24116d0424f Mon Sep 17 00:00:00 2001 From: hosted-fornet Date: Thu, 1 Aug 2024 15:48:49 -0700 Subject: [PATCH 04/17] publish: move around imports --- src/publish/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/publish/mod.rs b/src/publish/mod.rs index 5c1060f1..0d148e0b 100644 --- a/src/publish/mod.rs +++ b/src/publish/mod.rs @@ -1,6 +1,3 @@ -use color_eyre::eyre::{self, Result}; -use reqwest::Client; -use serde_json::Value; use std::fs; use std::path::Path; use std::str::FromStr; @@ -16,6 +13,9 @@ use alloy::{ }; use alloy_sol_macro::sol; use alloy_sol_types::SolCall; +use color_eyre::eyre::{self, Result}; +use reqwest::Client; +use serde_json::Value; sol! { function mint ( From 96c48430f046bef0b364268368a650bcd204cc1b Mon Sep 17 00:00:00 2001 From: hosted-fornet Date: Thu, 1 Aug 2024 18:04:27 -0700 Subject: [PATCH 05/17] publish: get compiling with new args --- Cargo.lock | 105 ++++++++++++++++++++++++++++++ Cargo.toml | 2 + src/main.rs | 58 +++++++++++------ src/publish/mod.rs | 156 ++++++++++++++++++++++----------------------- 4 files changed, 220 insertions(+), 101 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 87a1455c..911348a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -486,6 +486,8 @@ dependencies = [ "alloy-primitives", "alloy-signer", "async-trait", + "elliptic-curve", + "eth-keystore", "k256", "rand", "thiserror", @@ -1251,6 +1253,15 @@ dependencies = [ "typenum", ] +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + [[package]] name = "dashmap" version = "5.5.3" @@ -1436,6 +1447,28 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "eth-keystore" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fda3bf123be441da5260717e0661c25a2fd9cb2b2c1d20bf2e05580047158ab" +dependencies = [ + "aes", + "ctr", + "digest 0.10.7", + "hex", + "hmac", + "pbkdf2", + "rand", + "scrypt", + "serde", + "serde_json", + "sha2", + "sha3", + "thiserror", + "uuid", +] + [[package]] name = "eyre" version = "0.6.12" @@ -2196,6 +2229,15 @@ dependencies = [ "sha2", ] +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + [[package]] name = "keccak-asm" version = "0.1.1" @@ -2249,6 +2291,7 @@ dependencies = [ "regex", "reqwest 0.11.27", "rmp-serde", + "rpassword", "semver 1.0.23", "serde", "serde_json", @@ -3131,6 +3174,27 @@ dependencies = [ "serde", ] +[[package]] +name = "rpassword" +version = "7.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80472be3c897911d0137b2d2b9055faf6eeac5b14e324073d83bc17b191d7e3f" +dependencies = [ + "libc", + "rtoolbox", + "windows-sys 0.48.0", +] + +[[package]] +name = "rtoolbox" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c247d24e63230cdb56463ae328478bd5eac8b8faa8c69461a77e8e323afac90e" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "ruint" version = "1.12.3" @@ -3278,6 +3342,15 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "salsa20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" +dependencies = [ + "cipher", +] + [[package]] name = "same-file" version = "1.0.6" @@ -3302,6 +3375,18 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "scrypt" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f9e24d2b632954ded8ab2ef9fea0a0c769ea56ea98bddbafbad22caeeadf45d" +dependencies = [ + "hmac", + "pbkdf2", + "salsa20", + "sha2", +] + [[package]] name = "sec1" version = "0.7.3" @@ -3443,6 +3528,16 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + [[package]] name = "sha3-asm" version = "0.1.1" @@ -4159,6 +4254,16 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "uuid" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +dependencies = [ + "getrandom", + "serde", +] + [[package]] name = "valuable" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 9664eecd..06551615 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ alloy = { version = "0.1.3", features = [ "rpc-types", "rpc-types-eth", "signers", + "signer-keystore", "signer-local", ] } alloy-sol-macro = "0.7.6" @@ -38,6 +39,7 @@ nix = { version = "0.27", features = ["process", "signal", "term"] } regex = "1" reqwest = { version = "0.11", features = ["json"] } rmp-serde = "1.1.2" +rpassword = "7" semver = "1.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/src/main.rs b/src/main.rs index 76d48a6b..f9ace206 100644 --- a/src/main.rs +++ b/src/main.rs @@ -314,12 +314,19 @@ async fn execute( ) } Some(("publish", publish_matches)) => { - let private_key = publish_matches.get_one::("PRIVATE_KEY").cloned(); - let app_name = publish_matches.get_one::("APP_NAME").cloned(); - let fakechain_port = *publish_matches.get_one::("FAKECHAIN_PORT").unwrap(); - let manifest_path = publish_matches.get_one::("MANIFEST").unwrap(); + let package_dir = + PathBuf::from(publish_matches.get_one::("DIR").unwrap()); + let metadata_uri = publish_matches.get_one::("URI").unwrap(); + let keystore_path = + PathBuf::from(publish_matches.get_one::("PATH").unwrap()); + let fakechain_port = publish_matches.get_one::("FAKECHAIN_PORT").unwrap(); - publish::execute(private_key, app_name, fakechain_port, manifest_path).await + publish::execute( + &package_dir, + metadata_uri, + &keystore_path, + fakechain_port, + ).await } Some(("remove-package", remove_package_matches)) => { let package_name = remove_package_matches @@ -887,26 +894,35 @@ async fn make_app(current_dir: &std::ffi::OsString) -> Result { ) ) .subcommand(Command::new("publish") - .about("Publish or update an app") + .about("Publish or update a package") .visible_alias("p") - .arg(Arg::new("PRIVATE_KEY") + .arg(Arg::new("DIR") + .action(ArgAction::Set) + .help("The package directory to publish") + .default_value(current_dir) + ) + .arg(Arg::new("URI") + .action(ArgAction::Set) + .short('u') + .long("metadata-uri") + .help("URI where metadata lives") + .required(true) + ) + .arg(Arg::new("PATH") + .action(ArgAction::Set) .short('k') - .long("private-key") - .help("Private key for transaction signing") - .required(false) + .long("keystore-path") + .help("Path to private key keystore") // TODO: add link to docs? + .required(true) // TODO: -> false when add hardware wallets ) - .arg(Arg::new("APP_NAME") - .short('n') - .long("app-name") - .help("Name of the app to publish") - .required(false) + .arg(Arg::new("FAKECHAIN_PORT") + .action(ArgAction::Set) + .short('c') + .long("fakechain-port") + .help("The port to connect to the fakechain on") + .default_value("8545") + .value_parser(value_parser!(u16)) ) - .arg(Arg::new("MANIFEST") - .short('m') - .long("manifest") - .help("Path to the manifest file") - .default_value("manifest.json") - ) ) .subcommand(Command::new("remove-package") .about("Remove a running package from a node") diff --git a/src/publish/mod.rs b/src/publish/mod.rs index 0d148e0b..8b1c3202 100644 --- a/src/publish/mod.rs +++ b/src/publish/mod.rs @@ -1,5 +1,4 @@ -use std::fs; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::str::FromStr; use alloy::{ @@ -9,13 +8,15 @@ use alloy::{ pubsub::PubSubFrontend, rpc::client::WsConnect, rpc::types::eth::{TransactionInput, TransactionRequest}, - signers::local::PrivateKeySigner, + signers::local::LocalSigner, }; use alloy_sol_macro::sol; use alloy_sol_types::SolCall; -use color_eyre::eyre::{self, Result}; -use reqwest::Client; -use serde_json::Value; +use color_eyre::eyre::{eyre, Result}; +use fs_err as fs; +use tracing::{info, instrument}; + +use crate::build::{download_file, read_metadata}; sol! { function mint ( @@ -67,85 +68,84 @@ const MULTICALL_ADDRESS: &str = "0xcA11bde05977b3631167028862bE2a173976CA11"; const KINO_ACCOUNT_IMPL: &str = "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9"; const FAKE_DOTDEV_TBA: &str = "0x69C30C0Cf0e9726f9eEF50bb74FA32711fA0B02D"; -fn get_metadata_uri(manifest_path: &str) -> Result { - let manifest_path = Path::new(manifest_path); - if manifest_path.exists() { - let contents = fs::read_to_string(manifest_path)?; - let manifest: Value = serde_json::from_str(&contents)?; - if let Some(uri) = manifest["metadata_uri"].as_str() { - return Ok(uri.to_string()); - } - } - Err(eyre::eyre!("Metadata URI not found in the manifest file.")) -} - -async fn calculate_metadata_hash(metadata_uri: &str) -> Result { - let client = Client::new(); - let response = client.get(metadata_uri).send().await?; - let metadata_text = response.text().await?; - - let _: Value = serde_json::from_str(&metadata_text)?; - +#[instrument(level = "trace", skip_all)] +fn calculate_metadata_hash(package_dir: &Path) -> Result { + let metadata_text = fs::read_to_string(package_dir.join("metadata.json"))?; let hash = keccak256(metadata_text.as_bytes()); - Ok(format!("0x{}", hex::encode(hash))) } -fn get_private_key(cli_key: Option) -> Result { - if let Some(key) = cli_key { - return Ok(key); - } - - if let Ok(key) = std::env::var("PRIVATE_KEY") { - return Ok(key); - } +#[instrument(level = "trace", skip_all)] +fn read_keystore(keystore_path: &Path) -> Result<(Address, EthereumWallet)> { + let password = rpassword::prompt_password("Enter password: ")?; + let signer = LocalSigner::decrypt_keystore(keystore_path, password)?; + let address = signer.address(); + let wallet = EthereumWallet::from(signer); + Ok((address, wallet)) +} - let env_path = Path::new(".env"); - if env_path.exists() { - let contents = fs::read_to_string(env_path)?; - for line in contents.lines() { - if line.starts_with("PRIVATE_KEY=") { - return Ok(line.trim_start_matches("PRIVATE_KEY=").to_string()); - } - } +#[instrument(level = "trace", skip_all)] +pub async fn execute( + package_dir: &Path, + metadata_uri: &str, + keystore_path: &Path, + fakechain_port: &u16, +) -> Result<()> { + if !package_dir.join("pkg").exists() { + return Err(eyre!( + "Required `pkg/` dir not found within given input dir {:?} (or cwd, if none given). Please re-run targeting a package.", + package_dir, + )); } - Err(eyre::eyre!("Private key not found. Please provide it as an argument, set the PRIVATE_KEY environment variable, or include it in a .env file.")) -} + let metadata = read_metadata(package_dir)?; + let name = metadata.name.clone().unwrap_or_default(); -fn get_app_name(cli_name: Option, manifest_path: &str) -> Result { - if let Some(name) = cli_name { - return Ok(name); + let remote_metadata_path = PathBuf::from("/tmp/kinode-kit-cache/{name}"); + if !remote_metadata_path.exists() { + fs::create_dir_all(&remote_metadata_path)?; } - - let manifest_path = Path::new(manifest_path); - if manifest_path.exists() { - let contents = fs::read_to_string(manifest_path)?; - let manifest: Value = serde_json::from_str(&contents)?; - if let Some(name) = manifest["name"].as_str() { - return Ok(name.to_string()); - } + let remote_metadata_path = remote_metadata_path.join("metadata.json"); + download_file( + metadata_uri, + &remote_metadata_path, + ).await?; + let remote_metadata = read_metadata(&remote_metadata_path)?; + + //TODO: remove + println!( + "\033]8;;file://{}\033\\Local\033]8;;\033\\ and \033]8;;{}\033\\remote\033]8;;\033\\ metadata do not match loljk", + package_dir.join("metadata.json").to_str().unwrap_or_default(), + metadata_uri, + ); + // TODO: either add derive(PartialEq) or serialize b4 cmp + if serde_json::to_string(&metadata)? != serde_json::to_string(&remote_metadata)? { + return Err(eyre!( + "\033]8;;file://{}\033\\Local\033]8;;\033\\ and \033]8;;{}\033\\remote\033]8;;\033\\ metadata do not match", + package_dir.join("metadata.json").to_str().unwrap_or_default(), + metadata_uri, + )); } - Err(eyre::eyre!( - "App name not found. Please provide it as an argument or include it in the manifest file." - )) -} - -pub async fn execute( - private_key: Option, - app_name: Option, - fakechain_port: u16, - manifest_path: &str, -) -> Result<()> { - let private_key = get_private_key(private_key)?; - let app_name = get_app_name(app_name, manifest_path)?; - - let privkey_signer = - PrivateKeySigner::from_str(&private_key).expect("Failed to create signer from private key"); + let metadata_hash = calculate_metadata_hash(package_dir)?; + let current_version = &metadata.properties.current_version; + let expected_metadata_hash = metadata + .properties + .code_hashes + .get(current_version) + .cloned() + .unwrap_or_default(); + if metadata_hash != expected_metadata_hash { + return Err(eyre!( + "Published metadata at {} hashes to {}, not {} as expected for current_version {}", + metadata_uri, + metadata_hash, + expected_metadata_hash, + current_version, + )); + } - let wallet_address = privkey_signer.address(); - let wallet: EthereumWallet = privkey_signer.into(); + let (wallet_address, wallet) = read_keystore(keystore_path)?; let endpoint = format!("ws://localhost:{}", fakechain_port); let ws = WsConnect::new(endpoint); @@ -157,12 +157,9 @@ pub async fn execute( let kino_account_impl = Address::from_str(KINO_ACCOUNT_IMPL)?; // Create metadata calls - let metadata_uri = get_metadata_uri(manifest_path)?; - let metadata_hash = calculate_metadata_hash(&metadata_uri).await?; - let metadata_uri_call = noteCall { note: "~metadata-uri".into(), - data: metadata_uri.into(), + data: metadata_uri.to_string().into(), } .abi_encode(); @@ -195,7 +192,7 @@ pub async fn execute( let create_app_tba_call = mintCall { who: wallet_address, - label: app_name.clone().into(), + label: name.clone().into(), initialization: init_call.into(), erc721Data: Bytes::default(), implementation: kino_account_impl, @@ -225,7 +222,6 @@ pub async fn execute( let tx_encoded = tx_envelope.encoded_2718(); let tx_hash = provider.send_raw_transaction(&tx_encoded).await?; - println!("App '{}' published successfully!", app_name); - println!("Transaction hash: {:?}", tx_hash); + info!("{name} published successfully; tx hash {tx_hash:?}", ); Ok(()) } From 9dfc0fc09c65561d48c500459735fd52dbb9e881 Mon Sep 17 00:00:00 2001 From: bitful-pannul Date: Fri, 2 Aug 2024 21:17:20 +0300 Subject: [PATCH 06/17] publish: handle update case --- src/publish/mod.rs | 92 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 73 insertions(+), 19 deletions(-) diff --git a/src/publish/mod.rs b/src/publish/mod.rs index 97cade93..2b5a48b6 100644 --- a/src/publish/mod.rs +++ b/src/publish/mod.rs @@ -7,11 +7,13 @@ use std::str::FromStr; use alloy::{ network::{eip2718::Encodable2718, EthereumWallet, TransactionBuilder}, - primitives::{keccak256, Address, Bytes, U256}, + primitives::{keccak256, Address, Bytes, B256, U256}, providers::{Provider, ProviderBuilder, RootProvider}, pubsub::PubSubFrontend, - rpc::client::WsConnect, - rpc::types::eth::{TransactionInput, TransactionRequest}, + rpc::{ + client::WsConnect, + types::eth::{TransactionInput, TransactionRequest}, + }, signers::local::PrivateKeySigner, }; use alloy_sol_macro::sol; @@ -75,6 +77,7 @@ pub async fn execute( ) -> Result<()> { let private_key = get_private_key(private_key)?; let app_name = get_app_name(app_name, manifest_path)?; + let publisher_name = get_app_publisher(manifest_path)?; let privkey_signer = PrivateKeySigner::from_str(&private_key).expect("Failed to create signer from private key"); @@ -120,6 +123,27 @@ pub async fn execute( let notes_multicall = aggregateCall { calls }.abi_encode(); + // Check if the app already exists + let app_node = format!("{}.{}", app_name, wallet_address); + let app_node = namehash(&app_node); + + let get_tx = TransactionRequest::default().to(kimap).input( + getCall { + node: app_node.into(), + } + .abi_encode() + .into(), + ); + + let get_call = provider.call(&get_tx).await?; + let decoded = getCall::abi_decode_returns(&get_call, false)?; + + let tba = decoded.tba; + let owner = decoded.owner; + let _data = decoded.data; + + let is_update = tba != Address::default() && owner == wallet_address; + let init_call = executeCall { to: multicall_address, value: U256::from(0), @@ -128,22 +152,24 @@ pub async fn execute( } .abi_encode(); - let create_app_tba_call = mintCall { - who: wallet_address, - label: app_name.clone().into(), - initialization: init_call.into(), - erc721Data: Bytes::default(), - implementation: kino_account_impl, - } - .abi_encode(); - - let create_app_call = executeCall { - to: kimap, - value: U256::from(0), - data: create_app_tba_call.into(), - operation: 0, - } - .abi_encode(); + let create_app_call = if is_update { + executeCall { + to: kimap, + value: U256::from(0), + data: init_call.into(), + operation: 1, + } + .abi_encode() + } else { + mintCall { + who: wallet_address, + label: app_name.clone().into(), + initialization: init_call.into(), + erc721Data: Bytes::default(), + implementation: kino_account_impl, + } + .abi_encode() + }; let nonce = provider.get_transaction_count(wallet_address).await?; @@ -229,3 +255,31 @@ fn get_app_name(cli_name: Option, manifest_path: &str) -> Result "App name not found. Please provide it as an argument or include it in the manifest file." )) } + +fn get_app_publisher(manifest_path: &str) -> Result { + let manifest_path = Path::new(manifest_path); + if manifest_path.exists() { + let contents = fs::read_to_string(manifest_path)?; + let manifest: Value = serde_json::from_str(&contents)?; + if let Some(publisher) = manifest["publisher"].as_str() { + return Ok(publisher.to_string()); + } + } + Err(eyre::eyre!("Publisher not found in the manifest file.")) +} + +/// kinohash +pub fn namehash(name: &str) -> [u8; 32] { + let mut node = B256::default(); + + if name.is_empty() { + return node.into(); + } + let mut labels: Vec<&str> = name.split(".").collect(); + labels.reverse(); + for label in labels.iter() { + let label_hash = keccak256(label.as_bytes()); + node = keccak256([node, label_hash].concat()); + } + node.into() +} From c1d55d1df15d1dd6ca183270e8ffd18598991c05 Mon Sep 17 00:00:00 2001 From: hosted-fornet Date: Fri, 2 Aug 2024 11:23:49 -0700 Subject: [PATCH 07/17] publish: fix path & fix error print links --- src/boot_fake_node/mod.rs | 5 +---- src/publish/mod.rs | 30 ++++++++++++++++-------------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/boot_fake_node/mod.rs b/src/boot_fake_node/mod.rs index a293e6e0..605e80ac 100644 --- a/src/boot_fake_node/mod.rs +++ b/src/boot_fake_node/mod.rs @@ -390,10 +390,7 @@ pub async fn execute( .join(if release { "release" } else { "debug" }) .join("kinode") } else { - return Err(eyre!( - "--runtime-path {:?} must be a directory (the repo).", - runtime_path, - )); + runtime_path } }, }; diff --git a/src/publish/mod.rs b/src/publish/mod.rs index 8b1c3202..b98f5e1d 100644 --- a/src/publish/mod.rs +++ b/src/publish/mod.rs @@ -101,28 +101,30 @@ pub async fn execute( let metadata = read_metadata(package_dir)?; let name = metadata.name.clone().unwrap_or_default(); - let remote_metadata_path = PathBuf::from("/tmp/kinode-kit-cache/{name}"); - if !remote_metadata_path.exists() { - fs::create_dir_all(&remote_metadata_path)?; + let remote_metadata_dir = PathBuf::from(format!( + "/tmp/kinode-kit-cache/{name}" + )); + if !remote_metadata_dir.exists() { + fs::create_dir_all(&remote_metadata_dir)?; } - let remote_metadata_path = remote_metadata_path.join("metadata.json"); + let remote_metadata_path = remote_metadata_dir.join("metadata.json"); download_file( metadata_uri, &remote_metadata_path, ).await?; - let remote_metadata = read_metadata(&remote_metadata_path)?; + let remote_metadata = read_metadata(&remote_metadata_dir)?; - //TODO: remove - println!( - "\033]8;;file://{}\033\\Local\033]8;;\033\\ and \033]8;;{}\033\\remote\033]8;;\033\\ metadata do not match loljk", - package_dir.join("metadata.json").to_str().unwrap_or_default(), - metadata_uri, - ); - // TODO: either add derive(PartialEq) or serialize b4 cmp + // TODO: add derive(PartialEq) to Erc721 if serde_json::to_string(&metadata)? != serde_json::to_string(&remote_metadata)? { + let local_path = package_dir + .join("metadata.json") + .canonicalize() + .ok() + .and_then(|p| p.to_str().map(|s| s.to_string())) + .unwrap_or_default(); return Err(eyre!( - "\033]8;;file://{}\033\\Local\033]8;;\033\\ and \033]8;;{}\033\\remote\033]8;;\033\\ metadata do not match", - package_dir.join("metadata.json").to_str().unwrap_or_default(), + "\x1B]8;;file://{}\x1B\\Local\x1B]8;;\x1B\\ and \x1B]8;;{}\x1B\\remote\x1B]8;;\x1B\\ metadata do not match", + local_path, metadata_uri, )); } From 0db6ba84909a796e0e111626803a9ea9d35fc475 Mon Sep 17 00:00:00 2001 From: hosted-fornet Date: Fri, 2 Aug 2024 14:34:32 -0700 Subject: [PATCH 08/17] publish: get it working --- src/publish/mod.rs | 18 ++++++++++------- src/start_package/mod.rs | 43 ++++++++++++++++++++++++++-------------- 2 files changed, 39 insertions(+), 22 deletions(-) diff --git a/src/publish/mod.rs b/src/publish/mod.rs index b98f5e1d..c9720124 100644 --- a/src/publish/mod.rs +++ b/src/publish/mod.rs @@ -17,6 +17,7 @@ use fs_err as fs; use tracing::{info, instrument}; use crate::build::{download_file, read_metadata}; +use crate::start_package::{make_pkg_publisher, zip_pkg}; sol! { function mint ( @@ -128,22 +129,25 @@ pub async fn execute( metadata_uri, )); } - let metadata_hash = calculate_metadata_hash(package_dir)?; + + let pkg_publisher = make_pkg_publisher(&metadata); + let (_, pkg_hash) = zip_pkg(package_dir, &pkg_publisher)?; let current_version = &metadata.properties.current_version; - let expected_metadata_hash = metadata + let expected_pkg_hash = metadata .properties .code_hashes .get(current_version) .cloned() .unwrap_or_default(); - if metadata_hash != expected_metadata_hash { + if pkg_hash != expected_pkg_hash { return Err(eyre!( - "Published metadata at {} hashes to {}, not {} as expected for current_version {}", - metadata_uri, - metadata_hash, - expected_metadata_hash, + "Zipped pkg hashes to '{}' not '{}' as expected for current_version {} based on published metadata at \x1B]8;;{}\x1B\\{}\x1B]8;;\x1B\\", + pkg_hash, + expected_pkg_hash, current_version, + metadata_uri, + metadata_uri, )); } diff --git a/src/start_package/mod.rs b/src/start_package/mod.rs index 75271899..707c38d5 100644 --- a/src/start_package/mod.rs +++ b/src/start_package/mod.rs @@ -1,6 +1,6 @@ use sha2::{Sha256, Digest}; use std::io::{Read, Write}; -use std::path::Path; +use std::path::{Path, PathBuf}; use color_eyre::{Result, Section, eyre::{eyre, WrapErr}}; use fs_err as fs; @@ -80,6 +80,30 @@ pub fn interact_with_package( ) } +pub fn make_pkg_publisher(metadata: &Erc721Metadata) -> String { + let package_name = metadata.properties.package_name.as_str(); + let publisher = metadata.properties.publisher.as_str(); + let pkg_publisher = format!("{}:{}", package_name, publisher); + pkg_publisher +} + +#[instrument(level = "trace", skip_all)] +pub fn zip_pkg(package_dir: &Path, pkg_publisher: &str) -> Result<(PathBuf, String)> { + let pkg_dir = package_dir.join("pkg"); + let target_dir = package_dir.join("target"); + fs::create_dir_all(&target_dir)?; + let zip_filename = target_dir.join(pkg_publisher).with_extension("zip"); + zip_directory(&pkg_dir, &zip_filename.to_str().unwrap())?; + + let mut file = fs::File::open(&zip_filename)?; + let mut hasher = Sha256::new(); + let mut buffer = Vec::new(); + file.read_to_end(&mut buffer)?; + hasher.update(&buffer); + let hash_result = hasher.finalize(); + Ok((zip_filename, format!("{hash_result:x}"))) +} + #[instrument(level = "trace", skip_all)] pub fn zip_directory(directory: &Path, zip_filename: &str) -> Result<()> { let file = fs::File::create(zip_filename)?; @@ -126,7 +150,7 @@ pub async fn execute(package_dir: &Path, url: &str) -> Result<()> { let metadata = read_metadata(package_dir)?; let package_name = metadata.properties.package_name.as_str(); let publisher = metadata.properties.publisher.as_str(); - let pkg_publisher = format!("{}:{}", package_name, publisher); + let pkg_publisher = make_pkg_publisher(&metadata); let manifest: Vec = serde_json::from_reader(fs::File::open(pkg_dir.join("manifest.json")) @@ -147,20 +171,9 @@ pub async fn execute(package_dir: &Path, url: &str) -> Result<()> { info!("{}", pkg_publisher); - // Create zip and put it in /target - let parent_dir = pkg_dir.parent().unwrap(); - let target_dir = parent_dir.join("target"); - fs::create_dir_all(&target_dir)?; - let zip_filename = target_dir.join(&pkg_publisher).with_extension("zip"); - zip_directory(&pkg_dir, &zip_filename.to_str().unwrap())?; + let (zip_filename, hash_result) = zip_pkg(package_dir, &pkg_publisher)?; + info!("package zip hash: {hash_result}"); - let mut file = fs::File::open(&zip_filename)?; - let mut hasher = Sha256::new(); - let mut buffer = Vec::new(); - file.read_to_end(&mut buffer)?; - hasher.update(&buffer); - let hash_result = hasher.finalize(); - info!("package zip hash: {:x}", hash_result); // Create and send new package request let new_pkg_request = new_package( None, From 18c4b3306257b5b8b7107960df086838697d1040 Mon Sep 17 00:00:00 2001 From: bitful-pannul Date: Sat, 3 Aug 2024 15:18:55 +0300 Subject: [PATCH 09/17] update contract addresses --- src/publish/mod.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/publish/mod.rs b/src/publish/mod.rs index 58807827..acde4480 100644 --- a/src/publish/mod.rs +++ b/src/publish/mod.rs @@ -66,9 +66,9 @@ sol! { ) external payable returns (uint256 blockNumber, bytes[] memory returnData); } -const KIMAP_ADDRESS: &str = "0x0165878A594ca255338adfa4d48449f69242Eb8F"; +const KIMAP_ADDRESS: &str = "0xEce71a05B36CA55B895427cD9a440eEF7Cf3669D"; const MULTICALL_ADDRESS: &str = "0xcA11bde05977b3631167028862bE2a173976CA11"; -const KINO_ACCOUNT_IMPL: &str = "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9"; +const KINO_ACCOUNT_IMPL: &str = "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0"; const FAKE_DOTDEV_TBA: &str = "0x69C30C0Cf0e9726f9eEF50bb74FA32711fA0B02D"; #[instrument(level = "trace", skip_all)] @@ -119,17 +119,12 @@ pub async fn execute( let metadata = read_metadata(package_dir)?; let name = metadata.name.clone().unwrap_or_default(); - let remote_metadata_dir = PathBuf::from(format!( - "/tmp/kinode-kit-cache/{name}" - )); + let remote_metadata_dir = PathBuf::from(format!("/tmp/kinode-kit-cache/{name}")); if !remote_metadata_dir.exists() { fs::create_dir_all(&remote_metadata_dir)?; } let remote_metadata_path = remote_metadata_dir.join("metadata.json"); - download_file( - metadata_uri, - &remote_metadata_path, - ).await?; + download_file(metadata_uri, &remote_metadata_path).await?; let remote_metadata = read_metadata(&remote_metadata_dir)?; // TODO: add derive(PartialEq) to Erc721 @@ -284,6 +279,9 @@ pub async fn execute( let tx_encoded = tx_envelope.encoded_2718(); let tx_hash = provider.send_raw_transaction(&tx_encoded).await?; - info!("{name} published successfully; tx hash {:?}", tx_hash.tx_hash()); + info!( + "{name} published successfully; tx hash {:?}", + tx_hash.tx_hash() + ); Ok(()) } From 940c73c6a6d327aa8f747198d43c9082e3f066d7 Mon Sep 17 00:00:00 2001 From: hosted-fornet Date: Mon, 12 Aug 2024 21:03:51 -0700 Subject: [PATCH 10/17] publish: add real node options --- src/main.rs | 57 +++++++++++++++++++++++---- src/publish/mod.rs | 97 ++++++++++++++++++++++++++++++++-------------- 2 files changed, 117 insertions(+), 37 deletions(-) diff --git a/src/main.rs b/src/main.rs index 221a83f5..8b319fc2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -334,13 +334,21 @@ async fn execute( let package_dir = PathBuf::from(matches.get_one::("DIR").unwrap()); let metadata_uri = matches.get_one::("URI").unwrap(); let keystore_path = PathBuf::from(matches.get_one::("PATH").unwrap()); - let fakechain_port = matches.get_one::("FAKECHAIN_PORT").unwrap(); + let rpc_uri = matches.get_one::("RPC_URI").unwrap(); + let real = matches.get_one::("REAL").unwrap(); + let gas_limit = matches.get_one::("GAS_LIMIT").unwrap(); + let max_priority_fee = matches.get_one::("MAX_PRIORITY_FEE").unwrap(); + let max_fee_per_gas = matches.get_one::("MAX_FEE_PER_GAS").unwrap(); publish::execute( &package_dir, metadata_uri, &keystore_path, - fakechain_port, + rpc_uri, + real, + *gas_limit, + *max_priority_fee, + *max_fee_per_gas, ).await } Some(("remove-package", matches)) => { @@ -946,13 +954,46 @@ async fn make_app(current_dir: &std::ffi::OsString) -> Result { .help("Path to private key keystore") // TODO: add link to docs? .required(true) // TODO: -> false when add hardware wallets ) - .arg(Arg::new("FAKECHAIN_PORT") + .arg(Arg::new("RPC_URI") .action(ArgAction::Set) - .short('c') - .long("fakechain-port") - .help("The port to connect to the fakechain on") - .default_value("8545") - .value_parser(value_parser!(u16)) + .short('r') + .long("rpc") + .help("The ETH RPC WebSockets URI") + .required(true) + ) + .arg(Arg::new("REAL") + .action(ArgAction::SetTrue) + .short('e') + .long("real") + .help("If set, deploy to real network (default: fake node)") + .required(false) + ) + .arg(Arg::new("GAS_LIMIT") + .action(ArgAction::Set) + .short('g') + .long("gas-limit") + .help("The ETH transaction gas limit") + .default_value("1_000_000") + .value_parser(value_parser!(u64)) + .required(false) + ) + .arg(Arg::new("MAX_PRIORITY_FEE") + .action(ArgAction::Set) + .short('p') + .long("priority-fee") + .help("The ETH transaction max priority fee") + .default_value("200_000_000_000") + .value_parser(value_parser!(u64)) + .required(false) + ) + .arg(Arg::new("MAX_FEE_PER_GAS") + .action(ArgAction::Set) + .short('f') + .long("fee-per-gas") + .help("The ETH transaction max fee per gas") + .default_value("300_000_000_000") + .value_parser(value_parser!(u64)) + .required(false) ) ) .subcommand(Command::new("remove-package") diff --git a/src/publish/mod.rs b/src/publish/mod.rs index acde4480..7d06c57e 100644 --- a/src/publish/mod.rs +++ b/src/publish/mod.rs @@ -66,10 +66,14 @@ sol! { ) external payable returns (uint256 blockNumber, bytes[] memory returnData); } -const KIMAP_ADDRESS: &str = "0xEce71a05B36CA55B895427cD9a440eEF7Cf3669D"; +const FAKE_KIMAP_ADDRESS: &str = "0xEce71a05B36CA55B895427cD9a440eEF7Cf3669D"; +const FAKE_CHAIN_ID: u64 = 31337; + +const REAL_KIMAP_ADDRESS: &str = "0xcA92476B2483aBD5D82AEBF0b56701Bb2e9be658"; const MULTICALL_ADDRESS: &str = "0xcA11bde05977b3631167028862bE2a173976CA11"; -const KINO_ACCOUNT_IMPL: &str = "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0"; +const KINO_ACCOUNT_IMPL: &str = "0x58790D9957ECE58607A4b58308BBD5FE1a2e4789"; const FAKE_DOTDEV_TBA: &str = "0x69C30C0Cf0e9726f9eEF50bb74FA32711fA0B02D"; +const REAL_CHAIN_ID: u64 = 10; #[instrument(level = "trace", skip_all)] fn calculate_metadata_hash(package_dir: &Path) -> Result { @@ -102,12 +106,43 @@ fn namehash(name: &str) -> [u8; 32] { node.into() } +async fn kimap_get( + node: &str, + kimap: Address, + provider: &RootProvider, +) -> Result<(Address, Address, Option)> { + let node = namehash(&node); + let get_tx = TransactionRequest::default().to(kimap).input( + getCall { + node: node.into(), + } + .abi_encode() + .into(), + ); + + let get_call = provider.call(&get_tx).await?; + let decoded = getCall::abi_decode_returns(&get_call, false)?; + + let tba = decoded.tba; + let owner = decoded.owner; + let data = if decoded.data == Bytes::default() { + None + } else { + Some(decoded.data) + }; + Ok((tba, owner, data)) +} + #[instrument(level = "trace", skip_all)] pub async fn execute( package_dir: &Path, metadata_uri: &str, keystore_path: &Path, - fakechain_port: &u16, + rpc_uri: &str, + real: &bool, + gas_limit: u128, + max_priority_fee_per_gas: u128, + max_fee_per_gas: u128, ) -> Result<()> { if !package_dir.join("pkg").exists() { return Err(eyre!( @@ -117,7 +152,8 @@ pub async fn execute( } let metadata = read_metadata(package_dir)?; - let name = metadata.name.clone().unwrap_or_default(); + let name = metadata.name.clone().unwrap(); + let publisher = metadata.properties.publisher.clone(); let remote_metadata_dir = PathBuf::from(format!("/tmp/kinode-kit-cache/{name}")); if !remote_metadata_dir.exists() { @@ -165,14 +201,28 @@ pub async fn execute( let (wallet_address, wallet) = read_keystore(keystore_path)?; - let endpoint = format!("ws://localhost:{}", fakechain_port); - let ws = WsConnect::new(endpoint); + let ws = WsConnect::new(rpc_uri); let provider: RootProvider = ProviderBuilder::default().on_ws(ws).await?; - let kimap = Address::from_str(KIMAP_ADDRESS)?; + let kimap = Address::from_str( + if *real { + REAL_KIMAP_ADDRESS + } else { + FAKE_KIMAP_ADDRESS + } + )?; let multicall_address = Address::from_str(MULTICALL_ADDRESS)?; - let fakedotdev_tba = Address::from_str(FAKE_DOTDEV_TBA)?; let kino_account_impl = Address::from_str(KINO_ACCOUNT_IMPL)?; + let tld_tba = if *real { + let (tba, _, _) = kimap_get( + publisher.split('.').last().unwrap(), + kimap, + &provider, + ).await?; + tba + } else { + Address::from_str(FAKE_DOTDEV_TBA)? + }; // Create metadata calls let metadata_uri_call = noteCall { @@ -201,23 +251,12 @@ pub async fn execute( let notes_multicall = aggregateCall { calls }.abi_encode(); // Check if the app already exists - let app_node = format!("{}.{}", name, wallet_address); - let app_node = namehash(&app_node); - - let get_tx = TransactionRequest::default().to(kimap).input( - getCall { - node: app_node.into(), - } - .abi_encode() - .into(), - ); - - let get_call = provider.call(&get_tx).await?; - let decoded = getCall::abi_decode_returns(&get_call, false)?; - let tba = decoded.tba; - let owner = decoded.owner; - let _data = decoded.data; + let (tba, owner, _) = kimap_get( + &format!("{}.{}", name, publisher), + kimap, + &provider, + ).await?; let is_update = tba != Address::default() && owner == wallet_address; @@ -267,13 +306,13 @@ pub async fn execute( let nonce = provider.get_transaction_count(wallet_address).await?; let tx = TransactionRequest::default() - .to(fakedotdev_tba) + .to(tld_tba) .input(TransactionInput::new(create_app_call.into())) .nonce(nonce) - .with_chain_id(31337) - .with_gas_limit(1_000_000) - .with_max_priority_fee_per_gas(200_000_000_000) - .with_max_fee_per_gas(300_000_000_000); + .with_chain_id(if *real { REAL_CHAIN_ID } else { FAKE_CHAIN_ID }) + .with_gas_limit(gas_limit) + .with_max_priority_fee_per_gas(max_priority_fee_per_gas) + .with_max_fee_per_gas(max_fee_per_gas); let tx_envelope = tx.build(&wallet).await?; let tx_encoded = tx_envelope.encoded_2718(); From fefce450f2fe684dff0fbc7511a81ae505028ed6 Mon Sep 17 00:00:00 2001 From: hosted-fornet Date: Tue, 13 Aug 2024 16:28:59 -0700 Subject: [PATCH 11/17] publish: get it working --- src/main.rs | 29 +++-- src/publish/mod.rs | 281 ++++++++++++++++++++++++++------------------- 2 files changed, 179 insertions(+), 131 deletions(-) diff --git a/src/main.rs b/src/main.rs index 8b319fc2..28768218 100644 --- a/src/main.rs +++ b/src/main.rs @@ -36,6 +36,11 @@ struct Commit { sha: String, } +fn parse_u128_with_underscores(s: &str) -> Result { + let clean_string = s.replace('_', ""); + clean_string.parse::().map_err(|_| "Invalid number format") +} + async fn get_latest_commit_sha_from_branch( owner: &str, repo: &str, @@ -337,8 +342,12 @@ async fn execute( let rpc_uri = matches.get_one::("RPC_URI").unwrap(); let real = matches.get_one::("REAL").unwrap(); let gas_limit = matches.get_one::("GAS_LIMIT").unwrap(); - let max_priority_fee = matches.get_one::("MAX_PRIORITY_FEE").unwrap(); - let max_fee_per_gas = matches.get_one::("MAX_FEE_PER_GAS").unwrap(); + let max_priority_fee = matches + .get_one::("MAX_PRIORITY_FEE_PER_GAS") + .and_then(|mpf| Some(mpf.clone())); + let max_fee_per_gas = matches + .get_one::("MAX_FEE_PER_GAS") + .and_then(|mfpg| Some(mfpg.clone())); publish::execute( &package_dir, @@ -347,8 +356,8 @@ async fn execute( rpc_uri, real, *gas_limit, - *max_priority_fee, - *max_fee_per_gas, + max_priority_fee, + max_fee_per_gas, ).await } Some(("remove-package", matches)) => { @@ -974,16 +983,15 @@ async fn make_app(current_dir: &std::ffi::OsString) -> Result { .long("gas-limit") .help("The ETH transaction gas limit") .default_value("1_000_000") - .value_parser(value_parser!(u64)) + .value_parser(clap::builder::ValueParser::new(parse_u128_with_underscores)) .required(false) ) - .arg(Arg::new("MAX_PRIORITY_FEE") + .arg(Arg::new("MAX_PRIORITY_FEE_PER_GAS") .action(ArgAction::Set) .short('p') .long("priority-fee") - .help("The ETH transaction max priority fee") - .default_value("200_000_000_000") - .value_parser(value_parser!(u64)) + .help("The ETH transaction max priority fee per gas") + .value_parser(clap::builder::ValueParser::new(parse_u128_with_underscores)) .required(false) ) .arg(Arg::new("MAX_FEE_PER_GAS") @@ -991,8 +999,7 @@ async fn make_app(current_dir: &std::ffi::OsString) -> Result { .short('f') .long("fee-per-gas") .help("The ETH transaction max fee per gas") - .default_value("300_000_000_000") - .value_parser(value_parser!(u64)) + .value_parser(clap::builder::ValueParser::new(parse_u128_with_underscores)) .required(false) ) ) diff --git a/src/publish/mod.rs b/src/publish/mod.rs index 7d06c57e..1b87086d 100644 --- a/src/publish/mod.rs +++ b/src/publish/mod.rs @@ -18,6 +18,8 @@ use color_eyre::eyre::{eyre, Result}; use fs_err as fs; use tracing::{info, instrument}; +use kinode_process_lib::kernel_types::Erc721Metadata; + use crate::build::{download_file, read_metadata}; use crate::start_package::{make_pkg_publisher, zip_pkg}; @@ -71,7 +73,7 @@ const FAKE_CHAIN_ID: u64 = 31337; const REAL_KIMAP_ADDRESS: &str = "0xcA92476B2483aBD5D82AEBF0b56701Bb2e9be658"; const MULTICALL_ADDRESS: &str = "0xcA11bde05977b3631167028862bE2a173976CA11"; -const KINO_ACCOUNT_IMPL: &str = "0x58790D9957ECE58607A4b58308BBD5FE1a2e4789"; +const KINO_ACCOUNT_IMPL: &str = "0x38766C70a4FB2f23137D9251a1aA12b1143fC716"; const FAKE_DOTDEV_TBA: &str = "0x69C30C0Cf0e9726f9eEF50bb74FA32711fA0B02D"; const REAL_CHAIN_ID: u64 = 10; @@ -106,56 +108,12 @@ fn namehash(name: &str) -> [u8; 32] { node.into() } -async fn kimap_get( - node: &str, - kimap: Address, - provider: &RootProvider, -) -> Result<(Address, Address, Option)> { - let node = namehash(&node); - let get_tx = TransactionRequest::default().to(kimap).input( - getCall { - node: node.into(), - } - .abi_encode() - .into(), - ); - - let get_call = provider.call(&get_tx).await?; - let decoded = getCall::abi_decode_returns(&get_call, false)?; - - let tba = decoded.tba; - let owner = decoded.owner; - let data = if decoded.data == Bytes::default() { - None - } else { - Some(decoded.data) - }; - Ok((tba, owner, data)) -} - #[instrument(level = "trace", skip_all)] -pub async fn execute( - package_dir: &Path, - metadata_uri: &str, - keystore_path: &Path, - rpc_uri: &str, - real: &bool, - gas_limit: u128, - max_priority_fee_per_gas: u128, - max_fee_per_gas: u128, -) -> Result<()> { - if !package_dir.join("pkg").exists() { - return Err(eyre!( - "Required `pkg/` dir not found within given input dir {:?} (or cwd, if none given). Please re-run targeting a package.", - package_dir, - )); - } - - let metadata = read_metadata(package_dir)?; - let name = metadata.name.clone().unwrap(); - let publisher = metadata.properties.publisher.clone(); - - let remote_metadata_dir = PathBuf::from(format!("/tmp/kinode-kit-cache/{name}")); +async fn check_remote_metadata(metadata: &Erc721Metadata, metadata_uri: &str, package_dir: &Path) -> Result { + let remote_metadata_dir = PathBuf::from(format!( + "/tmp/kinode-kit-cache/{}", + metadata.name.as_ref().unwrap(), + )); if !remote_metadata_dir.exists() { fs::create_dir_all(&remote_metadata_dir)?; } @@ -178,7 +136,11 @@ pub async fn execute( )); } let metadata_hash = calculate_metadata_hash(package_dir)?; + Ok(metadata_hash) +} +#[instrument(level = "trace", skip_all)] +fn check_pkg_hash(metadata: &Erc721Metadata, package_dir: &Path, metadata_uri: &str) -> Result<()> { let pkg_publisher = make_pkg_publisher(&metadata); let (_, pkg_hash) = zip_pkg(package_dir, &pkg_publisher)?; let current_version = &metadata.properties.current_version; @@ -198,42 +160,25 @@ pub async fn execute( metadata_uri, )); } + Ok(()) +} - let (wallet_address, wallet) = read_keystore(keystore_path)?; - - let ws = WsConnect::new(rpc_uri); - let provider: RootProvider = ProviderBuilder::default().on_ws(ws).await?; - - let kimap = Address::from_str( - if *real { - REAL_KIMAP_ADDRESS - } else { - FAKE_KIMAP_ADDRESS - } - )?; - let multicall_address = Address::from_str(MULTICALL_ADDRESS)?; - let kino_account_impl = Address::from_str(KINO_ACCOUNT_IMPL)?; - let tld_tba = if *real { - let (tba, _, _) = kimap_get( - publisher.split('.').last().unwrap(), - kimap, - &provider, - ).await?; - tba - } else { - Address::from_str(FAKE_DOTDEV_TBA)? - }; - +#[instrument(level = "trace", skip_all)] +fn make_init_call( + metadata_uri: &str, + metadata_hash: &str, + kimap: Address, + multicall_address: Address, +) -> Vec { // Create metadata calls let metadata_uri_call = noteCall { note: "~metadata-uri".into(), data: metadata_uri.to_string().into(), } .abi_encode(); - let metadata_hash_call = noteCall { note: "~metadata-hash".into(), - data: metadata_hash.into(), + data: metadata_hash.to_string().into(), } .abi_encode(); @@ -250,16 +195,6 @@ pub async fn execute( let notes_multicall = aggregateCall { calls }.abi_encode(); - // Check if the app already exists - - let (tba, owner, _) = kimap_get( - &format!("{}.{}", name, publisher), - kimap, - &provider, - ).await?; - - let is_update = tba != Address::default() && owner == wallet_address; - let init_call = executeCall { to: multicall_address, value: U256::from(0), @@ -268,59 +203,165 @@ pub async fn execute( } .abi_encode(); - //let create_app_tba_call = mintCall { - // who: wallet_address, - // label: name.clone().into(), - // initialization: init_call.into(), - // erc721Data: Bytes::default(), - // implementation: kino_account_impl, - //} - //.abi_encode(); - - //let create_app_call = executeCall { - // to: kimap, - // value: U256::from(0), - // data: create_app_tba_call.into(), - // operation: 0, - //} - //.abi_encode(); - let create_app_call = if is_update { - executeCall { - to: kimap, - value: U256::from(0), - data: init_call.into(), - operation: 1, + init_call +} + +#[instrument(level = "trace", skip_all)] +async fn kimap_get( + node: &str, + kimap: Address, + provider: &RootProvider, +) -> Result<(Address, Address, Option)> { + let node = namehash(&node); + let get_tx = TransactionRequest::default().to(kimap).input( + getCall { + node: node.into(), } .abi_encode() + .into(), + ); + + let get_call = provider.call(&get_tx).await?; + let decoded = getCall::abi_decode_returns(&get_call, false)?; + + let tba = decoded.tba; + let owner = decoded.owner; + let data = if decoded.data == Bytes::default() { + None } else { - mintCall { + Some(decoded.data) + }; + Ok((tba, owner, data)) +} + +#[instrument(level = "trace", skip_all)] +async fn prepare_kimap_put( + init_call: Vec, + name: String, + publisher: &str, + kimap: Address, + provider: &RootProvider, + wallet_address: Address, + kino_account_impl: Address, +) -> Result<(Address, Vec)> { + // if app_tba exists, update existing state; + // else mint it & add new state + let (app_tba, owner, _) = kimap_get( + &format!("{}.{}", name, publisher), + kimap, + &provider, + ).await?; + let is_update = app_tba != Address::default() && owner == wallet_address; + + let (to, call) = if is_update { + ( + app_tba, + init_call, + ) + } else { + let (publisher_tba, _, _) = kimap_get( + &publisher, + kimap, + &provider, + ).await?; + let mint_call = mintCall { who: wallet_address, - label: name.clone().into(), + label: name.into(), initialization: init_call.into(), erc721Data: Bytes::default(), implementation: kino_account_impl, } - .abi_encode() + .abi_encode(); + let call = executeCall { + to: kimap, + value: U256::from(0), + data: mint_call.into(), + operation: 0, + } + .abi_encode(); + ( + publisher_tba, + call, + ) }; + Ok((to, call)) +} + +#[instrument(level = "trace", skip_all)] +pub async fn execute( + package_dir: &Path, + metadata_uri: &str, + keystore_path: &Path, + rpc_uri: &str, + real: &bool, + gas_limit: u128, + max_priority_fee_per_gas: Option, + max_fee_per_gas: Option, +) -> Result<()> { + if !package_dir.join("pkg").exists() { + return Err(eyre!( + "Required `pkg/` dir not found within given input dir {:?} (or cwd, if none given). Please re-run targeting a package.", + package_dir, + )); + } + + let metadata = read_metadata(package_dir)?; + + let metadata_hash = check_remote_metadata(&metadata, metadata_uri, package_dir).await?; + check_pkg_hash(&metadata, package_dir, metadata_uri)?; + + let name = metadata.name.clone().unwrap(); + let publisher = metadata.properties.publisher.clone(); + + let (wallet_address, wallet) = read_keystore(keystore_path)?; + + let ws = WsConnect::new(rpc_uri); + let provider: RootProvider = ProviderBuilder::default().on_ws(ws).await?; + + let kimap = Address::from_str( + if *real { + REAL_KIMAP_ADDRESS + } else { + FAKE_KIMAP_ADDRESS + } + )?; + let multicall_address = Address::from_str(MULTICALL_ADDRESS)?; + let kino_account_impl = Address::from_str(KINO_ACCOUNT_IMPL)?; + + let init_call = make_init_call(metadata_uri, &metadata_hash, kimap, multicall_address); + + let (to, call) = prepare_kimap_put( + init_call, + name.clone(), + &publisher, + kimap, + &provider, + wallet_address, + kino_account_impl, + ).await?; let nonce = provider.get_transaction_count(wallet_address).await?; + let gas_price = provider.get_gas_price().await?; let tx = TransactionRequest::default() - .to(tld_tba) - .input(TransactionInput::new(create_app_call.into())) + .to(to) + .input(TransactionInput::new(call.into())) .nonce(nonce) .with_chain_id(if *real { REAL_CHAIN_ID } else { FAKE_CHAIN_ID }) .with_gas_limit(gas_limit) - .with_max_priority_fee_per_gas(max_priority_fee_per_gas) - .with_max_fee_per_gas(max_fee_per_gas); + .with_max_priority_fee_per_gas(max_priority_fee_per_gas.unwrap_or_else(|| gas_price)) + .with_max_fee_per_gas(max_fee_per_gas.unwrap_or_else(|| gas_price)); let tx_envelope = tx.build(&wallet).await?; let tx_encoded = tx_envelope.encoded_2718(); - let tx_hash = provider.send_raw_transaction(&tx_encoded).await?; + let tx = provider.send_raw_transaction(&tx_encoded).await?; - info!( - "{name} published successfully; tx hash {:?}", - tx_hash.tx_hash() + let tx_hash = format!("{:?}", tx.tx_hash()); + let link = format!( + "\x1B]8;;https://optimistic.etherscan.io/tx/{}\x1B\\{}\x1B]8;;\x1B\\", + tx_hash, + tx_hash, ); + info!("{name} tx sent: {link}"); Ok(()) } From 0af3c06b9a7060c35f114edfd1770addd5077c72 Mon Sep 17 00:00:00 2001 From: hosted-fornet Date: Tue, 13 Aug 2024 16:30:41 -0700 Subject: [PATCH 12/17] publish: remove unused const --- src/publish/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/publish/mod.rs b/src/publish/mod.rs index 1b87086d..19b618d3 100644 --- a/src/publish/mod.rs +++ b/src/publish/mod.rs @@ -74,7 +74,6 @@ const FAKE_CHAIN_ID: u64 = 31337; const REAL_KIMAP_ADDRESS: &str = "0xcA92476B2483aBD5D82AEBF0b56701Bb2e9be658"; const MULTICALL_ADDRESS: &str = "0xcA11bde05977b3631167028862bE2a173976CA11"; const KINO_ACCOUNT_IMPL: &str = "0x38766C70a4FB2f23137D9251a1aA12b1143fC716"; -const FAKE_DOTDEV_TBA: &str = "0x69C30C0Cf0e9726f9eEF50bb74FA32711fA0B02D"; const REAL_CHAIN_ID: u64 = 10; #[instrument(level = "trace", skip_all)] From c716d5a92d7e1c37533ed1c2a052e6ffec1f2073 Mon Sep 17 00:00:00 2001 From: hosted-fornet Date: Tue, 13 Aug 2024 16:38:04 -0700 Subject: [PATCH 13/17] publish: change some fn names --- src/publish/mod.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/publish/mod.rs b/src/publish/mod.rs index 19b618d3..c7af4f49 100644 --- a/src/publish/mod.rs +++ b/src/publish/mod.rs @@ -163,7 +163,7 @@ fn check_pkg_hash(metadata: &Erc721Metadata, package_dir: &Path, metadata_uri: & } #[instrument(level = "trace", skip_all)] -fn make_init_call( +fn make_multicall( metadata_uri: &str, metadata_hash: &str, kimap: Address, @@ -235,7 +235,7 @@ async fn kimap_get( #[instrument(level = "trace", skip_all)] async fn prepare_kimap_put( - init_call: Vec, + multicall: Vec, name: String, publisher: &str, kimap: Address, @@ -255,7 +255,7 @@ async fn prepare_kimap_put( let (to, call) = if is_update { ( app_tba, - init_call, + multicall, ) } else { let (publisher_tba, _, _) = kimap_get( @@ -266,7 +266,7 @@ async fn prepare_kimap_put( let mint_call = mintCall { who: wallet_address, label: name.into(), - initialization: init_call.into(), + initialization: multicall.into(), erc721Data: Bytes::default(), implementation: kino_account_impl, } @@ -327,10 +327,10 @@ pub async fn execute( let multicall_address = Address::from_str(MULTICALL_ADDRESS)?; let kino_account_impl = Address::from_str(KINO_ACCOUNT_IMPL)?; - let init_call = make_init_call(metadata_uri, &metadata_hash, kimap, multicall_address); + let multicall = make_multicall(metadata_uri, &metadata_hash, kimap, multicall_address); let (to, call) = prepare_kimap_put( - init_call, + multicall, name.clone(), &publisher, kimap, From 1fa7b6ec24d1b047e997f26ee93051537267b73c Mon Sep 17 00:00:00 2001 From: hosted-fornet Date: Tue, 13 Aug 2024 16:38:21 -0700 Subject: [PATCH 14/17] fix some helptext formatting --- src/main.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main.rs b/src/main.rs index 28768218..f87e49f8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -807,7 +807,7 @@ async fn make_app(current_dir: &std::ffi::OsString) -> Result { .action(ArgAction::SetTrue) .short('d') .long("disconnect") - .help("If set, disconnect an existing tunnel (default: connect a new tunnel)") + .help("If set, disconnect an existing tunnel [default: connect a new tunnel]") .required(false) ) .arg(Arg::new("HOST") @@ -880,7 +880,7 @@ async fn make_app(current_dir: &std::ffi::OsString) -> Result { .action(ArgAction::Set) .short('n') .long("node") - .help("Node ID (default: our)") + .help("Node ID [default: our]") .required(false) ) .arg(Arg::new("PATH") @@ -974,7 +974,7 @@ async fn make_app(current_dir: &std::ffi::OsString) -> Result { .action(ArgAction::SetTrue) .short('e') .long("real") - .help("If set, deploy to real network (default: fake node)") + .help("If set, deploy to real network [default: fake node]") .required(false) ) .arg(Arg::new("GAS_LIMIT") @@ -1092,7 +1092,7 @@ async fn make_app(current_dir: &std::ffi::OsString) -> Result { .visible_alias("v") .arg(Arg::new("PACKAGE_ID") .action(ArgAction::Set) - .help("Get API of this package (default: list all APIs)") + .help("Get API of this package [default: list all APIs]") .required(false) ) .arg(Arg::new("NODE_PORT") From 3e9df4461c449c07b545edf89579556a69739fb6 Mon Sep 17 00:00:00 2001 From: hosted-fornet Date: Tue, 13 Aug 2024 17:01:32 -0700 Subject: [PATCH 15/17] publish: add a `--unpublish` flag --- src/main.rs | 11 +++++++++-- src/publish/mod.rs | 39 ++++++++++++++++++++++++++++----------- 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/src/main.rs b/src/main.rs index f87e49f8..c1f15deb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -341,6 +341,7 @@ async fn execute( let keystore_path = PathBuf::from(matches.get_one::("PATH").unwrap()); let rpc_uri = matches.get_one::("RPC_URI").unwrap(); let real = matches.get_one::("REAL").unwrap(); + let unpublish = matches.get_one::("UNPUBLISH").unwrap(); let gas_limit = matches.get_one::("GAS_LIMIT").unwrap(); let max_priority_fee = matches .get_one::("MAX_PRIORITY_FEE_PER_GAS") @@ -355,6 +356,7 @@ async fn execute( &keystore_path, rpc_uri, real, + unpublish, *gas_limit, max_priority_fee, max_fee_per_gas, @@ -977,6 +979,11 @@ async fn make_app(current_dir: &std::ffi::OsString) -> Result { .help("If set, deploy to real network [default: fake node]") .required(false) ) + .arg(Arg::new("UNPUBLISH") + .action(ArgAction::SetTrue) + .long("unpublish") + .help("If set, unpublish existing published package [default: publish a package]") + ) .arg(Arg::new("GAS_LIMIT") .action(ArgAction::Set) .short('g') @@ -990,7 +997,7 @@ async fn make_app(current_dir: &std::ffi::OsString) -> Result { .action(ArgAction::Set) .short('p') .long("priority-fee") - .help("The ETH transaction max priority fee per gas") + .help("The ETH transaction max priority fee per gas [default: estimated from network conditions]") .value_parser(clap::builder::ValueParser::new(parse_u128_with_underscores)) .required(false) ) @@ -998,7 +1005,7 @@ async fn make_app(current_dir: &std::ffi::OsString) -> Result { .action(ArgAction::Set) .short('f') .long("fee-per-gas") - .help("The ETH transaction max fee per gas") + .help("The ETH transaction max fee per gas [default: estimated from network conditions]") .value_parser(clap::builder::ValueParser::new(parse_u128_with_underscores)) .required(false) ) diff --git a/src/publish/mod.rs b/src/publish/mod.rs index c7af4f49..5b58d8eb 100644 --- a/src/publish/mod.rs +++ b/src/publish/mod.rs @@ -293,6 +293,7 @@ pub async fn execute( keystore_path: &Path, rpc_uri: &str, real: &bool, + unpublish: &bool, gas_limit: u128, max_priority_fee_per_gas: Option, max_fee_per_gas: Option, @@ -327,17 +328,33 @@ pub async fn execute( let multicall_address = Address::from_str(MULTICALL_ADDRESS)?; let kino_account_impl = Address::from_str(KINO_ACCOUNT_IMPL)?; - let multicall = make_multicall(metadata_uri, &metadata_hash, kimap, multicall_address); + let (to, call) = if *unpublish { + let app_node = format!("{}.{}", name, publisher); + let (app_tba, owner, _) = kimap_get( + &app_node, + kimap, + &provider, + ).await?; + let exists = app_tba != Address::default() && owner == wallet_address; + if !exists { + return Err(eyre!("Can't find {app_node} to unpublish.")); + } - let (to, call) = prepare_kimap_put( - multicall, - name.clone(), - &publisher, - kimap, - &provider, - wallet_address, - kino_account_impl, - ).await?; + let multicall = make_multicall("", "", kimap, multicall_address); + (app_tba, multicall) + } else { + let multicall = make_multicall(metadata_uri, &metadata_hash, kimap, multicall_address); + + prepare_kimap_put( + multicall, + name.clone(), + &publisher, + kimap, + &provider, + wallet_address, + kino_account_impl, + ).await? + }; let nonce = provider.get_transaction_count(wallet_address).await?; let gas_price = provider.get_gas_price().await?; @@ -361,6 +378,6 @@ pub async fn execute( tx_hash, tx_hash, ); - info!("{name} tx sent: {link}"); + info!("{} {name} tx sent: {link}", if *unpublish { "unpublish" } else { "publish" }); Ok(()) } From e3a698f74dc0ea5b3621551ef19c2f775926c57f Mon Sep 17 00:00:00 2001 From: hosted-fornet Date: Tue, 13 Aug 2024 21:22:07 -0700 Subject: [PATCH 16/17] publish: add ledger & trezor support --- Cargo.lock | 154 ++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 2 + src/main.rs | 38 ++++++++--- src/publish/mod.rs | 40 ++++++++++-- 4 files changed, 219 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 23d43236..09b3127d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -74,7 +74,9 @@ dependencies = [ "alloy-rpc-types 0.1.4", "alloy-serde 0.1.4", "alloy-signer", + "alloy-signer-ledger", "alloy-signer-local", + "alloy-signer-trezor", "alloy-transport 0.1.4", "alloy-transport-http", "alloy-transport-ws", @@ -475,6 +477,24 @@ dependencies = [ "thiserror", ] +[[package]] +name = "alloy-signer-ledger" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "575e4c924b23132234c75bd1f8f3871c1bc12ba462f76af9b59249515a38253e" +dependencies = [ + "alloy-consensus 0.1.4", + "alloy-network", + "alloy-primitives", + "alloy-signer", + "async-trait", + "coins-ledger", + "futures-util", + "semver 1.0.23", + "thiserror", + "tracing", +] + [[package]] name = "alloy-signer-local" version = "0.1.4" @@ -493,6 +513,23 @@ dependencies = [ "thiserror", ] +[[package]] +name = "alloy-signer-trezor" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd82e86e4a6604fd11f84b170638d16dcdac9db6c2b5f5b91a3941b7e7af7f94" +dependencies = [ + "alloy-consensus 0.1.4", + "alloy-network", + "alloy-primitives", + "alloy-signer", + "async-trait", + "semver 1.0.23", + "thiserror", + "tracing", + "trezor-client", +] + [[package]] name = "alloy-sol-macro" version = "0.7.7" @@ -1111,6 +1148,29 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +[[package]] +name = "coins-ledger" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "166ef757aa936b45f3e5d39c344047f65ef7d25a50067246a498021a816d074b" +dependencies = [ + "async-trait", + "byteorder", + "cfg-if", + "const-hex", + "getrandom", + "hidapi-rusb", + "js-sys", + "log", + "nix 0.26.4", + "once_cell", + "thiserror", + "tokio", + "tracing", + "wasm-bindgen", + "wasm-bindgen-futures", +] + [[package]] name = "color-eyre" version = "0.6.3" @@ -1674,8 +1734,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -1781,6 +1843,18 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" +[[package]] +name = "hidapi-rusb" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efdc2ec354929a6e8f3c6b6923a4d97427ec2f764cfee8cd4bfe890946cdf08b" +dependencies = [ + "cc", + "libc", + "pkg-config", + "rusb", +] + [[package]] name = "hmac" version = "0.12.1" @@ -2145,7 +2219,7 @@ dependencies = [ "git2", "hex", "kinode_process_lib", - "nix", + "nix 0.27.1", "regex", "reqwest 0.11.27", "rmp-serde", @@ -2227,6 +2301,18 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "libusb1-sys" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da050ade7ac4ff1ba5379af847a10a10a8e284181e060105bf8d86960ce9ce0f" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "libz-sys" version = "1.1.18" @@ -2285,6 +2371,15 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + [[package]] name = "mime" version = "0.3.17" @@ -2339,6 +2434,19 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset", + "pin-utils", +] + [[package]] name = "nix" version = "0.27.1" @@ -2733,6 +2841,26 @@ dependencies = [ "unarray", ] +[[package]] +name = "protobuf" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b65f4a8ec18723a734e5dc09c173e0abf9690432da5340285d536edcb4dac190" +dependencies = [ + "once_cell", + "protobuf-support", + "thiserror", +] + +[[package]] +name = "protobuf-support" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6872f4d4f4b98303239a2b5838f5bbbb77b01ffc892d627957f37a22d7cfe69c" +dependencies = [ + "thiserror", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -3044,6 +3172,16 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" +[[package]] +name = "rusb" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab9f9ff05b63a786553a4c02943b74b34a988448671001e9a27e2f0565cc05a4" +dependencies = [ + "libc", + "libusb1-sys", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -3926,6 +4064,20 @@ dependencies = [ "tracing-serde", ] +[[package]] +name = "trezor-client" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f62c95b37f6c769bd65a0d0beb8b2b003e72998003b896a616a6777c645c05ed" +dependencies = [ + "byteorder", + "hex", + "protobuf", + "rusb", + "thiserror", + "tracing", +] + [[package]] name = "try-lock" version = "0.2.5" diff --git a/Cargo.toml b/Cargo.toml index 31dae027..8cf96e05 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,9 @@ alloy = { version = "0.1.3", features = [ "rpc-types-eth", "signers", "signer-keystore", + "signer-ledger", "signer-local", + "signer-trezor", ] } alloy-sol-macro = "0.7.6" alloy-sol-types = "0.7.6" diff --git a/src/main.rs b/src/main.rs index c1f15deb..0db98395 100644 --- a/src/main.rs +++ b/src/main.rs @@ -338,7 +338,11 @@ async fn execute( Some(("publish", matches)) => { let package_dir = PathBuf::from(matches.get_one::("DIR").unwrap()); let metadata_uri = matches.get_one::("URI").unwrap(); - let keystore_path = PathBuf::from(matches.get_one::("PATH").unwrap()); + let keystore_path = matches + .get_one::("PATH") + .and_then(|kp| Some(PathBuf::from(kp))); + let ledger = matches.get_one::("LEDGER").unwrap(); + let trezor = matches.get_one::("TREZOR").unwrap(); let rpc_uri = matches.get_one::("RPC_URI").unwrap(); let real = matches.get_one::("REAL").unwrap(); let unpublish = matches.get_one::("UNPUBLISH").unwrap(); @@ -353,7 +357,9 @@ async fn execute( publish::execute( &package_dir, metadata_uri, - &keystore_path, + keystore_path, + ledger, + trezor, rpc_uri, real, unpublish, @@ -951,6 +957,27 @@ async fn make_app(current_dir: &std::ffi::OsString) -> Result { .help("The package directory to publish") .default_value(current_dir) ) + .arg(Arg::new("PATH") + .action(ArgAction::Set) + .short('k') + .long("keystore-path") + .help("Path to private key keystore (choose 1 of `k`, `l`, `t`)") // TODO: add link to docs? + .required(false) + ) + .arg(Arg::new("LEDGER") + .action(ArgAction::SetTrue) + .short('l') + .long("ledger") + .help("Use Ledger private key (choose 1 of `k`, `l`, `t`)") + .required(false) + ) + .arg(Arg::new("TREZOR") + .action(ArgAction::SetTrue) + .short('t') + .long("trezor") + .help("Use Trezor private key (choose 1 of `k`, `l`, `t`)") + .required(false) + ) .arg(Arg::new("URI") .action(ArgAction::Set) .short('u') @@ -958,13 +985,6 @@ async fn make_app(current_dir: &std::ffi::OsString) -> Result { .help("URI where metadata lives") .required(true) ) - .arg(Arg::new("PATH") - .action(ArgAction::Set) - .short('k') - .long("keystore-path") - .help("Path to private key keystore") // TODO: add link to docs? - .required(true) // TODO: -> false when add hardware wallets - ) .arg(Arg::new("RPC_URI") .action(ArgAction::Set) .short('r') diff --git a/src/publish/mod.rs b/src/publish/mod.rs index 5b58d8eb..52db6c56 100644 --- a/src/publish/mod.rs +++ b/src/publish/mod.rs @@ -10,7 +10,7 @@ use alloy::{ client::WsConnect, types::eth::{TransactionInput, TransactionRequest}, }, - signers::local::LocalSigner, + signers::{ledger, local::LocalSigner, trezor}, }; use alloy_sol_macro::sol; use alloy_sol_types::SolCall; @@ -92,6 +92,28 @@ fn read_keystore(keystore_path: &Path) -> Result<(Address, EthereumWallet)> { Ok((address, wallet)) } +#[instrument(level = "trace", skip_all)] +async fn read_ledger(chain_id: u64) -> Result<(Address, EthereumWallet)> { + let signer = ledger::LedgerSigner::new( + ledger::HDPath::LedgerLive(0), + Some(chain_id), + ).await?; + let address = signer.get_address().await?; + let wallet = EthereumWallet::from(signer); + Ok((address, wallet)) +} + +#[instrument(level = "trace", skip_all)] +async fn read_trezor(chain_id: u64) -> Result<(Address, EthereumWallet)> { + let signer = trezor::TrezorSigner::new( + trezor::HDPath::TrezorLive(0), + Some(chain_id), + ).await?; + let address = signer.get_address().await?; + let wallet = EthereumWallet::from(signer); + Ok((address, wallet)) +} + fn namehash(name: &str) -> [u8; 32] { let mut node = B256::default(); @@ -290,7 +312,9 @@ async fn prepare_kimap_put( pub async fn execute( package_dir: &Path, metadata_uri: &str, - keystore_path: &Path, + keystore_path: Option, + ledger: &bool, + trezor: &bool, rpc_uri: &str, real: &bool, unpublish: &bool, @@ -305,6 +329,14 @@ pub async fn execute( )); } + let chain_id = if *real { REAL_CHAIN_ID } else { FAKE_CHAIN_ID }; + let (wallet_address, wallet) = match (keystore_path, *ledger, *trezor) { + (Some(ref kp), false, false) => read_keystore(kp)?, + (None, true, false) => read_ledger(chain_id).await?, + (None, false, true) => read_trezor(chain_id).await?, + _ => return Err(eyre!("Must supply one and only one of `--keystore_path`, `--ledger`, or `--trezor`")), + }; + let metadata = read_metadata(package_dir)?; let metadata_hash = check_remote_metadata(&metadata, metadata_uri, package_dir).await?; @@ -313,8 +345,6 @@ pub async fn execute( let name = metadata.name.clone().unwrap(); let publisher = metadata.properties.publisher.clone(); - let (wallet_address, wallet) = read_keystore(keystore_path)?; - let ws = WsConnect::new(rpc_uri); let provider: RootProvider = ProviderBuilder::default().on_ws(ws).await?; @@ -363,7 +393,7 @@ pub async fn execute( .to(to) .input(TransactionInput::new(call.into())) .nonce(nonce) - .with_chain_id(if *real { REAL_CHAIN_ID } else { FAKE_CHAIN_ID }) + .with_chain_id(chain_id) .with_gas_limit(gas_limit) .with_max_priority_fee_per_gas(max_priority_fee_per_gas.unwrap_or_else(|| gas_price)) .with_max_fee_per_gas(max_fee_per_gas.unwrap_or_else(|| gas_price)); From 35490d945fa89cfd7dfb14bdbe3e3646075cf766 Mon Sep 17 00:00:00 2001 From: hosted-fornet Date: Tue, 20 Aug 2024 04:16:16 -0700 Subject: [PATCH 17/17] clarify rpc is for optimism mainnet --- src/main.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index 0db98395..6b525b8c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -521,7 +521,7 @@ async fn make_app(current_dir: &std::ffi::OsString) -> Result { .arg(Arg::new("RPC_ENDPOINT") .action(ArgAction::Set) .long("rpc") - .help("Ethereum RPC endpoint (wss://)") + .help("Ethereum Optimism mainnet RPC endpoint (wss://)") .required(false) ) .arg(Arg::new("PERSIST") @@ -598,7 +598,7 @@ async fn make_app(current_dir: &std::ffi::OsString) -> Result { .arg(Arg::new("RPC_ENDPOINT") .action(ArgAction::Set) .long("rpc") - .help("Ethereum RPC endpoint (wss://)") + .help("Ethereum Optimism mainnet RPC endpoint (wss://)") .required(false) ) //.arg(Arg::new("PASSWORD") // TODO: with develop 0.8.0 @@ -989,7 +989,7 @@ async fn make_app(current_dir: &std::ffi::OsString) -> Result { .action(ArgAction::Set) .short('r') .long("rpc") - .help("The ETH RPC WebSockets URI") + .help("Ethereum Optimism mainnet RPC endpoint (wss://)") .required(true) ) .arg(Arg::new("REAL")