diff --git a/.gitignore b/.gitignore index 95fc237d..e227e087 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ indexer.toml # IDE files .vscode/ # migrations/ +.helix diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 0f8d883b..49e7758b 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,6 +1,6 @@ { - "tap-agent": "1.6.0", - "common": "1.3.0", - "config": "1.2.0", - "service": "1.2.2" + "tap-agent": "1.7.2", + "common": "1.4.0", + "config": "1.2.1", + "service": "1.3.2" } diff --git a/.sqlx/query-05cd59d0d9e64a7c6f7d4560a97e6b79fd38523fbcef88b2dd169720b4e9188e.json b/.sqlx/query-05cd59d0d9e64a7c6f7d4560a97e6b79fd38523fbcef88b2dd169720b4e9188e.json new file mode 100644 index 00000000..e3038755 --- /dev/null +++ b/.sqlx/query-05cd59d0d9e64a7c6f7d4560a97e6b79fd38523fbcef88b2dd169720b4e9188e.json @@ -0,0 +1,82 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT * FROM dips_agreements WHERE id=$1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "signature", + "type_info": "Bytea" + }, + { + "ordinal": 2, + "name": "signed_payload", + "type_info": "Bytea" + }, + { + "ordinal": 3, + "name": "protocol", + "type_info": "Bpchar" + }, + { + "ordinal": 4, + "name": "service", + "type_info": "Bytea" + }, + { + "ordinal": 5, + "name": "payee", + "type_info": "Bytea" + }, + { + "ordinal": 6, + "name": "payer", + "type_info": "Bytea" + }, + { + "ordinal": 7, + "name": "created_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 8, + "name": "updated_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 9, + "name": "cancelled_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 10, + "name": "signed_cancellation_payload", + "type_info": "Bytea" + } + ], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false, + false, + false, + true, + true + ] + }, + "hash": "05cd59d0d9e64a7c6f7d4560a97e6b79fd38523fbcef88b2dd169720b4e9188e" +} diff --git a/.sqlx/query-181c9a792289c614082c63d9404ca0a67ae24ca43aead3972f3d77e54638ecb1.json b/.sqlx/query-181c9a792289c614082c63d9404ca0a67ae24ca43aead3972f3d77e54638ecb1.json new file mode 100644 index 00000000..e66f7db3 --- /dev/null +++ b/.sqlx/query-181c9a792289c614082c63d9404ca0a67ae24ca43aead3972f3d77e54638ecb1.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE dips_agreements SET updated_at=$1, cancelled_at=$1, signed_cancellation_payload=$2 WHERE id=$3", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Timestamptz", + "Bytea", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "181c9a792289c614082c63d9404ca0a67ae24ca43aead3972f3d77e54638ecb1" +} diff --git a/.sqlx/query-78fbff2889a77201ec01cd18fc83d4e7e63037d56e86bd7c462c8f8adc0cfac8.json b/.sqlx/query-78fbff2889a77201ec01cd18fc83d4e7e63037d56e86bd7c462c8f8adc0cfac8.json new file mode 100644 index 00000000..32d86355 --- /dev/null +++ b/.sqlx/query-78fbff2889a77201ec01cd18fc83d4e7e63037d56e86bd7c462c8f8adc0cfac8.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO dips_agreements VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,null,null)", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid", + "Bytea", + "Bytea", + "Bpchar", + "Bytea", + "Bytea", + "Bytea", + "Timestamptz", + "Timestamptz" + ] + }, + "nullable": [] + }, + "hash": "78fbff2889a77201ec01cd18fc83d4e7e63037d56e86bd7c462c8f8adc0cfac8" +} diff --git a/Cargo.lock b/Cargo.lock index e2ed3066..4454a150 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -45,6 +45,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", + "getrandom 0.2.15", "once_cell", "version_check", "zerocopy", @@ -67,9 +68,9 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "alloy" -version = "0.2.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f4a4aaae80afd4be443a6aecd92a6b255dcdd000f97996928efb33d8a71e100" +checksum = "ea8ebf106e84a1c37f86244df7da0c7587e697b71a0d565cce079449b85ac6f8" dependencies = [ "alloy-consensus", "alloy-contract", @@ -83,6 +84,9 @@ dependencies = [ "alloy-rpc-types", "alloy-serde", "alloy-signer", + "alloy-signer-aws", + "alloy-signer-gcp", + "alloy-signer-ledger", "alloy-signer-local", "alloy-transport", "alloy-transport-http", @@ -96,36 +100,38 @@ version = "0.1.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "156bfc5dcd52ef9a5f33381701fa03310317e14c65093a9430d3e3557b08dcd3" dependencies = [ - "alloy-primitives 0.8.8", + "alloy-primitives", "num_enum", - "strum 0.26.3", + "strum", ] [[package]] name = "alloy-consensus" -version = "0.2.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04c309895995eaa4bfcc345f5515a39c7df9447798645cc8bf462b6c5bf1dc96" +checksum = "41ed961a48297c732a5d97ee321aa8bb5009ecadbcb077d8bec90cb54e651629" dependencies = [ "alloy-eips", - "alloy-primitives 0.7.7", + "alloy-primitives", "alloy-rlp", "alloy-serde", + "auto_impl", "c-kzg", + "derive_more", "serde", ] [[package]] name = "alloy-contract" -version = "0.2.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f4e0ef72b0876ae3068b2ed7dfae9ae1779ce13cfaec2ee1f08f5bd0348dc57" +checksum = "460ab80ce4bda1c80bcf96fe7460520476f2c7b734581c6567fac2708e2a60ef" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", "alloy-network", "alloy-network-primitives", - "alloy-primitives 0.7.7", + "alloy-primitives", "alloy-provider", "alloy-pubsub", "alloy-rpc-types-eth", @@ -138,46 +144,72 @@ dependencies = [ [[package]] name = "alloy-core" -version = "0.7.7" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "529fc6310dc1126c8de51c376cbc59c79c7f662bd742be7dc67055d5421a81b4" +checksum = "eeb750349efda145ca6aada68d0336067f7f364d7d44ef09e2cf000b040c5e99" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", - "alloy-primitives 0.7.7", + "alloy-primitives", + "alloy-rlp", "alloy-sol-types", ] [[package]] name = "alloy-dyn-abi" -version = "0.7.7" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413902aa18a97569e60f679c23f46a18db1656d87ab4d4e49d0e1e52042f66df" +checksum = "f95d76a38cae906fd394a5afb0736aaceee5432efe76addfd71048e623e208af" dependencies = [ "alloy-json-abi", - "alloy-primitives 0.7.7", + "alloy-primitives", "alloy-sol-type-parser", "alloy-sol-types", "const-hex", - "derive_more 0.99.18", + "derive_more", "itoa", "serde", "serde_json", - "winnow 0.6.20", + "winnow", +] + +[[package]] +name = "alloy-eip2930" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0069cf0642457f87a01a014f6dc29d5d893cd4fd8fddf0c3cdfad1bb3ebafc41" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "serde", +] + +[[package]] +name = "alloy-eip7702" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ffc577390ce50234e02d841214b3dc0bea6aaaae8e04bbf3cb82e9a45da9eb" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "derive_more", + "k256", + "serde", ] [[package]] name = "alloy-eips" -version = "0.2.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9431c99a3b3fe606ede4b3d4043bdfbcb780c45b8d8d226c3804e2b75cfbe68" +checksum = "b69e06cf9c37be824b9d26d6d101114fdde6af0c87de2828b414c05c4b3daa71" dependencies = [ - "alloy-primitives 0.7.7", + "alloy-eip2930", + "alloy-eip7702", + "alloy-primitives", "alloy-rlp", "alloy-serde", "c-kzg", - "derive_more 0.99.18", - "k256", + "derive_more", "once_cell", "serde", "sha2", @@ -185,22 +217,22 @@ dependencies = [ [[package]] name = "alloy-genesis" -version = "0.2.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79614dfe86144328da11098edcc7bc1a3f25ad8d3134a9eb9e857e06f0d9840d" +checksum = "dde15e14944a88bd6a57d325e9a49b75558746fe16aaccc79713ae50a6a9574c" dependencies = [ - "alloy-primitives 0.7.7", + "alloy-primitives", "alloy-serde", "serde", ] [[package]] name = "alloy-json-abi" -version = "0.7.7" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc05b04ac331a9f07e3a4036ef7926e49a8bf84a99a1ccfc7e2ab55a5fcbb372" +checksum = "31a0f0d51db8a1a30a4d98a9f90e090a94c8f44cb4d9eafc7e03aa6d00aae984" dependencies = [ - "alloy-primitives 0.7.7", + "alloy-primitives", "alloy-sol-type-parser", "serde", "serde_json", @@ -208,11 +240,11 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "0.2.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57e2865c4c3bb4cdad3f0d9ec1ab5c0c657ba69a375651bd35e32fb6c180ccc2" +checksum = "af5979e0d5a7bf9c7eb79749121e8256e59021af611322aee56e77e20776b4b3" dependencies = [ - "alloy-primitives 0.7.7", + "alloy-primitives", "alloy-sol-types", "serde", "serde_json", @@ -222,15 +254,15 @@ dependencies = [ [[package]] name = "alloy-network" -version = "0.2.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e701fc87ef9a3139154b0b4ccb935b565d27ffd9de020fe541bf2dec5ae4ede" +checksum = "204237129086ce5dc17a58025e93739b01b45313841f98fa339eb1d780511e57" dependencies = [ "alloy-consensus", "alloy-eips", "alloy-json-rpc", "alloy-network-primitives", - "alloy-primitives 0.7.7", + "alloy-primitives", "alloy-rpc-types-eth", "alloy-serde", "alloy-signer", @@ -243,59 +275,50 @@ dependencies = [ [[package]] name = "alloy-network-primitives" -version = "0.2.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec9d5a0f9170b10988b6774498a022845e13eda94318440d17709d50687f67f9" +checksum = "514f70ee2a953db21631cd817b13a1571474ec77ddc03d47616d5e8203489fde" dependencies = [ - "alloy-primitives 0.7.7", + "alloy-consensus", + "alloy-eips", + "alloy-primitives", "alloy-serde", "serde", ] [[package]] name = "alloy-primitives" -version = "0.7.7" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccb3ead547f4532bc8af961649942f0b9c16ee9226e26caa3f38420651cc0bf4" +checksum = "8edae627382349b56cd6a7a2106f4fd69b243a9233e560c55c2e03cabb7e1d3c" dependencies = [ "alloy-rlp", "bytes", "cfg-if", "const-hex", - "derive_more 0.99.18", + "derive_more", + "foldhash", + "hashbrown 0.15.0", "hex-literal", + "indexmap 2.6.0", "itoa", "k256", "keccak-asm", + "paste", "proptest", "rand 0.8.5", "ruint", + "rustc-hash", "serde", - "tiny-keccak", -] - -[[package]] -name = "alloy-primitives" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f35429a652765189c1c5092870d8360ee7b7769b09b06d89ebaefd34676446" -dependencies = [ - "bytes", - "cfg-if", - "const-hex", - "derive_more 1.0.0", - "hex-literal", - "itoa", - "paste", - "ruint", + "sha3", "tiny-keccak", ] [[package]] name = "alloy-provider" -version = "0.2.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9c0ab10b93de601a6396fc7ff2ea10d3b28c46f079338fa562107ebf9857c8" +checksum = "4814d141ede360bb6cd1b4b064f1aab9de391e7c4d0d4d50ac89ea4bc1e25fbd" dependencies = [ "alloy-chains", "alloy-consensus", @@ -303,7 +326,7 @@ dependencies = [ "alloy-json-rpc", "alloy-network", "alloy-network-primitives", - "alloy-primitives 0.7.7", + "alloy-primitives", "alloy-pubsub", "alloy-rpc-client", "alloy-rpc-types-eth", @@ -314,27 +337,31 @@ dependencies = [ "async-stream", "async-trait", "auto_impl", - "dashmap 5.5.3", + "dashmap 6.1.0", "futures", "futures-utils-wasm", "lru", - "pin-project", + "parking_lot", + "pin-project 1.1.6", "reqwest 0.12.8", + "schnellru", "serde", "serde_json", + "thiserror", "tokio", "tracing", "url", + "wasmtimer", ] [[package]] name = "alloy-pubsub" -version = "0.2.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f5da2c55cbaf229bad3c5f8b00b5ab66c74ef093e5f3a753d874cfecf7d2281" +checksum = "96ba46eb69ddf7a9925b81f15229cb74658e6eebe5dd30a5b74e2cd040380573" dependencies = [ "alloy-json-rpc", - "alloy-primitives 0.7.7", + "alloy-primitives", "alloy-transport", "bimap", "futures", @@ -342,15 +369,15 @@ dependencies = [ "serde_json", "tokio", "tokio-stream", - "tower 0.4.13", + "tower 0.5.1", "tracing", ] [[package]] name = "alloy-rlp" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26154390b1d205a4a7ac7352aa2eb4f81f391399d4e2f546fb81a2f8bb383f62" +checksum = "da0822426598f95e45dd1ea32a738dac057529a709ee645fcc516ffa4cbde08f" dependencies = [ "alloy-rlp-derive", "arrayvec 0.7.6", @@ -359,9 +386,9 @@ dependencies = [ [[package]] name = "alloy-rlp-derive" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d0f2d905ebd295e7effec65e5f6868d153936130ae718352771de3e7d03c75c" +checksum = "2b09cae092c27b6f1bde952653a22708691802e57bfef4a2973b80bea21efd3f" dependencies = [ "proc-macro2", "quote", @@ -370,35 +397,37 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "0.2.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b38e3ffdb285df5d9f60cb988d336d9b8e3505acb78750c3bc60336a7af41d3" +checksum = "7fc2bd1e7403463a5f2c61e955bcc9d3072b63aa177442b0f9aa6a6d22a941e3" dependencies = [ "alloy-json-rpc", - "alloy-primitives 0.7.7", + "alloy-primitives", "alloy-pubsub", "alloy-transport", "alloy-transport-http", "alloy-transport-ipc", "alloy-transport-ws", "futures", - "pin-project", + "pin-project 1.1.6", "reqwest 0.12.8", "serde", "serde_json", "tokio", "tokio-stream", - "tower 0.4.13", + "tower 0.5.1", "tracing", "url", + "wasmtimer", ] [[package]] name = "alloy-rpc-types" -version = "0.2.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c31a3750b8f5a350d17354e46a52b0f2f19ec5f2006d816935af599dedc521" +checksum = "eea9bf1abdd506f985a53533f5ac01296bcd6102c5e139bbc5d40bc468d2c916" dependencies = [ + "alloy-primitives", "alloy-rpc-types-engine", "alloy-rpc-types-eth", "alloy-serde", @@ -407,60 +436,58 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" -version = "0.2.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff63f51b2fb2f547df5218527fd0653afb1947bf7fead5b3ce58c75d170b30f7" +checksum = "886d22d41992287a235af2f3af4299b5ced2bcafb81eb835572ad35747476946" dependencies = [ "alloy-consensus", "alloy-eips", - "alloy-primitives 0.7.7", + "alloy-primitives", "alloy-rlp", - "alloy-rpc-types-eth", "alloy-serde", - "jsonwebtoken", - "rand 0.8.5", + "derive_more", "serde", - "thiserror", + "strum", ] [[package]] name = "alloy-rpc-types-eth" -version = "0.2.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81e18424d962d7700a882fe423714bd5b9dde74c7a7589d4255ea64068773aef" +checksum = "00b034779a4850b4b03f5be5ea674a1cf7d746b2da762b34d1860ab45e48ca27" dependencies = [ "alloy-consensus", "alloy-eips", "alloy-network-primitives", - "alloy-primitives 0.7.7", + "alloy-primitives", "alloy-rlp", "alloy-serde", "alloy-sol-types", + "derive_more", "itertools 0.13.0", "serde", "serde_json", - "thiserror", ] [[package]] name = "alloy-serde" -version = "0.2.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33feda6a53e6079895aed1d08dcb98a1377b000d80d16370fbbdb8155d547ef" +checksum = "028e72eaa9703e4882344983cfe7636ce06d8cce104a78ea62fd19b46659efc4" dependencies = [ - "alloy-primitives 0.7.7", + "alloy-primitives", "serde", "serde_json", ] [[package]] name = "alloy-signer" -version = "0.2.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740a25b92e849ed7b0fa013951fe2f64be9af1ad5abe805037b44fb7770c5c47" +checksum = "592c185d7100258c041afac51877660c7bf6213447999787197db4842f0e938e" dependencies = [ "alloy-dyn-abi", - "alloy-primitives 0.7.7", + "alloy-primitives", "alloy-sol-types", "async-trait", "auto_impl", @@ -469,15 +496,71 @@ dependencies = [ "thiserror", ] +[[package]] +name = "alloy-signer-aws" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a406102908a4e51834f32c4e5c1b29aa2c407b3fd23a5cad129c28b56d85e1b8" +dependencies = [ + "alloy-consensus", + "alloy-network", + "alloy-primitives", + "alloy-signer", + "async-trait", + "aws-sdk-kms", + "k256", + "spki", + "thiserror", + "tracing", +] + +[[package]] +name = "alloy-signer-gcp" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8d363e12280cb43747d3b62a1e6f00d595bc1a56464bb20200c6b6ca5d68185" +dependencies = [ + "alloy-consensus", + "alloy-network", + "alloy-primitives", + "alloy-signer", + "async-trait", + "gcloud-sdk", + "k256", + "spki", + "thiserror", + "tracing", +] + +[[package]] +name = "alloy-signer-ledger" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a642c9f66ac73ae0d5398ce7ce3ce5bdfad5658d549abd48ea48962e585dca" +dependencies = [ + "alloy-consensus", + "alloy-dyn-abi", + "alloy-network", + "alloy-primitives", + "alloy-signer", + "alloy-sol-types", + "async-trait", + "coins-ledger", + "futures-util", + "semver 1.0.23", + "thiserror", + "tracing", +] + [[package]] name = "alloy-signer-local" -version = "0.2.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b0707d4f63e4356a110b30ef3add8732ab6d181dd7be4607bf79b8777105cee" +checksum = "6614f02fc1d5b079b2a4a5320018317b506fd0a6d67c1fd5542a71201724986c" dependencies = [ "alloy-consensus", "alloy-network", - "alloy-primitives 0.7.7", + "alloy-primitives", "alloy-signer", "async-trait", "coins-bip32", @@ -489,13 +572,13 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.7.7" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b40397ddcdcc266f59f959770f601ce1280e699a91fc1862f29cef91707cd09" +checksum = "841eabaa4710f719fddbc24c95d386eae313f07e6da4babc25830ee37945be0c" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", - "proc-macro-error", + "proc-macro-error2", "proc-macro2", "quote", "syn 2.0.79", @@ -503,16 +586,16 @@ dependencies = [ [[package]] name = "alloy-sol-macro-expander" -version = "0.7.7" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "867a5469d61480fea08c7333ffeca52d5b621f5ca2e44f271b117ec1fc9a0525" +checksum = "6672337f19d837b9f7073c45853aeb528ed9f7dd6a4154ce683e9e5cb7794014" dependencies = [ "alloy-json-abi", "alloy-sol-macro-input", "const-hex", "heck 0.5.0", "indexmap 2.6.0", - "proc-macro-error", + "proc-macro-error2", "proc-macro2", "quote", "syn 2.0.79", @@ -522,9 +605,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-input" -version = "0.7.7" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e482dc33a32b6fadbc0f599adea520bd3aaa585c141a80b404d0a3e3fa72528" +checksum = "0dff37dd20bfb118b777c96eda83b2067f4226d2644c5cfa00187b3bc01770ba" dependencies = [ "alloy-json-abi", "const-hex", @@ -539,22 +622,22 @@ dependencies = [ [[package]] name = "alloy-sol-type-parser" -version = "0.7.7" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbcba3ca07cf7975f15d871b721fb18031eec8bce51103907f6dcce00b255d98" +checksum = "5b853d42292dbb159671a3edae3b2750277ff130f32b726fe07dc2b17aa6f2b5" dependencies = [ "serde", - "winnow 0.6.20", + "winnow", ] [[package]] name = "alloy-sol-types" -version = "0.7.7" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a91ca40fa20793ae9c3841b83e74569d1cc9af29a2f5237314fd3452d51e38c7" +checksum = "aa828bb1b9a6dc52208fbb18084fb9ce2c30facc2bfda6a5d922349b4990354f" dependencies = [ "alloy-json-abi", - "alloy-primitives 0.7.7", + "alloy-primitives", "alloy-sol-macro", "const-hex", "serde", @@ -562,9 +645,9 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "0.2.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d0590afbdacf2f8cca49d025a2466f3b6584a016a8b28f532f29f8da1007bae" +checksum = "be77579633ebbc1266ae6fd7694f75c408beb1aeb6865d0b18f22893c265a061" dependencies = [ "alloy-json-rpc", "base64 0.22.1", @@ -574,31 +657,32 @@ dependencies = [ "serde_json", "thiserror", "tokio", - "tower 0.4.13", + "tower 0.5.1", "tracing", "url", + "wasmtimer", ] [[package]] name = "alloy-transport-http" -version = "0.2.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2437d145d80ea1aecde8574d2058cceb8b3c9cba05f6aea8e67907c660d46698" +checksum = "91fd1a5d0827939847983b46f2f79510361f901dc82f8e3c38ac7397af142c6e" dependencies = [ "alloy-json-rpc", "alloy-transport", "reqwest 0.12.8", "serde_json", - "tower 0.4.13", + "tower 0.5.1", "tracing", "url", ] [[package]] name = "alloy-transport-ipc" -version = "0.2.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "804494366e20468776db4e18f9eb5db7db0fe14f1271eb6dbf155d867233405c" +checksum = "8073d1186bfeeb8fbdd1292b6f1a0731f3aed8e21e1463905abfae0b96a887a6" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -606,7 +690,7 @@ dependencies = [ "bytes", "futures", "interprocess", - "pin-project", + "pin-project 1.1.6", "serde_json", "tokio", "tokio-util", @@ -615,9 +699,9 @@ dependencies = [ [[package]] name = "alloy-transport-ws" -version = "0.2.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af855163e7df008799941aa6dd324a43ef2bf264b08ba4b22d44aad6ced65300" +checksum = "61f27837bb4a1d6c83a28231c94493e814882f0e9058648a97e908a5f3fc9fcf" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -626,7 +710,7 @@ dependencies = [ "rustls 0.23.14", "serde_json", "tokio", - "tokio-tungstenite 0.23.1", + "tokio-tungstenite", "tracing", "ws_stream_wasm", ] @@ -870,6 +954,19 @@ dependencies = [ "futures-core", ] +[[package]] +name = "async-compression" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cb8f1d480b0ea3783ab015936d2a55c87e219676f0c0b7dec61494043f21857" +dependencies = [ + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", +] + [[package]] name = "async-graphql" version = "7.0.11" @@ -909,7 +1006,7 @@ checksum = "e9aa80e171205c6d562057fd5a49167c8fbe61f7db2bed6540f6d4f2234d7ff2" dependencies = [ "async-graphql", "async-trait", - "axum 0.7.7", + "axum", "bytes", "futures-util", "serde_json", @@ -928,10 +1025,10 @@ dependencies = [ "Inflector", "async-graphql-parser", "darling", - "proc-macro-crate 3.2.0", + "proc-macro-crate", "proc-macro2", "quote", - "strum 0.26.3", + "strum", "syn 2.0.79", "thiserror", ] @@ -1079,35 +1176,202 @@ dependencies = [ ] [[package]] -name = "axum" -version = "0.6.20" +name = "aws-credential-types" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +checksum = "60e8f6b615cb5fc60a98132268508ad104310f0cfb25a1c22eee76efdf9154da" dependencies = [ - "async-trait", - "axum-core 0.3.4", - "bitflags 1.3.2", + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "zeroize", +] + +[[package]] +name = "aws-runtime" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a10d5c055aa540164d9561a0e2e74ad30f0dcf7393c3a92f6733ddf9c5762468" +dependencies = [ + "aws-credential-types", + "aws-sigv4", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand 2.1.1", + "http 0.2.12", + "http-body 0.4.6", + "once_cell", + "percent-encoding", + "pin-project-lite", + "tracing", + "uuid", +] + +[[package]] +name = "aws-sdk-kms" +version = "1.49.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f4c89f1d2e0df99ccd21f98598c1e587ad78bd87ae22a74aba392b5566bb038" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "http 0.2.12", + "once_cell", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sigv4" +version = "1.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5619742a0d8f253be760bfbb8e8e8368c69e3587e4637af5754e488a611499b1" +dependencies = [ + "aws-credential-types", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", "bytes", + "form_urlencoded", + "hex", + "hmac", + "http 0.2.12", + "http 1.1.0", + "once_cell", + "percent-encoding", + "sha2", + "time", + "tracing", +] + +[[package]] +name = "aws-smithy-async" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62220bc6e97f946ddd51b5f1361f78996e704677afc518a4ff66b7a72ea1378c" +dependencies = [ "futures-util", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "aws-smithy-http" +version = "0.60.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8bc3e8fdc6b8d07d976e301c02fe553f72a39b7a9fea820e023268467d7ab6" +dependencies = [ + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "bytes-utils", + "futures-core", "http 0.2.12", "http-body 0.4.6", - "hyper 0.14.31", - "itoa", - "matchit", - "memchr", - "mime", + "once_cell", "percent-encoding", "pin-project-lite", - "rustversion", - "serde", - "serde_json", - "serde_path_to_error", - "serde_urlencoded", - "sync_wrapper 0.1.2", + "pin-utils", + "tracing", +] + +[[package]] +name = "aws-smithy-json" +version = "0.60.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4683df9469ef09468dad3473d129960119a0d3593617542b7d52086c8486f2d6" +dependencies = [ + "aws-smithy-types", +] + +[[package]] +name = "aws-smithy-runtime" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be28bd063fa91fd871d131fc8b68d7cd4c5fa0869bea68daca50dcb1cbd76be2" +dependencies = [ + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "fastrand 2.1.1", + "http 0.2.12", + "http-body 0.4.6", + "http-body 1.0.1", + "httparse", + "once_cell", + "pin-project-lite", + "pin-utils", "tokio", - "tower 0.4.13", - "tower-layer", - "tower-service", + "tracing", +] + +[[package]] +name = "aws-smithy-runtime-api" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e086682a53d3aa241192aa110fa8dfce98f2f5ac2ead0de84d41582c7e8fdb96" +dependencies = [ + "aws-smithy-async", + "aws-smithy-types", + "bytes", + "http 0.2.12", + "http 1.1.0", + "pin-project-lite", + "tokio", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-smithy-types" +version = "1.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07c9cdc179e6afbf5d391ab08c85eac817b51c87e1892a5edb5f7bbdc64314b4" +dependencies = [ + "base64-simd", + "bytes", + "bytes-utils", + "http 0.2.12", + "http 1.1.0", + "http-body 0.4.6", + "http-body 1.0.1", + "http-body-util", + "itoa", + "num-integer", + "pin-project-lite", + "pin-utils", + "ryu", + "serde", + "time", +] + +[[package]] +name = "aws-types" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5221b91b3e441e6675310829fd8984801b772cb1546ef6c0e54dec9f1ac13fef" +dependencies = [ + "aws-credential-types", + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "rustc_version 0.4.1", + "tracing", ] [[package]] @@ -1117,7 +1381,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae" dependencies = [ "async-trait", - "axum-core 0.4.5", + "axum-core", "base64 0.22.1", "bytes", "futures-util", @@ -1140,30 +1404,13 @@ dependencies = [ "sha1", "sync_wrapper 1.0.1", "tokio", - "tokio-tungstenite 0.24.0", + "tokio-tungstenite", "tower 0.5.1", "tower-layer", "tower-service", "tracing", ] -[[package]] -name = "axum-core" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http 0.2.12", - "http-body 0.4.6", - "mime", - "rustversion", - "tower-layer", - "tower-service", -] - [[package]] name = "axum-core" version = "0.4.5" @@ -1191,8 +1438,8 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73c3220b188aea709cf1b6c5f9b01c3bd936bb08bd2b5184a12b35ac8131b1f9" dependencies = [ - "axum 0.7.7", - "axum-core 0.4.5", + "axum", + "axum-core", "bytes", "futures-util", "headers", @@ -1246,6 +1493,16 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" +dependencies = [ + "outref", + "vsimd", +] + [[package]] name = "base64ct" version = "1.6.0" @@ -1258,15 +1515,6 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" -[[package]] -name = "beef" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" -dependencies = [ - "serde", -] - [[package]] name = "bigdecimal" version = "0.4.5" @@ -1367,15 +1615,6 @@ dependencies = [ "wyz", ] -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] - [[package]] name = "block-buffer" version = "0.10.4" @@ -1437,7 +1676,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3ef8005764f53cd4dca619f5bf64cafd4664dada50ece25e4d81de54c80cc0b" dependencies = [ "once_cell", - "proc-macro-crate 3.2.0", + "proc-macro-crate", "proc-macro2", "quote", "syn 2.0.79", @@ -1454,16 +1693,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "bstr" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" -dependencies = [ - "memchr", - "serde", -] - [[package]] name = "build-info" version = "0.0.39" @@ -1501,7 +1730,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6fa54101dfbd88efc3981859e92c3d47c729ff54d5df73ec36505c337e5c5e" dependencies = [ "chrono", - "derive_more 1.0.0", + "derive_more", "semver 1.0.23", "serde", ] @@ -1588,6 +1817,16 @@ dependencies = [ "serde", ] +[[package]] +name = "bytes-utils" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dafe3a8757b027e2be6e4e5601ed563c55989fcf1546e933c66c8eb3a058d35" +dependencies = [ + "bytes", + "either", +] + [[package]] name = "c-kzg" version = "1.0.3" @@ -1725,9 +1964,9 @@ checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "coins-bip32" -version = "0.11.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66c43ff7fd9ff522219058808a259e61423335767b1071d5b346de60d9219657" +checksum = "2073678591747aed4000dd468b97b14d7007f7936851d3f2f01846899f5ebf08" dependencies = [ "bs58", "coins-core", @@ -1741,9 +1980,9 @@ dependencies = [ [[package]] name = "coins-bip39" -version = "0.11.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4587c0b4064da887ed39a6522f577267d57e58bdd583178cd877d721b56a2e" +checksum = "74b169b26623ff17e9db37a539fe4f15342080df39f129ef7631df7683d6d9d4" dependencies = [ "bitvec", "coins-bip32", @@ -1757,9 +1996,9 @@ dependencies = [ [[package]] name = "coins-core" -version = "0.11.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b3aeeec621f4daec552e9d28befd58020a78cfc364827d06a753e8bc13c6c4b" +checksum = "62b962ad8545e43a28e14e87377812ba9ae748dd4fd963f4c10e9fcc6d13475b" dependencies = [ "base64 0.21.7", "bech32", @@ -1774,6 +2013,29 @@ dependencies = [ "thiserror", ] +[[package]] +name = "coins-ledger" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab9bc0994d0aa0f4ade5f3a9baf4a8d936f250278c85a1124b401860454246ab" +dependencies = [ + "async-trait", + "byteorder", + "cfg-if", + "const-hex", + "getrandom 0.2.15", + "hidapi-rusb", + "js-sys", + "log", + "nix", + "once_cell", + "thiserror", + "tokio", + "tracing", + "wasm-bindgen", + "wasm-bindgen-futures", +] + [[package]] name = "colorchoice" version = "1.0.2" @@ -1831,12 +2093,6 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - [[package]] name = "core-foundation" version = "0.9.4" @@ -1893,6 +2149,15 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + [[package]] name = "crossbeam-epoch" version = "0.9.18" @@ -2076,19 +2341,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "derive_more" -version = "0.99.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" -dependencies = [ - "convert_case", - "proc-macro2", - "quote", - "rustc_version 0.4.1", - "syn 2.0.79", -] - [[package]] name = "derive_more" version = "1.0.0" @@ -2131,7 +2383,7 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer 0.10.4", + "block-buffer", "const-oid", "crypto-common", "subtle", @@ -2196,6 +2448,7 @@ dependencies = [ "ff", "generic-array", "group", + "pem-rfc7468", "pkcs8", "rand_core 0.6.4", "sec1", @@ -2355,6 +2608,16 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "flate2" +version = "1.0.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "flume" version = "0.11.0" @@ -2555,12 +2818,40 @@ dependencies = [ "slab", ] -[[package]] -name = "futures-utils-wasm" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" - +[[package]] +name = "futures-utils-wasm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" + +[[package]] +name = "gcloud-sdk" +version = "0.25.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a1130d4e37435a63bd9d968a33c11b64bf35a2013779a353e29cd3598989d39" +dependencies = [ + "async-trait", + "bytes", + "chrono", + "futures", + "hyper 1.5.0", + "jsonwebtoken", + "once_cell", + "prost", + "prost-types", + "reqwest 0.12.8", + "secret-vault-value", + "serde", + "serde_json", + "tokio", + "tonic", + "tower 0.5.1", + "tower-layer", + "tower-util", + "tracing", + "url", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -2608,19 +2899,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" -[[package]] -name = "globset" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19" -dependencies = [ - "aho-corasick", - "bstr", - "log", - "regex-automata 0.4.8", - "regex-syntax 0.8.5", -] - [[package]] name = "governor" version = "0.6.3" @@ -2780,9 +3058,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ff8ae62cd3a9102e5637afc8452c55acf3844001bd5374e0b0bd7b6616c038" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ "ahash 0.8.11", ] @@ -2806,6 +3084,7 @@ dependencies = [ "allocator-api2", "equivalent", "foldhash", + "serde", ] [[package]] @@ -2880,6 +3159,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 = "hkdf" version = "0.12.4" @@ -3067,12 +3358,26 @@ dependencies = [ "hyper-util", "log", "rustls 0.23.14", + "rustls-native-certs 0.8.0", "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", "tower-service", ] +[[package]] +name = "hyper-timeout" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" +dependencies = [ + "hyper 1.5.0", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + [[package]] name = "hyper-tls" version = "0.6.0" @@ -3091,9 +3396,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" dependencies = [ "bytes", "futures-channel", @@ -3169,13 +3474,13 @@ dependencies = [ [[package]] name = "indexer-common" -version = "1.3.0" +version = "1.4.0" dependencies = [ "alloy", "anyhow", "async-trait", "autometrics", - "axum 0.7.7", + "axum", "axum-extra", "bigdecimal", "bip39", @@ -3192,7 +3497,7 @@ dependencies = [ "serde", "serde_json", "sqlx", - "tap_core 1.0.0 (git+https://github.com/semiotic-ai/timeline-aggregation-protocol?rev=3fe6bc2)", + "tap_core", "test-log", "thegraph-core", "thegraph-graphql-http", @@ -3207,7 +3512,7 @@ dependencies = [ [[package]] name = "indexer-config" -version = "1.2.0" +version = "1.2.1" dependencies = [ "alloy", "bigdecimal", @@ -3228,14 +3533,28 @@ dependencies = [ "url", ] +[[package]] +name = "indexer-dips" +version = "0.1.0" +dependencies = [ + "alloy", + "alloy-rlp", + "alloy-sol-types", + "anyhow", + "thegraph-core", + "thiserror", +] + [[package]] name = "indexer-service-rs" -version = "1.2.2" +version = "1.3.2" dependencies = [ + "alloy", "anyhow", "async-graphql", "async-graphql-axum", - "axum 0.7.7", + "axum", + "base64 0.22.1", "build-info", "build-info-build", "clap", @@ -3243,6 +3562,7 @@ dependencies = [ "hex-literal", "indexer-common", "indexer-config", + "indexer-dips", "lazy_static", "prometheus", "reqwest 0.12.8", @@ -3255,16 +3575,17 @@ dependencies = [ "tokio", "tracing", "tracing-subscriber", + "uuid", ] [[package]] name = "indexer-tap-agent" -version = "1.6.0" +version = "1.7.2" dependencies = [ "alloy", "anyhow", "async-trait", - "axum 0.7.7", + "axum", "bigdecimal", "clap", "eventuals", @@ -3273,7 +3594,7 @@ dependencies = [ "graphql_client", "indexer-common", "indexer-config", - "jsonrpsee 0.24.6", + "jsonrpsee", "lazy_static", "prometheus", "ractor", @@ -3283,7 +3604,7 @@ dependencies = [ "serde_json", "sqlx", "tap_aggregator", - "tap_core 1.0.0 (git+https://github.com/semiotic-ai/timeline-aggregation-protocol?rev=3fe6bc2)", + "tap_core", "tempfile", "thegraph-core", "thiserror", @@ -3442,58 +3763,24 @@ dependencies = [ [[package]] name = "jsonrpsee" -version = "0.18.2" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1822d18e4384a5e79d94dc9e4d1239cfa9fad24e55b44d2efeff5b394c9fece4" +checksum = "c5c71d8c1a731cc4227c2f698d377e7848ca12c8a48866fc5e6951c43a4db843" dependencies = [ - "jsonrpsee-core 0.18.2", + "jsonrpsee-core", + "jsonrpsee-http-client", "jsonrpsee-proc-macros", "jsonrpsee-server", - "jsonrpsee-types 0.18.2", - "tracing", -] - -[[package]] -name = "jsonrpsee" -version = "0.24.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02f01f48e04e0d7da72280ab787c9943695699c9b32b99158ece105e8ad0afea" -dependencies = [ - "jsonrpsee-core 0.24.6", - "jsonrpsee-http-client", - "jsonrpsee-types 0.24.6", - "tracing", -] - -[[package]] -name = "jsonrpsee-core" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c6832a55f662b5a6ecc844db24b8b9c387453f923de863062c60ce33d62b81" -dependencies = [ - "anyhow", - "async-trait", - "beef", - "futures-util", - "globset", - "hyper 0.14.31", - "jsonrpsee-types 0.18.2", - "parking_lot", - "rand 0.8.5", - "rustc-hash", - "serde", - "serde_json", - "soketto", - "thiserror", + "jsonrpsee-types", "tokio", "tracing", ] [[package]] name = "jsonrpsee-core" -version = "0.24.6" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c2709a32915d816a6e8f625bf72cf74523ebe5d8829f895d6b041b1d3137818" +checksum = "f2882f6f8acb9fdaec7cefc4fd607119a9bd709831df7d7672a1d3b644628280" dependencies = [ "async-trait", "bytes", @@ -3501,7 +3788,10 @@ dependencies = [ "http 1.1.0", "http-body 1.0.1", "http-body-util", - "jsonrpsee-types 0.24.6", + "jsonrpsee-types", + "parking_lot", + "rand 0.8.5", + "rustc-hash", "serde", "serde_json", "thiserror", @@ -3511,9 +3801,9 @@ dependencies = [ [[package]] name = "jsonrpsee-http-client" -version = "0.24.6" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc54db939002b030e794fbfc9d5a925aa2854889c5a2f0352b0bffa54681707e" +checksum = "b3638bc4617f96675973253b3a45006933bde93c2fd8a6170b33c777cc389e5b" dependencies = [ "async-trait", "base64 0.22.1", @@ -3521,8 +3811,8 @@ dependencies = [ "hyper 1.5.0", "hyper-rustls 0.27.3", "hyper-util", - "jsonrpsee-core 0.24.6", - "jsonrpsee-types 0.24.6", + "jsonrpsee-core", + "jsonrpsee-types", "rustls 0.23.14", "rustls-platform-verifier", "serde", @@ -3536,30 +3826,37 @@ dependencies = [ [[package]] name = "jsonrpsee-proc-macros" -version = "0.18.2" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6027ac0b197ce9543097d02a290f550ce1d9432bf301524b013053c0b75cc94" +checksum = "c06c01ae0007548e73412c08e2285ffe5d723195bf268bce67b1b77c3bb2a14d" dependencies = [ - "heck 0.4.1", - "proc-macro-crate 1.3.1", + "heck 0.5.0", + "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.79", ] [[package]] name = "jsonrpsee-server" -version = "0.18.2" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f06661d1a6b6e5b85469dc9c29acfbb9b3bb613797a6fd10a3ebb8a70754057" +checksum = "82ad8ddc14be1d4290cd68046e7d1d37acd408efed6d3ca08aefcc3ad6da069c" dependencies = [ "futures-util", - "hyper 0.14.31", - "jsonrpsee-core 0.18.2", - "jsonrpsee-types 0.18.2", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.5.0", + "hyper-util", + "jsonrpsee-core", + "jsonrpsee-types", + "pin-project 1.1.6", + "route-recognizer", "serde", "serde_json", "soketto", + "thiserror", "tokio", "tokio-stream", "tokio-util", @@ -3569,23 +3866,9 @@ dependencies = [ [[package]] name = "jsonrpsee-types" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e5bf6c75ce2a4217421154adfc65a24d2b46e77286e59bba5d9fa6544ccc8f4" -dependencies = [ - "anyhow", - "beef", - "serde", - "serde_json", - "thiserror", - "tracing", -] - -[[package]] -name = "jsonrpsee-types" -version = "0.24.6" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ca331cd7b3fe95b33432825c2d4c9f5a43963e207fdc01ae67f9fd80ab0930f" +checksum = "a178c60086f24cc35bb82f57c651d0d25d99c4742b4d335de04e97fa1f08a8a1" dependencies = [ "http 1.1.0", "serde", @@ -3686,6 +3969,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 = "linkme" version = "0.3.28" @@ -3777,6 +4072,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 = "metrics" version = "0.21.1" @@ -3815,13 +4119,13 @@ dependencies = [ [[package]] name = "metrics-util" -version = "0.15.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4de2ed6e491ed114b40b732e4d1659a9d53992ebd87490c44a6ffe23739d973e" +checksum = "111cb375987443c3de8d503580b536f77dc8416d32db62d9456db5d93bd7ac47" dependencies = [ "crossbeam-epoch", "crossbeam-utils", - "hashbrown 0.13.1", + "hashbrown 0.13.2", "metrics", "num_cpus", "quanta 0.11.1", @@ -3834,6 +4138,16 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -3901,6 +4215,19 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c96aba5aa877601bb3f6dd6a63a969e1f82e60646e81e71b14496995e9853c91" +[[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 = "no-std-compat" version = "0.4.1" @@ -4105,12 +4432,6 @@ version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" -[[package]] -name = "opaque-debug" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" - [[package]] name = "openssl" version = "0.10.66" @@ -4210,6 +4531,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "outref" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4030760ffd992bef45b0ae3f10ce1aba99e33464c90d14dd7c039884963ddc7a" + [[package]] name = "overload" version = "0.1.1" @@ -4236,7 +4563,7 @@ version = "3.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" dependencies = [ - "proc-macro-crate 3.2.0", + "proc-macro-crate", "proc-macro2", "quote", "syn 1.0.109", @@ -4356,13 +4683,33 @@ dependencies = [ "rustc_version 0.4.1", ] +[[package]] +name = "pin-project" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ef0f924a5ee7ea9cbcea77529dba45f8a9ba9f622419fe3386ca581a3ae9d5a" +dependencies = [ + "pin-project-internal 0.4.30", +] + [[package]] name = "pin-project" version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf123a161dde1e524adf36f90bc5d8d3462824a9c43553ad07a8183161189ec" dependencies = [ - "pin-project-internal", + "pin-project-internal 1.1.6", +] + +[[package]] +name = "pin-project-internal" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "851c8d0ce9bebe43790dedfc86614c23494ac9f423dd618d3a61fc693eafe61e" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -4457,23 +4804,13 @@ dependencies = [ "uint", ] -[[package]] -name = "proc-macro-crate" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" -dependencies = [ - "once_cell", - "toml_edit 0.19.15", -] - [[package]] name = "proc-macro-crate" version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ - "toml_edit 0.22.22", + "toml_edit", ] [[package]] @@ -4485,7 +4822,6 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn 1.0.109", "version_check", ] @@ -4602,6 +4938,38 @@ dependencies = [ "unarray", ] +[[package]] +name = "prost" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b0487d90e047de87f984913713b85c601c05609aad5b0df4b4573fbf69aa13f" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5" +dependencies = [ + "anyhow", + "itertools 0.13.0", + "proc-macro2", + "quote", + "syn 2.0.79", +] + +[[package]] +name = "prost-types" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4759aa0d3a6232fb8dbdb97b61de2c20047c68aca932c7ed76da9d788508d670" +dependencies = [ + "prost", +] + [[package]] name = "protobuf" version = "2.28.0" @@ -4659,12 +5027,61 @@ dependencies = [ "winapi", ] -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quinn" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684" +dependencies = [ + "bytes", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls 0.23.14", + "socket2", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "quinn-proto" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" +dependencies = [ + "bytes", + "rand 0.8.5", + "ring", + "rustc-hash", + "rustls 0.23.14", + "slab", + "thiserror", + "tinyvec", + "tracing", +] + +[[package]] +name = "quinn-udp" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e346e016eacfff12233c243718197ca12f148c84e1e84268a896699b41c71780" +dependencies = [ + "cfg_aliases 0.2.1", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.59.0", +] + [[package]] name = "quote" version = "1.0.37" @@ -4685,7 +5102,7 @@ dependencies = [ "dashmap 6.1.0", "futures", "once_cell", - "strum 0.26.3", + "strum", "tokio", "tracing", ] @@ -4718,6 +5135,7 @@ dependencies = [ "libc", "rand_chacha 0.3.1", "rand_core 0.6.4", + "serde", ] [[package]] @@ -4841,6 +5259,12 @@ dependencies = [ "regex-syntax 0.8.5", ] +[[package]] +name = "regex-lite" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" + [[package]] name = "regex-syntax" version = "0.6.29" @@ -4909,6 +5333,7 @@ version = "0.12.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b" dependencies = [ + "async-compression", "base64 0.22.1", "bytes", "encoding_rs", @@ -4926,11 +5351,16 @@ dependencies = [ "js-sys", "log", "mime", + "mime_guess", "native-tls", "once_cell", "percent-encoding", "pin-project-lite", + "quinn", + "rustls 0.23.14", + "rustls-native-certs 0.8.0", "rustls-pemfile 2.2.0", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", @@ -4938,10 +5368,13 @@ dependencies = [ "system-configuration 0.6.1", "tokio", "tokio-native-tls", + "tokio-rustls 0.26.0", + "tokio-util", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams", "web-sys", "windows-registry", ] @@ -5025,6 +5458,12 @@ dependencies = [ "rustc-hex", ] +[[package]] +name = "route-recognizer" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746" + [[package]] name = "rsa" version = "0.9.6" @@ -5045,32 +5484,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rstest" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de1bb486a691878cd320c2f0d319ba91eeaa2e894066d8b5f8f117c000e9d962" -dependencies = [ - "futures", - "futures-timer", - "rstest_macros", - "rustc_version 0.4.1", -] - -[[package]] -name = "rstest_macros" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290ca1a1c8ca7edb7c3283bd44dc35dd54fdec6253a3912e201ba1072018fca8" -dependencies = [ - "cfg-if", - "proc-macro2", - "quote", - "rustc_version 0.4.1", - "syn 1.0.109", - "unicode-ident", -] - [[package]] name = "ruint" version = "1.12.3" @@ -5101,6 +5514,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 = "rust_decimal" version = "1.36.0" @@ -5125,9 +5548,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" -version = "1.1.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" [[package]] name = "rustc-hex" @@ -5206,6 +5629,19 @@ dependencies = [ "security-framework", ] +[[package]] +name = "rustls-native-certs" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcaf18a4f2be7326cd874a5fa579fae794320a0f388d365dca7e480e55f83f8a" +dependencies = [ + "openssl-probe", + "rustls-pemfile 2.2.0", + "rustls-pki-types", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -5242,7 +5678,7 @@ dependencies = [ "log", "once_cell", "rustls 0.23.14", - "rustls-native-certs", + "rustls-native-certs 0.7.3", "rustls-platform-verifier-android", "rustls-webpki 0.102.8", "security-framework", @@ -5332,6 +5768,17 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "schnellru" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9a8ef13a93c54d20580de1e5c413e624e53121d42fc7e2c11d10ef7f8b02367" +dependencies = [ + "ahash 0.8.11", + "cfg-if", + "hashbrown 0.13.2", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -5390,6 +5837,19 @@ dependencies = [ "zeroize", ] +[[package]] +name = "secret-vault-value" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc32a777b53b3433b974c9c26b6d502a50037f8da94e46cb8ce2ced2cfdfaea0" +dependencies = [ + "prost", + "prost-types", + "serde", + "serde_json", + "zeroize", +] + [[package]] name = "security-framework" version = "2.11.1" @@ -5580,19 +6040,6 @@ dependencies = [ "syn 2.0.79", ] -[[package]] -name = "sha-1" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - [[package]] name = "sha1" version = "0.10.6" @@ -5723,18 +6170,18 @@ dependencies = [ [[package]] name = "soketto" -version = "0.7.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d1c5305e39e09653383c2c7244f2f78b3bcae37cf50c64cb4789c9f5096ec2" +checksum = "37468c595637c10857701c990f93a40ce0e357cedb0953d1c26c8d8027f9bb53" dependencies = [ - "base64 0.13.1", + "base64 0.22.1", "bytes", "futures", - "http 0.2.12", + "http 1.1.0", "httparse", "log", "rand 0.8.5", - "sha-1", + "sha1", ] [[package]] @@ -5839,6 +6286,7 @@ dependencies = [ "tokio-stream", "tracing", "url", + "uuid", ] [[package]] @@ -5922,6 +6370,7 @@ dependencies = [ "stringprep", "thiserror", "tracing", + "uuid", "whoami", ] @@ -5964,6 +6413,7 @@ dependencies = [ "stringprep", "thiserror", "tracing", + "uuid", "whoami", ] @@ -5989,6 +6439,7 @@ dependencies = [ "sqlx-core", "tracing", "url", + "uuid", ] [[package]] @@ -6020,35 +6471,13 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" -[[package]] -name = "strum" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" -dependencies = [ - "strum_macros 0.24.3", -] - [[package]] name = "strum" version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" dependencies = [ - "strum_macros 0.26.4", -] - -[[package]] -name = "strum_macros" -version = "0.24.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "rustversion", - "syn 1.0.109", + "strum_macros", ] [[package]] @@ -6094,9 +6523,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.7.7" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c837dc8852cb7074e46b444afb81783140dab12c58867b49fb3898fbafedf7ea" +checksum = "16320d4a2021ba1a32470b3759676114a918885e9800e68ad60f2c67969fba62" dependencies = [ "paste", "proc-macro2", @@ -6181,31 +6610,32 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tap_aggregator" -version = "0.3.1" -source = "git+https://github.com/semiotic-ai/timeline-aggregation-protocol?rev=eb8447e#eb8447ed4566ced6846c03b510b25b915f985186" +version = "0.3.2" +source = "git+https://github.com/semiotic-ai/timeline-aggregation-protocol?rev=61b47b3#61b47b3b96aaa1437fc390eb581636d51134b006" dependencies = [ "alloy", "anyhow", - "axum 0.6.20", + "axum", "clap", "futures-util", - "jsonrpsee 0.18.2", + "jsonrpsee", "lazy_static", "log", "prometheus", "ruint", "serde", "serde_json", - "strum 0.24.1", - "tap_core 1.0.0 (git+https://github.com/semiotic-ai/timeline-aggregation-protocol?rev=eb8447e)", + "strum", + "tap_core", "tokio", + "tower 0.4.13", "tracing-subscriber", ] [[package]] name = "tap_core" -version = "1.0.0" -source = "git+https://github.com/semiotic-ai/timeline-aggregation-protocol?rev=3fe6bc2#3fe6bc27c10161e5a1c011c088281d3da227df53" +version = "2.0.0" +source = "git+https://github.com/semiotic-ai/timeline-aggregation-protocol?rev=61b47b3#61b47b3b96aaa1437fc390eb581636d51134b006" dependencies = [ "alloy", "anyhow", @@ -6217,24 +6647,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tap_core" -version = "1.0.0" -source = "git+https://github.com/semiotic-ai/timeline-aggregation-protocol?rev=eb8447e#eb8447ed4566ced6846c03b510b25b915f985186" -dependencies = [ - "alloy", - "anyhow", - "async-trait", - "rand 0.8.5", - "rand_core 0.6.4", - "rstest", - "serde", - "strum 0.24.1", - "strum_macros 0.24.3", - "thiserror", - "tokio", -] - [[package]] name = "tempfile" version = "3.13.0" @@ -6270,10 +6682,10 @@ dependencies = [ [[package]] name = "thegraph-core" -version = "0.5.7" -source = "git+https://github.com/edgeandnode/toolshed?rev=85ee00b#85ee00bd1a6ec89e4cf94753a03ab54889e08f38" +version = "0.7.0" +source = "git+https://github.com/edgeandnode/toolshed?rev=1663534fc1738e2db1b11cb54b5bb478ee970d40#1663534fc1738e2db1b11cb54b5bb478ee970d40" dependencies = [ - "alloy-primitives 0.7.7", + "alloy-primitives", "alloy-signer", "alloy-sol-types", "bs58", @@ -6469,9 +6881,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.23.1" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6989540ced10490aaf14e6bad2e3d33728a2813310a0c71d1574304c49631cd" +checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" dependencies = [ "futures-util", "log", @@ -6479,22 +6891,10 @@ dependencies = [ "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", - "tungstenite 0.23.0", + "tungstenite", "webpki-roots 0.26.6", ] -[[package]] -name = "tokio-tungstenite" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" -dependencies = [ - "futures-util", - "log", - "tokio", - "tungstenite 0.24.0", -] - [[package]] name = "tokio-util" version = "0.7.12" @@ -6518,7 +6918,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.22", + "toml_edit", ] [[package]] @@ -6532,26 +6932,48 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.19.15" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap 2.6.0", + "serde", + "serde_spanned", "toml_datetime", - "winnow 0.5.40", + "winnow", ] [[package]] -name = "toml_edit" -version = "0.22.22" +name = "tonic" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" dependencies = [ - "indexmap 2.6.0", - "serde", - "serde_spanned", - "toml_datetime", - "winnow 0.6.20", + "async-stream", + "async-trait", + "axum", + "base64 0.22.1", + "bytes", + "h2 0.4.6", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.5.0", + "hyper-timeout", + "hyper-util", + "percent-encoding", + "pin-project 1.1.6", + "prost", + "rustls-native-certs 0.8.0", + "rustls-pemfile 2.2.0", + "socket2", + "tokio", + "tokio-rustls 0.26.0", + "tokio-stream", + "tower 0.4.13", + "tower-layer", + "tower-service", + "tracing", ] [[package]] @@ -6562,9 +6984,13 @@ checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ "futures-core", "futures-util", - "pin-project", + "indexmap 1.9.3", + "pin-project 1.1.6", "pin-project-lite", + "rand 0.8.5", + "slab", "tokio", + "tokio-util", "tower-layer", "tower-service", "tracing", @@ -6615,17 +7041,29 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" +[[package]] +name = "tower-util" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1093c19826d33807c72511e68f73b4a0469a3f22c2bd5f7d5212178b4b89674" +dependencies = [ + "futures-core", + "futures-util", + "pin-project 0.4.30", + "tower-service", +] + [[package]] name = "tower_governor" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aea939ea6cfa7c4880f3e7422616624f97a567c16df67b53b11f0d03917a8e46" dependencies = [ - "axum 0.7.7", + "axum", "forwarded-header-value", "governor", "http 1.1.0", - "pin-project", + "pin-project 1.1.6", "thiserror", "tower 0.5.1", "tracing", @@ -6733,26 +7171,6 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "tungstenite" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e2ce1e47ed2994fd43b04c8f618008d4cabdd5ee34027cf14f9d918edd9c8" -dependencies = [ - "byteorder", - "bytes", - "data-encoding", - "http 1.1.0", - "httparse", - "log", - "rand 0.8.5", - "rustls 0.23.14", - "rustls-pki-types", - "sha1", - "thiserror", - "utf-8", -] - [[package]] name = "tungstenite" version = "0.24.0" @@ -6766,6 +7184,8 @@ dependencies = [ "httparse", "log", "rand 0.8.5", + "rustls 0.23.14", + "rustls-pki-types", "sha1", "thiserror", "utf-8", @@ -6810,6 +7230,12 @@ dependencies = [ "version_check", ] +[[package]] +name = "unicase" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" + [[package]] name = "unicode-bidi" version = "0.3.17" @@ -6896,9 +7322,12 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +dependencies = [ + "getrandom 0.2.15", +] [[package]] name = "valuable" @@ -6924,6 +7353,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + [[package]] name = "wait-timeout" version = "0.2.0" @@ -7043,6 +7478,33 @@ version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wasmtimer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7ed9d8b15c7fb594d72bfb4b5a276f3d2029333cd93a932f376f5937f6f80ee" +dependencies = [ + "futures", + "js-sys", + "parking_lot", + "pin-utils", + "slab", + "wasm-bindgen", +] + [[package]] name = "web-sys" version = "0.3.72" @@ -7302,15 +7764,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "winnow" -version = "0.5.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" -dependencies = [ - "memchr", -] - [[package]] name = "winnow" version = "0.6.20" diff --git a/Cargo.toml b/Cargo.toml index ed66b25d..d3a30a7d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,24 +1,31 @@ [workspace] -members = ["common", "config", "service", "tap-agent"] +members = ["common", "config", "dips", "service", "tap-agent"] resolver = "2" [profile.dev.package."*"] opt-level = 3 [workspace.dependencies] -alloy = { version = "0.2.1", features = [ +alloy = { version = "=0.5.4", features = [ "kzg", "signer-mnemonic", + "dyn-abi", + "sol-types", + "signer-local", + "eip712", + "rlp", + "signers", ], default-features = false } clap = "4.4.3" lazy_static = "1.4.0" -axum = { version = "0.7.5", default-features = false } -tokio = "1.39.2" +axum = { version = "0.7.7", default-features = false } +tokio = "1.40" prometheus = "0.13.3" anyhow = { version = "1.0.72" } thiserror = "1.0.49" async-trait = "0.1.72" eventuals = "0.6.7" +base64 = "0.22.1" reqwest = { version = "0.12", features = [ "charset", "h2", @@ -34,17 +41,20 @@ sqlx = { version = "0.8.2", features = [ "postgres", "runtime-tokio", "rust_decimal", + "uuid", ], default-features = false } +uuid = { version = "1.11.0", features = ["v7"] } tracing = { version = "0.1.40", default-features = false } bigdecimal = "0.4.3" build-info = "0.0.39" -tap_core = { git = "https://github.com/semiotic-ai/timeline-aggregation-protocol", rev = "3fe6bc2", default-features = false } +tap_core = { git = "https://github.com/semiotic-ai/timeline-aggregation-protocol", rev = "61b47b3", default-features = false } +tap_aggregator = { git = "https://github.com/semiotic-ai/timeline-aggregation-protocol", rev = "61b47b3", default-features = false } tracing-subscriber = { version = "0.3", features = [ "json", "env-filter", "ansi", ], default-features = false } -thegraph-core = { git = "https://github.com/edgeandnode/toolshed", rev = "85ee00b", features = [ +thegraph-core = { git = "https://github.com/edgeandnode/toolshed", rev = "1663534fc1738e2db1b11cb54b5bb478ee970d40", features = [ "subgraph-client", ] } thegraph-graphql-http = "0.2.0" diff --git a/README.md b/README.md index 9c49cffb..6bff30df 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,35 @@ curl -X POST \ } ``` +## Subgraph health check +```bash +curl http://localhost:7600/subgraphs/health/QmVhiE4nax9i86UBnBmQCYDzvjWuwHShYh7aspGPQhU5Sj +``` +```json +{ + "health": "healthy" +} +``` +## Unfound subgraph +```bash +curl http://localhost:7600/subgraphs/health/QmacQnSgia4iDPWHpeY6aWxesRFdb8o5DKZUx96zZqEWrB +``` +```json +{ + "error": "Deployment not found" +} +``` +## Failed Subgraph +```bash +curl http://localhost:7600/subgraphs/health/QmVGSJyvjEjkk5U9EdxyyB78NCXK3EAoFhrzm6LV7SxxAm +``` +```json +{ + "fatalError": "transaction 21e77ed08fbc9df7be81101e9b03c2616494cee7cac2f6ad4f1ee387cf799e0c: error while executing at wasm backtrace:\t 0: 0x5972 - !mappings/core/handleSwap: Mapping aborted at mappings/core.ts, line 73, column 16, with message: unexpected null in handler `handleSwap` at block #36654250 (5ab4d80c8e2cd628d5bf03abab4c302fd21d25d734e66afddff7a706b804fe13)", + "health": "failed" +} +``` + # Network queries ## Checks for auth and configuration to serve-network-subgraph diff --git a/common/CHANGELOG.md b/common/CHANGELOG.md index 16600086..2f778649 100644 --- a/common/CHANGELOG.md +++ b/common/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## [1.4.0](https://github.com/graphprotocol/indexer-rs/compare/indexer-common-v1.3.1...indexer-common-v1.4.0) (2024-11-08) + + +### Features + +* Add grace period ([#467](https://github.com/graphprotocol/indexer-rs/issues/467)) ([775f2f6](https://github.com/graphprotocol/indexer-rs/commit/775f2f62e6c7aad0489beef03a9346a943d7b406)) +* add subgraph health endpoint ([#449](https://github.com/graphprotocol/indexer-rs/issues/449)) ([bab22af](https://github.com/graphprotocol/indexer-rs/commit/bab22afabb10c58138f5d72f366cf3bf015b8251)) + +## [1.3.1](https://github.com/graphprotocol/indexer-rs/compare/indexer-common-v1.3.0...indexer-common-v1.3.1) (2024-11-06) + + +### Bug Fixes + +* use request from gateway instead of serde req ([#464](https://github.com/graphprotocol/indexer-rs/issues/464)) ([fdeda9f](https://github.com/graphprotocol/indexer-rs/commit/fdeda9fea996f96e1c0a7bef291a551f426f5591)) + ## [1.3.0](https://github.com/graphprotocol/indexer-rs/compare/indexer-common-v1.2.1...indexer-common-v1.3.0) (2024-11-01) diff --git a/common/Cargo.toml b/common/Cargo.toml index 6956c445..03e390fd 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "indexer-common" -version = "1.3.0" +version = "1.4.0" edition = "2021" [dependencies] diff --git a/common/src/indexer_service/http/health.rs b/common/src/indexer_service/http/health.rs new file mode 100644 index 00000000..d6111257 --- /dev/null +++ b/common/src/indexer_service/http/health.rs @@ -0,0 +1,97 @@ +// Copyright 2023-, Edge & Node, GraphOps, and Semiotic Labs. +// SPDX-License-Identifier: Apache-2.0 + +use axum::{ + extract::Path, + response::{IntoResponse, Response as AxumResponse}, + Extension, Json, +}; +use graphql_client::GraphQLQuery; +use indexer_config::GraphNodeConfig; +use reqwest::StatusCode; +use serde_json::json; +use thiserror::Error; + +#[derive(GraphQLQuery)] +#[graphql( + schema_path = "../graphql/indexing_status.schema.graphql", + query_path = "../graphql/subgraph_health.query.graphql", + response_derives = "Debug", + variables_derives = "Clone" +)] +pub struct HealthQuery; + +#[derive(Debug, Error)] +pub enum CheckHealthError { + #[error("Failed to send request")] + RequestFailed, + #[error("Failed to decode response")] + BadResponse, + #[error("Deployment not found")] + DeploymentNotFound, + #[error("Invalid health status found")] + InvalidHealthStatus, +} + +impl IntoResponse for CheckHealthError { + fn into_response(self) -> AxumResponse { + let status = match &self { + CheckHealthError::DeploymentNotFound => StatusCode::NOT_FOUND, + CheckHealthError::InvalidHealthStatus | CheckHealthError::BadResponse => { + StatusCode::INTERNAL_SERVER_ERROR + } + CheckHealthError::RequestFailed => StatusCode::BAD_GATEWAY, + }; + let body = serde_json::json!({ + "error": self.to_string(), + }); + (status, Json(body)).into_response() + } +} + +pub async fn health( + Path(deployment_id): Path, + Extension(graph_node): Extension, +) -> Result { + let req_body = HealthQuery::build_query(health_query::Variables { + ids: vec![deployment_id], + }); + + let client = reqwest::Client::new(); + let response = client + .post(graph_node.status_url) + .json(&req_body) + .send() + .await + .map_err(|_| CheckHealthError::RequestFailed)?; + + let graphql_response: graphql_client::Response = response + .json() + .await + .map_err(|_| CheckHealthError::BadResponse)?; + + let data = match (graphql_response.data, graphql_response.errors) { + (Some(data), None) => data, + _ => return Err(CheckHealthError::BadResponse), + }; + + let Some(status) = data.indexing_statuses.first() else { + return Err(CheckHealthError::DeploymentNotFound); + }; + let health_response = match status.health { + health_query::Health::healthy => json!({ "health": status.health }), + health_query::Health::unhealthy => { + let errors: Vec<&String> = status + .non_fatal_errors + .iter() + .map(|msg| &msg.message) + .collect(); + json!({ "health": status.health, "nonFatalErrors": errors }) + } + health_query::Health::failed => { + json!({ "health": status.health, "fatalError": status.fatal_error.as_ref().map_or("null", |msg| &msg.message) }) + } + health_query::Health::Other(_) => return Err(CheckHealthError::InvalidHealthStatus), + }; + Ok(Json(health_response)) +} diff --git a/common/src/indexer_service/http/indexer_service.rs b/common/src/indexer_service/http/indexer_service.rs index d5c3b454..e9e64853 100644 --- a/common/src/indexer_service/http/indexer_service.rs +++ b/common/src/indexer_service/http/indexer_service.rs @@ -33,8 +33,10 @@ use tower_http::{cors, cors::CorsLayer, normalize_path::NormalizePath, trace::Tr use tracing::error; use tracing::{info, info_span}; +use super::request_handler::request_handler; use crate::escrow_accounts::EscrowAccounts; use crate::escrow_accounts::EscrowAccountsError; +use crate::indexer_service::http::health::health; use crate::{ address::public_key, indexer_service::http::static_subgraph::static_subgraph_request_handler, @@ -44,8 +46,6 @@ use crate::{ }, tap::IndexerTapContext, }; - -use super::request_handler::request_handler; use indexer_config::Config; pub trait IndexerServiceResponse { @@ -72,7 +72,7 @@ pub trait IndexerServiceImpl { &self, manifest_id: DeploymentId, request: Request, - ) -> Result<(Request, Self::Response), Self::Error>; + ) -> Result; } #[derive(Debug, Error)] @@ -386,7 +386,7 @@ impl IndexerService { .route("/", get("Service is up and running")) .route("/version", get(Json(options.release))) .route("/info", get(operator_address)) - .layer(misc_rate_limiter); + .layer(misc_rate_limiter.clone()); // Rate limits by allowing bursts of 50 requests and requiring 20ms of // time between consecutive requests after that, effectively rate @@ -401,6 +401,12 @@ impl IndexerService { ), }; + // Check subgraph Health + misc_routes = misc_routes + .route("/subgraph/health/:deployment_id", get(health)) + .route_layer(Extension(options.config.graph_node.clone())) + .layer(misc_rate_limiter); + if options.config.service.serve_network_subgraph { info!("Serving network subgraph at /network"); diff --git a/common/src/indexer_service/http/mod.rs b/common/src/indexer_service/http/mod.rs index 2c1da686..04baef2e 100644 --- a/common/src/indexer_service/http/mod.rs +++ b/common/src/indexer_service/http/mod.rs @@ -1,6 +1,7 @@ // Copyright 2023-, Edge & Node, GraphOps, and Semiotic Labs. // SPDX-License-Identifier: Apache-2.0 +mod health; mod indexer_service; mod request_handler; mod static_subgraph; diff --git a/common/src/indexer_service/http/request_handler.rs b/common/src/indexer_service/http/request_handler.rs index 62ae21ce..7a243c3f 100644 --- a/common/src/indexer_service/http/request_handler.rs +++ b/common/src/indexer_service/http/request_handler.rs @@ -4,7 +4,6 @@ use std::sync::Arc; use axum::{ - body::Bytes, extract::{Path, State}, http::HeaderMap, response::IntoResponse, @@ -54,7 +53,7 @@ pub async fn request_handler( typed_header: TypedHeader, state: State>>, headers: HeaderMap, - body: Bytes, + body: String, ) -> Result> where I: IndexerServiceImpl + Sync + Send + 'static, @@ -73,21 +72,15 @@ async fn _request_handler( TypedHeader(receipt): TypedHeader, State(state): State>>, headers: HeaderMap, - body: Bytes, + req: String, ) -> Result> where I: IndexerServiceImpl + Sync + Send + 'static, { trace!("Handling request for deployment `{manifest_id}`"); - #[derive(Debug, serde::Deserialize, serde::Serialize)] - pub struct QueryBody { - pub query: String, - pub variables: Option>, - } - let request: QueryBody = - serde_json::from_slice(&body).map_err(|e| IndexerServiceError::InvalidRequest(e.into()))?; + serde_json::from_str(&req).map_err(|e| IndexerServiceError::InvalidRequest(e.into()))?; let Some(receipt) = receipt.into_signed_receipt() else { // Serve free query, NO METRICS @@ -112,7 +105,6 @@ where .process_request(manifest_id, request) .await .map_err(IndexerServiceError::ProcessingError)? - .1 .finalize(AttestationOutput::Attestable); return Ok((StatusCode::OK, response)); }; @@ -179,15 +171,12 @@ where .cloned() .ok_or_else(|| (IndexerServiceError::NoSignerForAllocation(allocation_id)))?; - let (request, response) = state + let response = state .service_impl .process_request(manifest_id, request) .await .map_err(IndexerServiceError::ProcessingError)?; - let req = serde_json::to_string(&request) - .map_err(|_| IndexerServiceError::FailedToSignAttestation)?; - let res = response .as_str() .map_err(|_| IndexerServiceError::FailedToSignAttestation)?; @@ -202,3 +191,9 @@ where Ok((StatusCode::OK, response)) } + +#[derive(Debug, serde::Deserialize, serde::Serialize)] +pub struct QueryBody { + pub query: String, + pub variables: Option>, +} diff --git a/common/src/tap.rs b/common/src/tap.rs index ab6d990f..14850960 100644 --- a/common/src/tap.rs +++ b/common/src/tap.rs @@ -26,6 +26,8 @@ mod receipt_store; pub use checks::value_check::AgoraQuery; +const GRACE_PERIOD: u64 = 60; + #[derive(Clone)] pub struct IndexerTapContext { domain_separator: Arc, @@ -57,7 +59,7 @@ impl IndexerTapContext { Arc::new(TimestampCheck::new(timestamp_error_tolerance)), Arc::new(DenyListCheck::new(pgpool.clone(), escrow_accounts, domain_separator).await), Arc::new(ReceiptMaxValueCheck::new(receipt_max_value)), - Arc::new(MinimumValue::new(pgpool).await), + Arc::new(MinimumValue::new(pgpool, Duration::from_secs(GRACE_PERIOD)).await), ] } diff --git a/common/src/tap/checks/value_check.rs b/common/src/tap/checks/value_check.rs index daf9237c..e55bd925 100644 --- a/common/src/tap/checks/value_check.rs +++ b/common/src/tap/checks/value_check.rs @@ -8,10 +8,12 @@ use sqlx::{ postgres::{PgListener, PgNotification}, PgPool, }; +use std::time::Duration; use std::{ collections::HashMap, str::FromStr, sync::{Arc, RwLock}, + time::Instant, }; use thegraph_core::DeploymentId; use tracing::error; @@ -39,6 +41,7 @@ pub struct AgoraQuery { type CostModelMap = Arc>>; type GlobalModel = Arc>>; +type GracePeriod = Arc>; /// Represents the check for minimum for a receipt /// @@ -48,6 +51,8 @@ pub struct MinimumValue { cost_model_map: CostModelMap, global_model: GlobalModel, watcher_cancel_token: tokio_util::sync::CancellationToken, + updated_at: GracePeriod, + grace_period: Duration, } struct CostModelWatcher { @@ -55,6 +60,7 @@ struct CostModelWatcher { cost_models: CostModelMap, global_model: GlobalModel, + updated_at: GracePeriod, } impl CostModelWatcher { @@ -64,11 +70,13 @@ impl CostModelWatcher { cost_models: CostModelMap, global_model: GlobalModel, cancel_token: tokio_util::sync::CancellationToken, + grace_period: GracePeriod, ) { let cost_model_watcher = CostModelWatcher { pgpool, global_model, cost_models, + updated_at: grace_period, }; loop { @@ -123,6 +131,8 @@ impl CostModelWatcher { } }, }; + + *self.updated_at.write().unwrap() = Instant::now(); } fn handle_delete(&self, deployment: String) { @@ -142,6 +152,7 @@ impl CostModelWatcher { } }, }; + *self.updated_at.write().unwrap() = Instant::now(); } async fn handle_unexpected_notification(&self, payload: &str) { @@ -157,7 +168,9 @@ impl CostModelWatcher { self.global_model.clone(), ) .await - .expect("should be able to reload cost models") + .expect("should be able to reload cost models"); + + *self.updated_at.write().unwrap() = Instant::now(); } } @@ -170,9 +183,10 @@ impl Drop for MinimumValue { } impl MinimumValue { - pub async fn new(pgpool: PgPool) -> Self { + pub async fn new(pgpool: PgPool, grace_period: Duration) -> Self { let cost_model_map: CostModelMap = Default::default(); let global_model: GlobalModel = Default::default(); + let updated_at: GracePeriod = Arc::new(RwLock::new(Instant::now())); Self::value_check_reload(&pgpool, cost_model_map.clone(), global_model.clone()) .await .expect("should be able to reload cost models"); @@ -193,15 +207,22 @@ impl MinimumValue { cost_model_map.clone(), global_model.clone(), watcher_cancel_token.clone(), + updated_at.clone(), )); - Self { global_model, cost_model_map, watcher_cancel_token, + updated_at, + grace_period, } } + fn inside_grace_period(&self) -> bool { + let time_elapsed = Instant::now().duration_since(*self.updated_at.read().unwrap()); + time_elapsed < self.grace_period + } + fn expected_value(&self, agora_query: &AgoraQuery) -> anyhow::Result { // get agora model for the deployment_id let model = self.cost_model_map.read().unwrap(); @@ -271,14 +292,17 @@ impl Check for MinimumValue { let agora_query = ctx .get() .ok_or(CheckError::Failed(anyhow!("Could not find agora query")))?; + // get value + let value = receipt.signed_receipt().message.value; + + if self.inside_grace_period() && value >= MINIMAL_VALUE { + return Ok(()); + } let expected_value = self .expected_value(agora_query) .map_err(CheckError::Failed)?; - // get value - let value = receipt.signed_receipt().message.value; - let should_accept = value >= expected_value; tracing::trace!( @@ -339,7 +363,7 @@ mod tests { #[sqlx::test(migrations = "../migrations")] async fn initialize_check(pgpool: PgPool) { - let check = MinimumValue::new(pgpool).await; + let check = MinimumValue::new(pgpool, Duration::from_secs(0)).await; assert_eq!(check.cost_model_map.read().unwrap().len(), 0); } @@ -350,7 +374,7 @@ mod tests { add_cost_models(&pgpool, to_db_models(test_models.clone())).await; - let check = MinimumValue::new(pgpool).await; + let check = MinimumValue::new(pgpool, Duration::from_secs(0)).await; assert_eq!(check.cost_model_map.read().unwrap().len(), 2); // no global model @@ -359,7 +383,7 @@ mod tests { #[sqlx::test(migrations = "../migrations")] async fn should_watch_model_insert(pgpool: PgPool) { - let check = MinimumValue::new(pgpool.clone()).await; + let check = MinimumValue::new(pgpool.clone(), Duration::from_secs(0)).await; assert_eq!(check.cost_model_map.read().unwrap().len(), 0); // insert 2 cost models for different deployment_id @@ -379,7 +403,7 @@ mod tests { let test_models = crate::cost_model::test::test_data(); add_cost_models(&pgpool, to_db_models(test_models.clone())).await; - let check = MinimumValue::new(pgpool.clone()).await; + let check = MinimumValue::new(pgpool.clone(), Duration::from_secs(0)).await; assert_eq!(check.cost_model_map.read().unwrap().len(), 2); // remove @@ -398,13 +422,13 @@ mod tests { let global_model = global_cost_model(); add_cost_models(&pgpool, vec![global_model.clone()]).await; - let check = MinimumValue::new(pgpool.clone()).await; + let check = MinimumValue::new(pgpool.clone(), Duration::from_secs(0)).await; assert!(check.global_model.read().unwrap().is_some()); } #[sqlx::test(migrations = "../migrations")] async fn should_watch_global_model(pgpool: PgPool) { - let check = MinimumValue::new(pgpool.clone()).await; + let check = MinimumValue::new(pgpool.clone(), Duration::from_secs(0)).await; let global_model = global_cost_model(); add_cost_models(&pgpool, vec![global_model.clone()]).await; @@ -418,7 +442,7 @@ mod tests { let global_model = global_cost_model(); add_cost_models(&pgpool, vec![global_model.clone()]).await; - let check = MinimumValue::new(pgpool.clone()).await; + let check = MinimumValue::new(pgpool.clone(), Duration::from_secs(0)).await; assert!(check.global_model.read().unwrap().is_some()); sqlx::query!(r#"DELETE FROM "CostModels""#) @@ -440,7 +464,7 @@ mod tests { add_cost_models(&pgpool, to_db_models(test_models.clone())).await; - let check = MinimumValue::new(pgpool).await; + let check = MinimumValue::new(pgpool, Duration::from_secs(1)).await; let deployment_id = test_models[0].deployment; let mut ctx = Context::new(); @@ -477,6 +501,14 @@ mod tests { let signed_receipt = create_signed_receipt(ALLOCATION_ID, u64::MAX, u64::MAX, minimal_value - 1).await; let receipt = ReceiptWithState::new(signed_receipt); + + assert!( + check.check(&ctx, &receipt).await.is_ok(), + "Should accept since its inside grace period " + ); + + sleep(Duration::from_millis(1010)).await; + assert!( check.check(&ctx, &receipt).await.is_err(), "Should require minimal value" @@ -508,7 +540,7 @@ mod tests { add_cost_models(&pgpool, vec![global_model.clone()]).await; add_cost_models(&pgpool, to_db_models(test_models.clone())).await; - let check = MinimumValue::new(pgpool).await; + let check = MinimumValue::new(pgpool, Duration::from_secs(0)).await; let deployment_id = test_models[0].deployment; let mut ctx = Context::new(); diff --git a/config/CHANGELOG.md b/config/CHANGELOG.md index a7191dd5..3fba8a53 100644 --- a/config/CHANGELOG.md +++ b/config/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [1.2.1](https://github.com/graphprotocol/indexer-rs/compare/indexer-config-v1.2.0...indexer-config-v1.2.1) (2024-11-08) + + +### Bug Fixes + +* ignore empty environment variables strings ([#473](https://github.com/graphprotocol/indexer-rs/issues/473)) ([1bc3c4e](https://github.com/graphprotocol/indexer-rs/commit/1bc3c4e96584ef8977a133e03530cdcb801d2270)) + ## [1.2.0](https://github.com/graphprotocol/indexer-rs/compare/indexer-config-v1.1.0...indexer-config-v1.2.0) (2024-10-09) diff --git a/config/Cargo.toml b/config/Cargo.toml index 3c602648..d66101b4 100644 --- a/config/Cargo.toml +++ b/config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "indexer-config" -version = "1.2.0" +version = "1.2.1" edition = "2021" [dependencies] diff --git a/config/maximal-config-example.toml b/config/maximal-config-example.toml index ed855f0e..0efa9d57 100644 --- a/config/maximal-config-example.toml +++ b/config/maximal-config-example.toml @@ -145,3 +145,6 @@ max_receipts_per_request = 10000 # Key-Value of all senders and their aggregator endpoints 0xdeadbeefcafebabedeadbeefcafebabedeadbeef = "https://example.com/aggregate-receipts" 0x0123456789abcdef0123456789abcdef01234567 = "https://other.example.com/aggregate-receipts" + +[dips] +allowed_payers = ["0x3333333333333333333333333333333333333333"] diff --git a/config/minimal-config-example.toml b/config/minimal-config-example.toml index 3cc0bda4..61cd9460 100644 --- a/config/minimal-config-example.toml +++ b/config/minimal-config-example.toml @@ -63,3 +63,4 @@ receipts_verifier_address = "0x2222222222222222222222222222222222222222" # Key-Value of all senders and their aggregator endpoints 0xdeadbeefcafebabedeadbeefcafebabedeadbeef = "https://example.com/aggregate-receipts" 0x0123456789abcdef0123456789abcdef01234567 = "https://other.example.com/aggregate-receipts" + diff --git a/config/src/config.rs b/config/src/config.rs index 1462a2bb..d34c366d 100644 --- a/config/src/config.rs +++ b/config/src/config.rs @@ -41,6 +41,7 @@ pub struct Config { pub blockchain: BlockchainConfig, pub service: ServiceConfig, pub tap: TapConfig, + pub dips: Option, } // Newtype wrapping Config to be able use serde_ignored with Figment @@ -89,9 +90,10 @@ impl Config { config_content = Self::substitute_env_vars(config_content)?; figment_config = figment_config.merge(Toml::string(&config_content)); } + let config: ConfigWrapper = figment_config - .merge(Env::prefixed(prefix.get_prefix()).split("__")) - .merge(Env::prefixed(SHARED_PREFIX).split("__")) + .merge(Self::from_env_ignore_empty(prefix.get_prefix())) + .merge(Self::from_env_ignore_empty(SHARED_PREFIX)) .extract() .map_err(|e| e.to_string())?; @@ -99,6 +101,25 @@ impl Config { Ok(config.0) } + fn from_env_ignore_empty(prefix: &str) -> Env { + let prefixed_env = Env::prefixed(prefix).split("__"); + let ignore_prefixed: Vec<_> = prefixed_env + .iter() + .filter_map(|(key, value)| { + if value.is_empty() { + Some(key.into_string()) + } else { + None + } + }) + .collect(); + let ref_ignore = ignore_prefixed + .iter() + .map(|k| k.as_str()) + .collect::>(); + prefixed_env.ignore(&ref_ignore) + } + fn substitute_env_vars(content: String) -> Result { let reg = Regex::new(r"\$\{([A-Z_][A-Z0-9_]*)\}").map_err(|e| e.to_string())?; let mut missing_vars = Vec::new(); @@ -362,6 +383,13 @@ pub struct TapConfig { pub sender_aggregator_endpoints: HashMap, } +#[derive(Debug, Deserialize, Clone)] +#[cfg_attr(test, derive(PartialEq))] +pub struct DipsConfig { + pub allowed_payers: Vec
, + pub cancellation_time_tolerance: Option, +} + impl TapConfig { pub fn get_trigger_value(&self) -> u128 { let grt_wei = self.max_amount_willing_to_lose_grt.get_value(); @@ -391,13 +419,15 @@ pub struct RavRequestConfig { #[cfg(test)] mod tests { + use alloy::primitives::FixedBytes; + use figment::value::Uncased; use sealed_test::prelude::*; - use std::{env, fs, path::PathBuf}; + use std::{env, fs, path::PathBuf, str::FromStr}; use tracing_test::traced_test; use crate::{Config, ConfigPrefix}; - use super::DatabaseConfig; + use super::{DatabaseConfig, SHARED_PREFIX}; #[test] fn test_minimal_config() { @@ -411,11 +441,18 @@ mod tests { #[test] fn test_maximal_config() { // Generate full config by deserializing the minimal config and let the code fill in the defaults. - let max_config = Config::parse( + let mut max_config = Config::parse( ConfigPrefix::Service, Some(PathBuf::from("minimal-config-example.toml")).as_ref(), ) .unwrap(); + max_config.dips = Some(crate::DipsConfig { + allowed_payers: vec![thegraph_core::Address( + FixedBytes::<20>::from_str("0x3333333333333333333333333333333333333333").unwrap(), + )], + cancellation_time_tolerance: None, + }); + let max_config_file: Config = toml::from_str( fs::read_to_string("maximal-config-example.toml") .unwrap() @@ -512,6 +549,23 @@ mod tests { ); } + #[test] + fn test_ignore_empty_values() { + env::set_var("INDEXER_TEST1", "123"); + env::set_var("INDEXER_TEST2", ""); + env::set_var("INDEXER_TEST3__TEST1", "123"); + env::set_var("INDEXER_TEST3__TEST2", ""); + + let env = Config::from_env_ignore_empty(SHARED_PREFIX); + + let values: Vec<_> = env.iter().collect(); + + assert_eq!(values.len(), 2); + + assert_eq!(values[0], (Uncased::new("test1"), "123".to_string())); + assert_eq!(values[1], (Uncased::new("test3.test1"), "123".to_string())); + } + // Test to check substitute_env_vars function is substituting env variables // indexers can use ${ENV_VAR_NAME} to point to the required env variable #[test] diff --git a/dips/Cargo.toml b/dips/Cargo.toml new file mode 100644 index 00000000..2e0ff600 --- /dev/null +++ b/dips/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "indexer-dips" +version = "0.1.0" +edition = "2021" + +[dependencies] +alloy.workspace = true +thiserror.workspace = true +anyhow.workspace = true +alloy-sol-types = "=0.8.10" +alloy-rlp = "0.3.9" +thegraph-core.workspace = true diff --git a/dips/src/lib.rs b/dips/src/lib.rs new file mode 100644 index 00000000..c073c439 --- /dev/null +++ b/dips/src/lib.rs @@ -0,0 +1,388 @@ +// Copyright 2023-, Edge & Node, GraphOps, and Semiotic Labs. +// SPDX-License-Identifier: Apache-2.0 + +use std::str::FromStr; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; + +pub use alloy; +pub use alloy_rlp; + +use alloy::core::primitives::Address; +use alloy::rlp::{RlpDecodable, RlpEncodable}; +use alloy::signers::{Signature, SignerSync}; +use alloy_rlp::Encodable; +use thegraph_core::alloy_sol_types::{sol, Eip712Domain, SolStruct}; +use thiserror::Error; + +sol! { + // EIP712 encoded bytes, ABI - ethers + #[derive(Debug, RlpEncodable, RlpDecodable, PartialEq)] + struct SignedIndexingAgreementVoucher { + IndexingAgreementVoucher voucher; + bytes signature; + } + + #[derive(Debug, RlpEncodable, RlpDecodable, PartialEq)] + struct IndexingAgreementVoucher { + // should coincide with signer + address payer; + // should coincide with indexer + address payee; + // data service that will initiate payment collection + address service; + // initial indexing amount max + uint256 maxInitialAmount; + uint256 maxOngoingAmountPerEpoch; + // time to accept the agreement, intended to be on the order + // of hours or mins + uint64 deadline; + uint32 maxEpochsPerCollection; + uint32 minEpochsPerCollection; + // after which the agreement is complete + uint32 durationEpochs; + bytes metadata; + } + + // the vouchers are generic to each data service, in the case of subgraphs this is an ABI-encoded SubgraphIndexingVoucherMetadata + #[derive(Debug, RlpEncodable, RlpDecodable, PartialEq)] + struct SubgraphIndexingVoucherMetadata { + uint256 pricePerBlock; // wei GRT + bytes32 protocolNetwork; // eip199:1 format + // differentiate based on indexed chain + bytes32 chainId; // eip199:1 format + string deployment_ipfs_hash; + } + + #[derive(Debug, RlpEncodable, RlpDecodable, PartialEq)] + struct SignedCancellationRequest { + CancellationRequest request; + bytes signature; + } + + #[derive(Debug, RlpEncodable, RlpDecodable, PartialEq)] + struct CancellationRequest { + // should coincide with signer. + address payer; + // should coincide with indexer. + address payee; + // data service that will initiate payment collection. + address service; + // signer of the cancellation, can be signed by either party. + address cancellled_by; + // should only be usable within a limited period of time. + uint64 timestamp; + bytes metadata; + } +} + +#[derive(Error, Debug, PartialEq)] +pub enum AgreementVoucherValidationError { + #[error("signature is not valid, error: {0}")] + InvalidSignature(String), + #[error("payer {0} not authorised")] + PayerNotAuthorised(Address), + #[error("voucher payee {actual} does not match the expected address {expected}")] + UnexpectedPayee { expected: Address, actual: Address }, +} + +impl IndexingAgreementVoucher { + pub fn sign( + &self, + domain: &Eip712Domain, + signer: S, + ) -> anyhow::Result { + let voucher = SignedIndexingAgreementVoucher { + voucher: self.clone(), + signature: signer.sign_typed_data_sync(self, domain)?.as_bytes().into(), + }; + + Ok(voucher) + } +} + +impl SignedIndexingAgreementVoucher { + // TODO: Validate all values + pub fn validate( + &self, + domain: &Eip712Domain, + expected_payee: &Address, + allowed_payers: impl AsRef<[Address]>, + ) -> Result<(), AgreementVoucherValidationError> { + let sig = Signature::from_str(&self.signature.to_string()) + .map_err(|err| AgreementVoucherValidationError::InvalidSignature(err.to_string()))?; + + let payer = sig + .recover_address_from_prehash(&self.voucher.eip712_signing_hash(domain)) + .map_err(|err| AgreementVoucherValidationError::InvalidSignature(err.to_string()))?; + + if allowed_payers.as_ref().is_empty() + || !allowed_payers.as_ref().iter().any(|addr| addr.eq(&payer)) + { + return Err(AgreementVoucherValidationError::PayerNotAuthorised(payer)); + } + + if !self.voucher.payee.eq(expected_payee) { + return Err(AgreementVoucherValidationError::UnexpectedPayee { + expected: *expected_payee, + actual: self.voucher.payee, + }); + } + + Ok(()) + } + + pub fn encode_vec(&self) -> Vec { + let mut out = vec![]; + self.encode(&mut out); + + out + } +} + +#[derive(Error, Debug, PartialEq)] +pub enum CancellationRequestValidationError { + #[error("signature is not valid, error: {0}")] + InvalidSignature(String), + #[error("cancelled_by is expected to match the signer")] + UnexpectedSigner, + #[error("signer {0} not authorised")] + SignerNotAuthorised(Address), + #[error("cancellation request has expired")] + ExpiredRequest, +} + +impl CancellationRequest { + pub fn sign( + &self, + domain: &Eip712Domain, + signer: S, + ) -> anyhow::Result { + let voucher = SignedCancellationRequest { + request: self.clone(), + signature: signer.sign_typed_data_sync(self, domain)?.as_bytes().into(), + }; + + Ok(voucher) + } +} + +impl SignedCancellationRequest { + // TODO: Validate all values + pub fn validate( + &self, + domain: &Eip712Domain, + time_tolerance: Duration, + ) -> Result<(), CancellationRequestValidationError> { + let sig = Signature::from_str(&self.signature.to_string()) + .map_err(|err| CancellationRequestValidationError::InvalidSignature(err.to_string()))?; + + let signer = sig + .recover_address_from_prehash(&self.request.eip712_signing_hash(domain)) + .map_err(|err| CancellationRequestValidationError::InvalidSignature(err.to_string()))?; + + if signer.ne(&self.request.cancellled_by) { + return Err(CancellationRequestValidationError::UnexpectedSigner); + } + + if signer.ne(&self.request.payer) && signer.ne(&self.request.payee) { + return Err(CancellationRequestValidationError::SignerNotAuthorised( + signer, + )); + } + + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map_err(|_| CancellationRequestValidationError::ExpiredRequest)? + .as_secs(); + if now - self.request.timestamp >= time_tolerance.as_secs() { + return Err(CancellationRequestValidationError::ExpiredRequest); + } + + Ok(()) + } + pub fn encode_vec(&self) -> Vec { + let mut out = vec![]; + self.encode(&mut out); + + out + } +} + +#[cfg(test)] +mod test { + use std::time::{Duration, SystemTime, UNIX_EPOCH}; + + use alloy::primitives::{Address, FixedBytes, U256}; + use alloy::signers::local::PrivateKeySigner; + use alloy::sol_types::SolStruct; + use thegraph_core::attestation::eip712_domain; + + use crate::{ + AgreementVoucherValidationError, CancellationRequest, CancellationRequestValidationError, + IndexingAgreementVoucher, SubgraphIndexingVoucherMetadata, + }; + + #[test] + fn voucher_signature_verification() { + let deployment_id = "Qmbg1qF4YgHjiVfsVt6a13ddrVcRtWyJQfD4LA3CwHM29f".to_string(); + let payee = PrivateKeySigner::random(); + let payee_addr = payee.address(); + let payer = PrivateKeySigner::random(); + let payer_addr = payer.address(); + + let metadata = SubgraphIndexingVoucherMetadata { + pricePerBlock: U256::from(10000_u64), + protocolNetwork: FixedBytes::left_padding_from("arbitrum-one".as_bytes()), + chainId: FixedBytes::left_padding_from("mainnet".as_bytes()), + deployment_ipfs_hash: deployment_id, + }; + + let voucher = IndexingAgreementVoucher { + payer: payer_addr, + payee: payee.address(), + service: Address(FixedBytes::ZERO), + maxInitialAmount: U256::from(10000_u64), + maxOngoingAmountPerEpoch: U256::from(10000_u64), + deadline: 1000, + maxEpochsPerCollection: 1000, + minEpochsPerCollection: 1000, + durationEpochs: 1000, + metadata: metadata.eip712_hash_struct().to_owned().into(), + }; + + let domain = eip712_domain(0, Address::ZERO); + let signed = voucher.sign(&domain, payer).unwrap(); + assert_eq!( + signed.validate(&domain, &payee_addr, vec![]).unwrap_err(), + AgreementVoucherValidationError::PayerNotAuthorised(voucher.payer) + ); + assert!(signed + .validate(&domain, &payee_addr, vec![payer_addr]) + .is_ok()); + } + + #[test] + fn check_voucher_modified() { + let deployment_id = "Qmbg1qF4YgHjiVfsVt6a13ddrVcRtWyJQfD4LA3CwHM29f".to_string(); + let payee = PrivateKeySigner::random(); + let payee_addr = payee.address(); + let payer = PrivateKeySigner::random(); + let payer_addr = payer.address(); + + let metadata = SubgraphIndexingVoucherMetadata { + pricePerBlock: U256::from(10000_u64), + protocolNetwork: FixedBytes::left_padding_from("arbitrum-one".as_bytes()), + chainId: FixedBytes::left_padding_from("mainnet".as_bytes()), + deployment_ipfs_hash: deployment_id, + }; + + let voucher = IndexingAgreementVoucher { + payer: payer_addr, + payee: payee_addr, + service: Address(FixedBytes::ZERO), + maxInitialAmount: U256::from(10000_u64), + maxOngoingAmountPerEpoch: U256::from(10000_u64), + deadline: 1000, + maxEpochsPerCollection: 1000, + minEpochsPerCollection: 1000, + durationEpochs: 1000, + metadata: metadata.eip712_hash_struct().to_owned().into(), + }; + let domain = eip712_domain(0, Address::ZERO); + + let mut signed = voucher.sign(&domain, payer).unwrap(); + signed.voucher.service = Address::repeat_byte(9); + + assert!(matches!( + signed + .validate(&domain, &payee_addr, vec![payer_addr]) + .unwrap_err(), + AgreementVoucherValidationError::PayerNotAuthorised(_) + )); + } + + #[test] + fn cancel_voucher_validation() { + let deployment_id = "Qmbg1qF4YgHjiVfsVt6a13ddrVcRtWyJQfD4LA3CwHM29f".to_string(); + let payee = PrivateKeySigner::random(); + let payee_addr = payee.address(); + let payer = PrivateKeySigner::random(); + let payer_addr = payer.address(); + let other_signer = PrivateKeySigner::random(); + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs(); + + let metadata = SubgraphIndexingVoucherMetadata { + pricePerBlock: U256::from(10000_u64), + protocolNetwork: FixedBytes::left_padding_from("arbitrum-one".as_bytes()), + chainId: FixedBytes::left_padding_from("mainnet".as_bytes()), + deployment_ipfs_hash: deployment_id, + }; + + struct Case<'a> { + name: &'a str, + signer: PrivateKeySigner, + timestamp: u64, + error: Option, + } + + let cases: Vec = vec![ + Case { + name: "happy path payer", + signer: payee.clone(), + timestamp: now, + error: None, + }, + Case { + name: "happy path payee", + signer: payer, + timestamp: now, + error: None, + }, + Case { + name: "invalid signer", + signer: other_signer.clone(), + timestamp: now, + error: Some(CancellationRequestValidationError::SignerNotAuthorised( + other_signer.address(), + )), + }, + Case { + name: "expired timestamp", + signer: payee, + timestamp: 100, + error: Some(CancellationRequestValidationError::SignerNotAuthorised( + other_signer.address(), + )), + }, + ]; + + for Case { + name, + timestamp, + signer, + error, + } in cases.into_iter() + { + let voucher = CancellationRequest { + payer: payer_addr, + payee: payee_addr, + service: Address(FixedBytes::ZERO), + metadata: metadata.eip712_hash_struct().to_owned().into(), + cancellled_by: signer.address(), + timestamp, + }; + let domain = eip712_domain(0, Address::ZERO); + + let signed = voucher.sign(&domain, signer).unwrap(); + + let res = signed.validate(&domain, Duration::from_secs(10)); + match error { + Some(_err) => assert!(matches!(res.unwrap_err(), _err), "case: {}", name), + None => assert!(res.is_ok(), "case: {}, err: {}", name, res.unwrap_err()), + } + } + } +} diff --git a/graphql/indexing_status.schema.graphql b/graphql/indexing_status.schema.graphql new file mode 100644 index 00000000..e1156d5a --- /dev/null +++ b/graphql/indexing_status.schema.graphql @@ -0,0 +1,182 @@ +schema { + query: Query +} + +type ApiVersion { + """ + Version number in SemVer format + + """ + version: String! +} + +scalar BigInt + +type Block { + hash: Bytes! + number: BigInt! +} + +input BlockInput { + hash: Bytes! + number: BigInt! +} + +scalar Bytes + +type CachedEthereumCall { + idHash: Bytes! + block: Block! + contractAddress: Bytes! + returnValue: Bytes! +} + +interface ChainIndexingStatus { + network: String! + chainHeadBlock: Block + earliestBlock: EarliestBlock + latestBlock: Block + lastHealthyBlock: Block +} + +scalar Date + +type EarliestBlock { + hash: Bytes! + number: BigInt! +} + +type EntityChanges { + updates: [EntityTypeUpdates!]! + deletions: [EntityTypeDeletions!]! +} + +type EntityTypeDeletions { + type: String! + entities: [ID!]! +} + +type EntityTypeUpdates { + type: String! + entities: [JSONObject!]! +} + +type EthereumIndexingStatus implements ChainIndexingStatus { + network: String! + chainHeadBlock: Block + earliestBlock: EarliestBlock + latestBlock: Block + lastHealthyBlock: Block +} + +enum Feature { + nonFatalErrors + grafting + fullTextSearch + ipfsOnEthereumContracts + aggregations + declaredEthCalls + immutableEntities + bytesAsIds +} + +enum Health { + """Subgraph syncing normally""" + healthy + """Subgraph syncing but with errors""" + unhealthy + """Subgraph halted due to errors""" + failed +} + +scalar JSONObject + +type PartialBlock { + hash: Bytes + number: BigInt! +} + +input ProofOfIndexingRequest { + deployment: String! + block: BlockInput! +} + +type ProofOfIndexingResult { + deployment: String! + block: Block! + """ + There may not be a proof of indexing available for the deployment and block + """ + proofOfIndexing: Bytes +} + +input PublicProofOfIndexingRequest { + deployment: String! + blockNumber: BigInt! +} + +type PublicProofOfIndexingResult { + deployment: String! + block: PartialBlock! + proofOfIndexing: Bytes! +} + +type Query { + indexingStatusForCurrentVersion(subgraphName: String!): SubgraphIndexingStatus + indexingStatusForPendingVersion(subgraphName: String!): SubgraphIndexingStatus + indexingStatusesForSubgraphName(subgraphName: String!): [SubgraphIndexingStatus!]! + indexingStatuses(subgraphs: [String!]): [SubgraphIndexingStatus!]! + proofOfIndexing(subgraph: String!, blockNumber: Int!, blockHash: Bytes!, indexer: Bytes): Bytes + """ + Proofs of indexing for several deployments and blocks that can be shared and + compared in public without revealing the _actual_ proof of indexing that every + indexer has in their database + + """ + publicProofsOfIndexing(requests: [PublicProofOfIndexingRequest!]!): [PublicProofOfIndexingResult!]! + subgraphFeatures(subgraphId: String!): SubgraphFeatures! + entityChangesInBlock(subgraphId: String!, blockNumber: Int!): EntityChanges! + blockData(network: String!, blockHash: Bytes!): JSONObject + blockHashFromNumber(network: String!, blockNumber: Int!): Bytes + version: Version! + cachedEthereumCalls(network: String!, blockHash: Bytes!): [CachedEthereumCall!] + apiVersions(subgraphId: String!): [ApiVersion!]! +} + +type SubgraphError { + message: String! + block: Block + handler: String + deterministic: Boolean! +} + +type SubgraphFeatures { + apiVersion: String + specVersion: String! + features: [Feature!]! + dataSources: [String!]! + handlers: [String!]! + network: String +} + +type SubgraphIndexingStatus { + subgraph: String! + synced: Boolean! + health: Health! + """If the subgraph has failed, this is the error caused it""" + fatalError: SubgraphError + """Sorted from first to last, limited to first 1000""" + nonFatalErrors: [SubgraphError!]! + chains: [ChainIndexingStatus!]! + entityCount: BigInt! + """null if deployment is not assigned to an indexing node""" + node: String + """null if deployment is not assigned to an indexing node""" + paused: Boolean + historyBlocks: Int! +} + +type Version { + version: String! + commit: String! +} \ No newline at end of file diff --git a/graphql/subgraph_health.query.graphql b/graphql/subgraph_health.query.graphql new file mode 100644 index 00000000..22ab6cab --- /dev/null +++ b/graphql/subgraph_health.query.graphql @@ -0,0 +1,11 @@ +query HealthQuery($ids: [String!]!) { + indexingStatuses(subgraphs: $ids) { + health + fatalError { + message + } + nonFatalErrors { + message + } + } +} \ No newline at end of file diff --git a/justfile b/justfile new file mode 100644 index 00000000..29ec5f16 --- /dev/null +++ b/justfile @@ -0,0 +1,30 @@ +# Set DATABASE_URL: +# export DATABASE_URL=postgresql://postgres:postgres@127.0.0.1:5432 +# +# # print the help +help: + just -l + +deps: + cargo install sqlx-cli --no-default-features --features native-tls,postgres + +url: + @echo export DATABASE_URL=postgresql://postgres:postgres@127.0.0.1:5432 + +clippy: + cargo clippy --all-targets --all-features + +sqlx-prepare: + cargo sqlx prepare --workspace -- --all-targets --all-features + +psql-up: + @docker run -d --name indexer-rs-psql -p 5432:5432 -e POSTGRES_PASSWORD=postgres postgres + @sleep 5 + @just migrate + +psql-down: + docker stop indexer-rs-psql + docker rm indexer-rs-psql + +migrate: + sqlx migrate run --database-url postgresql://postgres:postgres@127.0.0.1:5432 diff --git a/migrations/20241030141929_dips.down.sql b/migrations/20241030141929_dips.down.sql new file mode 100644 index 00000000..00993582 --- /dev/null +++ b/migrations/20241030141929_dips.down.sql @@ -0,0 +1,2 @@ +-- Add down migration script here +DROP TABLE IF EXISTS dips_agreements; diff --git a/migrations/20241030141929_dips.up.sql b/migrations/20241030141929_dips.up.sql new file mode 100644 index 00000000..9aaedcbd --- /dev/null +++ b/migrations/20241030141929_dips.up.sql @@ -0,0 +1,20 @@ +-- Add up migration script here + +CREATE TABLE IF NOT EXISTS dips_agreements ( + id UUID PRIMARY KEY, + signature BYTEA NOT NULL, + signed_payload BYTEA NOT NULL, + + protocol CHAR(40) NOT NULL, + service BYTEA NOT NULL, + payee BYTEA NOT NULL, + payer BYTEA NOT NULL, + + created_at TIMESTAMP WITH TIME ZONE NOT NULL, + updated_at TIMESTAMP WITH TIME ZONE NOT NULL, + + cancelled_at TIMESTAMP WITH TIME ZONE, + signed_cancellation_payload BYTEA +); + +CREATE UNIQUE INDEX IX_UNIQ_SIGNATURE_AGREEMENT on dips_agreements(signature); diff --git a/security.md b/security.md index b440bbe4..717ff780 100644 --- a/security.md +++ b/security.md @@ -4,7 +4,7 @@ The team and community take all security bugs very seriously. We appreciate your efforts and responsible disclosure and will make every effort to acknowledge your contributions. -Report security bugs by emailing the lead maintainer at hope@graphops.xyz. +Report security bugs by emailing the lead maintainer at gustavo@semiotic.ai. The maintainer will acknowledge your email within 48 hours, and will send a more detailed response within 48 hours indicating the next steps in handling your report. After the initial reply to your report, the security team will endeavor to keep you informed of the progress towards a fix and full announcement, and may ask for additional information or guidance. diff --git a/service/CHANGELOG.md b/service/CHANGELOG.md index 2d2be7fb..532aa1a7 100644 --- a/service/CHANGELOG.md +++ b/service/CHANGELOG.md @@ -4,6 +4,22 @@ + + +## [1.3.1](https://github.com/graphprotocol/indexer-rs/compare/indexer-service-rs-v1.3.0...indexer-service-rs-v1.3.1) (2024-11-06) + + +### Bug Fixes + +* use request from gateway instead of serde req ([#464](https://github.com/graphprotocol/indexer-rs/issues/464)) ([fdeda9f](https://github.com/graphprotocol/indexer-rs/commit/fdeda9fea996f96e1c0a7bef291a551f426f5591)) + +## [1.3.0](https://github.com/graphprotocol/indexer-rs/compare/indexer-service-rs-v1.2.2...indexer-service-rs-v1.3.0) (2024-11-05) + + +### Features + +* add versioning on cli ([#460](https://github.com/graphprotocol/indexer-rs/issues/460)) ([419e7ff](https://github.com/graphprotocol/indexer-rs/commit/419e7ff513fd11294c8523f5dae102a5cbf77f94)) + ## [1.2.0](https://github.com/graphprotocol/indexer-rs/compare/indexer-service-rs-v1.1.1...indexer-service-rs-v1.2.0) (2024-10-30) diff --git a/service/Cargo.toml b/service/Cargo.toml index b952d5dc..68f218b8 100644 --- a/service/Cargo.toml +++ b/service/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "indexer-service-rs" -version = "1.2.2" +version = "1.3.2" edition = "2021" license = "Apache-2.0" @@ -9,6 +9,7 @@ license = "Apache-2.0" [dependencies] indexer-common = { path = "../common" } indexer-config = { path = "../config" } +indexer-dips = { path = "../dips" } anyhow = { workspace = true } prometheus = { workspace = true } reqwest = { workspace = true } @@ -27,8 +28,11 @@ build-info.workspace = true lazy_static.workspace = true async-graphql = { version = "7.0.11", default-features = false } async-graphql-axum = "7.0.11" +base64.workspace = true graphql = { git = "https://github.com/edgeandnode/toolshed", tag = "graphql-v0.3.0" } - +uuid.workspace = true +alloy.workspace = true + [dev-dependencies] hex-literal = "0.4.1" diff --git a/service/src/cli.rs b/service/src/cli.rs index 557e151f..c12d0ab3 100644 --- a/service/src/cli.rs +++ b/service/src/cli.rs @@ -6,6 +6,7 @@ use std::path::PathBuf; use clap::Parser; #[derive(Parser)] +#[command(version)] pub struct Cli { /// Path to the configuration file. /// See https://github.com/graphprotocol/indexer-rs/tree/main/service for examples. diff --git a/service/src/database/dips.rs b/service/src/database/dips.rs new file mode 100644 index 00000000..317b341f --- /dev/null +++ b/service/src/database/dips.rs @@ -0,0 +1,129 @@ +// Copyright 2023-, Edge & Node, GraphOps, and Semiotic Labs. +// SPDX-License-Identifier: Apache-2.0 + +use std::collections::HashMap; + +use alloy::rlp::Decodable; +use anyhow::bail; +use axum::async_trait; +use build_info::chrono::Utc; + +use indexer_dips::{SignedCancellationRequest, SignedIndexingAgreementVoucher}; +use sqlx::PgPool; +use uuid::Uuid; + +#[async_trait] +pub trait AgreementStore: Sync + Send { + async fn get_by_id(&self, id: Uuid) -> anyhow::Result>; + async fn create_agreement( + &self, + id: Uuid, + agreement: SignedIndexingAgreementVoucher, + protocol: String, + ) -> anyhow::Result<()>; + async fn cancel_agreement( + &self, + id: Uuid, + signed_cancellation: SignedCancellationRequest, + ) -> anyhow::Result; +} + +#[derive(Default)] +pub struct InMemoryAgreementStore { + pub data: tokio::sync::RwLock>, +} + +#[async_trait] +impl AgreementStore for InMemoryAgreementStore { + async fn get_by_id(&self, id: Uuid) -> anyhow::Result> { + Ok(self.data.try_read()?.get(&id).cloned()) + } + async fn create_agreement( + &self, + id: Uuid, + agreement: SignedIndexingAgreementVoucher, + _protocol: String, + ) -> anyhow::Result<()> { + self.data.try_write()?.insert(id, agreement.clone()); + + Ok(()) + } + async fn cancel_agreement( + &self, + id: Uuid, + _signed_cancellation: SignedCancellationRequest, + ) -> anyhow::Result { + self.data.try_write()?.remove(&id); + + Ok(id) + } +} + +#[derive(Debug)] +pub struct PsqlAgreementStore { + pool: PgPool, +} + +#[async_trait] +impl AgreementStore for PsqlAgreementStore { + async fn get_by_id(&self, id: Uuid) -> anyhow::Result> { + let item = sqlx::query!("SELECT * FROM dips_agreements WHERE id=$1", id,) + .fetch_one(&self.pool) + .await; + + let item = match item { + Ok(item) => item, + Err(sqlx::Error::RowNotFound) => return Ok(None), + Err(err) => bail!(err), + }; + + let signed = SignedIndexingAgreementVoucher::decode(&mut item.signed_payload.as_ref())?; + + Ok(Some(signed)) + } + async fn create_agreement( + &self, + id: Uuid, + agreement: SignedIndexingAgreementVoucher, + protocol: String, + ) -> anyhow::Result<()> { + let bs = agreement.encode_vec(); + let now = Utc::now(); + + sqlx::query!( + "INSERT INTO dips_agreements VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,null,null)", + id, + agreement.signature.as_ref(), + bs, + protocol, + agreement.voucher.service.as_slice(), + agreement.voucher.payee.as_slice(), + agreement.voucher.payer.as_slice(), + now, + now, + ) + .execute(&self.pool) + .await?; + + Ok(()) + } + async fn cancel_agreement( + &self, + id: Uuid, + signed_cancellation: SignedCancellationRequest, + ) -> anyhow::Result { + let bs = signed_cancellation.encode_vec(); + let now = Utc::now(); + + sqlx::query!( + "UPDATE dips_agreements SET updated_at=$1, cancelled_at=$1, signed_cancellation_payload=$2 WHERE id=$3", + now, + bs, + id, + ) + .execute(&self.pool) + .await?; + + Ok(id) + } +} diff --git a/service/src/database.rs b/service/src/database/mod.rs similarity index 96% rename from service/src/database.rs rename to service/src/database/mod.rs index 7054f035..e667e444 100644 --- a/service/src/database.rs +++ b/service/src/database/mod.rs @@ -1,8 +1,11 @@ // Copyright 2023-, Edge & Node, GraphOps, and Semiotic Labs. // SPDX-License-Identifier: Apache-2.0 -use sqlx::{postgres::PgPoolOptions, PgPool}; +pub mod dips; + use std::time::Duration; + +use sqlx::{postgres::PgPoolOptions, PgPool}; use tracing::debug; pub async fn connect(url: &str) -> PgPool { diff --git a/service/src/routes/dips.rs b/service/src/routes/dips.rs new file mode 100644 index 00000000..e9579edd --- /dev/null +++ b/service/src/routes/dips.rs @@ -0,0 +1,300 @@ +// Copyright 2023-, Edge & Node, GraphOps, and Semiotic Labs. +// SPDX-License-Identifier: Apache-2.0 + +use std::time::Duration; +use std::{str::FromStr, sync::Arc}; + +use anyhow::bail; +use async_graphql::{Context, FieldResult, Object, SimpleObject}; +use base64::{engine::general_purpose::STANDARD, Engine}; +use indexer_dips::alloy::dyn_abi::Eip712Domain; +use indexer_dips::SignedCancellationRequest; +use indexer_dips::{ + alloy::core::primitives::Address, alloy_rlp::Decodable, SignedIndexingAgreementVoucher, + SubgraphIndexingVoucherMetadata, +}; +use uuid::Uuid; + +use crate::database::dips::AgreementStore; + +pub enum NetworkProtocol { + ArbitrumMainnet, +} + +impl FromStr for NetworkProtocol { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + let p = match s { + "arbitrum-mainnet" => NetworkProtocol::ArbitrumMainnet, + _ => bail!("unknown network protocol"), + }; + + Ok(p) + } +} + +#[derive(SimpleObject, Debug, Clone, PartialEq)] +pub struct Agreement { + pub id: String, + pub signed_payload: String, + pub protocol_network: String, +} + +#[derive(SimpleObject, Debug, Clone, PartialEq)] +pub struct Cancellation { + pub signed_payload: String, + pub protocol_network: String, +} + +impl TryInto for (Uuid, SignedIndexingAgreementVoucher) { + type Error = anyhow::Error; + + fn try_into(self) -> Result { + let signed_payload = STANDARD.encode(self.1.encode_vec()); + let metadata = self.1.voucher.metadata; + let metadata: SubgraphIndexingVoucherMetadata = + SubgraphIndexingVoucherMetadata::decode(&mut metadata.as_ref())?; + + Ok(Agreement { + id: self.0.to_string(), + signed_payload, + protocol_network: metadata.protocolNetwork.to_string(), + }) + } +} + +impl TryInto for Agreement { + type Error = anyhow::Error; + + fn try_into(self) -> Result { + let rlp_bytes = STANDARD.decode(self.signed_payload)?; + let signed_voucher = SignedIndexingAgreementVoucher::decode(&mut rlp_bytes.as_ref())?; + + Ok(signed_voucher) + } +} + +#[derive(SimpleObject, Debug, Clone)] +pub struct Price { + price_per_block: String, + chain_id: String, + protocol_network: String, +} + +#[derive(Debug)] +pub struct AgreementQuery {} + +#[Object] +impl AgreementQuery { + pub async fn get_agreement_by_id<'a>( + &self, + ctx: &'a Context<'_>, + id: String, + ) -> FieldResult> { + let store: &Arc = ctx.data()?; + let id = Uuid::from_str(&id)?; + + match store + .get_by_id(id) + .await + .map_err(async_graphql::Error::from)? + { + Some(a) => (id, a) + .try_into() + .map(Some) + .map_err(async_graphql::Error::from), + None => Ok(None), + } + } + + pub async fn get_price<'a>( + &self, + ctx: &'a Context<'_>, + protocol_network: String, + chain_id: String, + ) -> FieldResult> { + let prices: &Vec = ctx.data()?; + + let p = prices + .iter() + .find(|p| p.protocol_network.eq(&protocol_network) && p.chain_id.eq(&chain_id)); + + Ok(p.cloned()) + } + + pub async fn get_all_prices<'a>(&self, ctx: &'a Context<'_>) -> FieldResult> { + let prices: &Vec = ctx.data()?; + + Ok(prices.clone()) + } +} + +#[derive(Debug)] +pub struct AgreementMutation { + pub expected_payee: Address, + pub allowed_payers: Vec
, + pub domain: Eip712Domain, + pub cancel_voucher_time_tolerance: Duration, +} + +#[Object] +impl AgreementMutation { + // create_agreements returns the UUID under which the agreement was stored. + pub async fn create_agreement<'a>( + &self, + ctx: &'a Context<'_>, + // uuid v7 that this agreement should use. + id: String, + // data should be the signed voucher, eip712 signed, rlp and base64 encoded. + signed_voucher: String, + ) -> FieldResult { + let store: &Arc = ctx.data()?; + let uid = Uuid::from_str(&id)?; + + validate_and_create_agreement( + store.clone(), + &self.domain, + uid, + &self.expected_payee, + &self.allowed_payers, + signed_voucher, + ) + .await + .map_err(async_graphql::Error::from)?; + + Ok(id) + } + + pub async fn cancel_agreement<'a>( + &self, + ctx: &'a Context<'_>, + id: String, + // data should be the signed voucher, eip712 signed, rlp and base64 encoded. + signed_request: String, + ) -> FieldResult { + let store: &Arc = ctx.data()?; + let uid = Uuid::from_str(&id)?; + + validate_and_cancel_agreement( + store.clone(), + &self.domain, + uid, + signed_request.clone(), + self.cancel_voucher_time_tolerance, + ) + .await + .map_err(async_graphql::Error::from)?; + + Ok(id) + } +} + +async fn validate_and_create_agreement( + store: Arc, + domain: &Eip712Domain, + id: Uuid, + expected_payee: &Address, + allowed_payers: impl AsRef<[Address]>, + agreement: String, +) -> anyhow::Result { + let rlp_bs = STANDARD.decode(agreement.clone())?; + let voucher = SignedIndexingAgreementVoucher::decode(&mut rlp_bs.as_ref())?; + let metadata = SubgraphIndexingVoucherMetadata::decode(&mut voucher.voucher.metadata.as_ref())?; + + voucher.validate(domain, expected_payee, allowed_payers)?; + + store + .create_agreement(id, voucher, metadata.protocolNetwork.to_string()) + .await?; + + Ok(id) +} + +async fn validate_and_cancel_agreement( + store: Arc, + domain: &Eip712Domain, + id: Uuid, + agreement: String, + time_tolerance: Duration, +) -> anyhow::Result { + let rlp_bs = STANDARD.decode(agreement.clone())?; + let voucher = SignedCancellationRequest::decode(&mut rlp_bs.as_ref())?; + + voucher.validate(domain, time_tolerance)?; + + store.cancel_agreement(id, voucher).await?; + + Ok(id) +} + +#[cfg(test)] +mod test { + use std::sync::Arc; + + use alloy::signers::local::PrivateKeySigner; + use base64::{engine::general_purpose::STANDARD, Engine}; + use indexer_dips::{ + alloy::core::primitives::{Address, FixedBytes, U256}, + alloy_rlp::{self}, + IndexingAgreementVoucher, SubgraphIndexingVoucherMetadata, + }; + use thegraph_core::attestation::eip712_domain; + use uuid::Uuid; + + use crate::database::dips::{AgreementStore, InMemoryAgreementStore}; + + #[tokio::test] + async fn test_validate_and_create_agreement() -> anyhow::Result<()> { + let deployment_id = "Qmbg1qF4YgHjiVfsVt6a13ddrVcRtWyJQfD4LA3CwHM29f".to_string(); + let payee = PrivateKeySigner::random(); + let payee_addr = payee.address(); + let payer = PrivateKeySigner::random(); + let payer_addr = payer.address(); + + let metadata = SubgraphIndexingVoucherMetadata { + pricePerBlock: U256::from(10000_u64), + protocolNetwork: FixedBytes::left_padding_from("arbitrum-one".as_bytes()), + chainId: FixedBytes::left_padding_from("mainnet".as_bytes()), + deployment_ipfs_hash: deployment_id, + }; + + let voucher = IndexingAgreementVoucher { + payer: payer_addr, + payee: payee_addr, + service: Address(FixedBytes::ZERO), + maxInitialAmount: U256::from(10000_u64), + maxOngoingAmountPerEpoch: U256::from(10000_u64), + deadline: 1000, + maxEpochsPerCollection: 1000, + minEpochsPerCollection: 1000, + durationEpochs: 1000, + metadata: alloy_rlp::encode(metadata).into(), + }; + let domain = eip712_domain(0, Address::ZERO); + + let voucher = voucher.sign(&domain, payer)?; + let rlp_voucher = alloy_rlp::encode(voucher.clone()); + let b64 = STANDARD.encode(rlp_voucher); + let id = Uuid::now_v7(); + + let store = Arc::new(InMemoryAgreementStore::default()); + + let actual_id = super::validate_and_create_agreement( + store.clone(), + &domain, + id, + &payee_addr, + vec![payer_addr], + b64, + ) + .await + .unwrap(); + + let actual = store.get_by_id(actual_id).await.unwrap(); + + let actual_voucher = actual.unwrap(); + assert_eq!(voucher, actual_voucher); + Ok(()) + } +} diff --git a/service/src/routes/mod.rs b/service/src/routes/mod.rs index 9ac12f8f..e65d13db 100644 --- a/service/src/routes/mod.rs +++ b/service/src/routes/mod.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 pub mod cost; +pub mod dips; mod status; pub use status::status; diff --git a/service/src/service.rs b/service/src/service.rs index 12504677..2a4b8328 100644 --- a/service/src/service.rs +++ b/service/src/service.rs @@ -6,18 +6,32 @@ use std::time::Duration; use super::{error::SubgraphServiceError, routes}; use anyhow::anyhow; -use axum::{async_trait, routing::post, Json, Router}; +use async_graphql::{EmptySubscription, Schema}; +use async_graphql_axum::GraphQL; +use axum::{ + async_trait, + routing::{post, post_service}, + Json, Router, +}; use indexer_common::indexer_service::http::{ AttestationOutput, IndexerServiceImpl, IndexerServiceResponse, }; -use indexer_config::Config; +use indexer_config::{Config, DipsConfig}; use reqwest::Url; use serde::{de::DeserializeOwned, Serialize}; use serde_json::{json, Value}; use sqlx::PgPool; +use thegraph_core::attestation::eip712_domain; use thegraph_core::DeploymentId; -use crate::{cli::Cli, database}; +use crate::{ + cli::Cli, + database::{ + self, + dips::{AgreementStore, InMemoryAgreementStore}, + }, + routes::dips::Price, +}; use clap::Parser; use indexer_common::indexer_service::http::{ @@ -90,7 +104,7 @@ impl IndexerServiceImpl for SubgraphService { &self, deployment: DeploymentId, request: Request, - ) -> Result<(Request, Self::Response), Self::Error> { + ) -> Result { let deployment_url = self .state .graph_node_query_base_url @@ -118,7 +132,7 @@ impl IndexerServiceImpl for SubgraphService { .await .map_err(SubgraphServiceError::QueryForwardingError)?; - Ok((request, SubgraphServiceResponse::new(body, attestable))) + Ok(SubgraphServiceResponse::new(body, attestable)) } } @@ -163,15 +177,47 @@ pub async fn run() -> anyhow::Result<()> { graph_node_query_base_url: &config.graph_node.query_url, }); + let agreement_store: Arc = Arc::new(InMemoryAgreementStore::default()); + let prices: Vec = vec![]; + + let mut router = Router::new() + .route("/cost", post(routes::cost::cost)) + .route("/status", post(routes::status)) + .with_state(state.clone()); + + if let Some(DipsConfig { + allowed_payers, + cancellation_time_tolerance, + }) = config.dips.as_ref() + { + let schema = Schema::build( + routes::dips::AgreementQuery {}, + routes::dips::AgreementMutation { + expected_payee: config.indexer.indexer_address, + allowed_payers: allowed_payers.clone(), + domain: eip712_domain( + // 42161, // arbitrum + config.blockchain.chain_id as u64, + config.blockchain.receipts_verifier_address, + ), + cancel_voucher_time_tolerance: cancellation_time_tolerance + .unwrap_or(Duration::from_secs(5)), + }, + EmptySubscription, + ) + .data(agreement_store) + .data(prices) + .finish(); + + router = router.route("/dips", post_service(GraphQL::new(schema))); + } + IndexerService::run(IndexerServiceOptions { release, config, url_namespace: "subgraphs", - service_impl: SubgraphService::new(state.clone()), - extra_routes: Router::new() - .route("/cost", post(routes::cost::cost)) - .route("/status", post(routes::status)) - .with_state(state), + service_impl: SubgraphService::new(state), + extra_routes: router, }) .await } diff --git a/tap-agent/CHANGELOG.md b/tap-agent/CHANGELOG.md index 18bf6f5e..012329e9 100644 --- a/tap-agent/CHANGELOG.md +++ b/tap-agent/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog + + + + +## [1.7.0](https://github.com/graphprotocol/indexer-rs/compare/indexer-tap-agent-v1.6.0...indexer-tap-agent-v1.7.0) (2024-11-05) + + +### Features + +* add versioning on cli ([#460](https://github.com/graphprotocol/indexer-rs/issues/460)) ([419e7ff](https://github.com/graphprotocol/indexer-rs/commit/419e7ff513fd11294c8523f5dae102a5cbf77f94)) + ## [1.6.0](https://github.com/graphprotocol/indexer-rs/compare/indexer-tap-agent-v1.5.0...indexer-tap-agent-v1.6.0) (2024-11-04) diff --git a/tap-agent/Cargo.toml b/tap-agent/Cargo.toml index d3747bdc..731d1f22 100644 --- a/tap-agent/Cargo.toml +++ b/tap-agent/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "indexer-tap-agent" -version = "1.6.0" +version = "1.7.2" edition = "2021" publish = false @@ -37,10 +37,10 @@ ruint = { version = "1.12.3", features = [ ], default-features = false } futures-util = { version = "0.3.28", default-features = false } jsonrpsee = { version = "0.24.0", features = ["http-client", "tracing"] } -tap_aggregator = { git = "https://github.com/semiotic-ai/timeline-aggregation-protocol", rev = "eb8447e" } ractor = { version = "0.13", features = [ "async-trait", ], default-features = false } +tap_aggregator.workspace = true futures = { version = "0.3.30", default-features = false } [dev-dependencies] diff --git a/tap-agent/src/cli.rs b/tap-agent/src/cli.rs index 9a392882..74fb8876 100644 --- a/tap-agent/src/cli.rs +++ b/tap-agent/src/cli.rs @@ -10,6 +10,7 @@ use tracing::{error, level_filters::LevelFilter}; use tracing_subscriber::{EnvFilter, FmtSubscriber}; #[derive(Parser)] +#[command(version)] pub struct Cli { /// Path to the configuration file. /// See https://github.com/graphprotocol/indexer-rs/tree/main/tap-agent for examples.