From 9e4735a908b21e5a9b052ed7367960a78b28f54c Mon Sep 17 00:00:00 2001 From: Alexis Asseman Date: Thu, 21 Sep 2023 16:46:23 -0700 Subject: [PATCH 01/18] feat: TAP Agent Signed-off-by: Alexis Asseman --- ...7f5b1ce3c8594b3657436a6cd68d683ef8190.json | 41 + ...ddcfb586bc049fa2d61740c400ae64d33d3b8.json | 35 + ...0a64bf03831f535c6f8766f9ebea7b26d18dc.json | 26 + ...021befec9a5efd2f9d39078d9b45f79c1f31a.json | 16 + ...eee486bfc78faac6376100e4b3fbe54cdbeb2.json | 20 + ...159932331aaf7fe2fd7fdbb5df04485ec91ec.json | 12 + ...4315bab48aeb9ddd1ed80486f22511c39c92e.json | 30 + ...3226c8a792538eed5ae1397f5515ef43f52af.json | 16 + ...f5c57d44af2722c379e4528596e9b041242d4.json | 16 + ...1d3afe596556e730b48fb31edfaf14096d3dd.json | 23 + ...b3f102b32750519586a393c3a72c297f10d7e.json | 26 + ...a24471d24c11c957f986144ed04694024e922.json | 20 + Cargo.lock | 1786 ++++++++++------- Cargo.toml | 1 + common/src/tap_manager.rs | 24 +- migrations/20230912220523_tap_receipts.up.sql | 5 +- migrations/20230915230734_tap_ravs.down.sql | 1 + migrations/20230915230734_tap_ravs.up.sql | 7 + service/src/main.rs | 1 - tap_agent/Cargo.toml | 46 + tap_agent/src/agent.rs | 89 + tap_agent/src/aggregator_endpoints.rs | 45 + tap_agent/src/config.rs | 320 +++ tap_agent/src/database.rs | 32 + tap_agent/src/main.rs | 44 + tap_agent/src/tap/account.rs | 857 ++++++++ tap_agent/src/tap/accounts_manager.rs | 422 ++++ tap_agent/src/tap/escrow_adapter.rs | 154 ++ tap_agent/src/tap/mod.rs | 12 + tap_agent/src/tap/rav_storage_adapter.rs | 155 ++ tap_agent/src/tap/receipt_checks_adapter.rs | 205 ++ tap_agent/src/tap/receipt_storage_adapter.rs | 608 ++++++ tap_agent/src/tap/test_utils.rs | 148 ++ 33 files changed, 4456 insertions(+), 787 deletions(-) create mode 100644 .sqlx/query-089809ab6cd948db1adb1cedff47f5b1ce3c8594b3657436a6cd68d683ef8190.json create mode 100644 .sqlx/query-0e46530ba12da19cedaebaab66bddcfb586bc049fa2d61740c400ae64d33d3b8.json create mode 100644 .sqlx/query-1fdec6cc247605be5d1991db42d0a64bf03831f535c6f8766f9ebea7b26d18dc.json create mode 100644 .sqlx/query-26e4dda54e43d15e3c747982c6c021befec9a5efd2f9d39078d9b45f79c1f31a.json create mode 100644 .sqlx/query-500a0fe538960d8e455537a6591eee486bfc78faac6376100e4b3fbe54cdbeb2.json create mode 100644 .sqlx/query-8e0a94a7385212c8bd515a77671159932331aaf7fe2fd7fdbb5df04485ec91ec.json create mode 100644 .sqlx/query-aede274abf6f8fabe510f7765fc4315bab48aeb9ddd1ed80486f22511c39c92e.json create mode 100644 .sqlx/query-b3726609aaf6bbeb8a08cbcc10f3226c8a792538eed5ae1397f5515ef43f52af.json create mode 100644 .sqlx/query-c3e88c5a56db17eb8e1f8056c58f5c57d44af2722c379e4528596e9b041242d4.json create mode 100644 .sqlx/query-c4014f5e68d5a00d47138828d941d3afe596556e730b48fb31edfaf14096d3dd.json create mode 100644 .sqlx/query-d7ac08a02062e6cc1a252567431b3f102b32750519586a393c3a72c297f10d7e.json create mode 100644 .sqlx/query-dc8cea825babfaadfc1845481c1a24471d24c11c957f986144ed04694024e922.json create mode 100644 migrations/20230915230734_tap_ravs.down.sql create mode 100644 migrations/20230915230734_tap_ravs.up.sql create mode 100644 tap_agent/Cargo.toml create mode 100644 tap_agent/src/agent.rs create mode 100644 tap_agent/src/aggregator_endpoints.rs create mode 100644 tap_agent/src/config.rs create mode 100644 tap_agent/src/database.rs create mode 100644 tap_agent/src/main.rs create mode 100644 tap_agent/src/tap/account.rs create mode 100644 tap_agent/src/tap/accounts_manager.rs create mode 100644 tap_agent/src/tap/escrow_adapter.rs create mode 100644 tap_agent/src/tap/mod.rs create mode 100644 tap_agent/src/tap/rav_storage_adapter.rs create mode 100644 tap_agent/src/tap/receipt_checks_adapter.rs create mode 100644 tap_agent/src/tap/receipt_storage_adapter.rs create mode 100644 tap_agent/src/tap/test_utils.rs diff --git a/.sqlx/query-089809ab6cd948db1adb1cedff47f5b1ce3c8594b3657436a6cd68d683ef8190.json b/.sqlx/query-089809ab6cd948db1adb1cedff47f5b1ce3c8594b3657436a6cd68d683ef8190.json new file mode 100644 index 00000000..39c2485c --- /dev/null +++ b/.sqlx/query-089809ab6cd948db1adb1cedff47f5b1ce3c8594b3657436a6cd68d683ef8190.json @@ -0,0 +1,41 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE scalar_tap_latest_ravs\n SET is_last = true\n WHERE allocation_id = $1 AND sender_address = $2\n RETURNING *\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "allocation_id", + "type_info": "Bpchar" + }, + { + "ordinal": 1, + "name": "sender_address", + "type_info": "Bpchar" + }, + { + "ordinal": 2, + "name": "rav", + "type_info": "Json" + }, + { + "ordinal": 3, + "name": "is_last", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Bpchar", + "Bpchar" + ] + }, + "nullable": [ + false, + false, + false, + false + ] + }, + "hash": "089809ab6cd948db1adb1cedff47f5b1ce3c8594b3657436a6cd68d683ef8190" +} diff --git a/.sqlx/query-0e46530ba12da19cedaebaab66bddcfb586bc049fa2d61740c400ae64d33d3b8.json b/.sqlx/query-0e46530ba12da19cedaebaab66bddcfb586bc049fa2d61740c400ae64d33d3b8.json new file mode 100644 index 00000000..ecc6face --- /dev/null +++ b/.sqlx/query-0e46530ba12da19cedaebaab66bddcfb586bc049fa2d61740c400ae64d33d3b8.json @@ -0,0 +1,35 @@ +{ + "db_name": "PostgreSQL", + "query": "\n WITH rav AS (\n SELECT \n rav -> 'message' ->> 'timestamp_ns' AS timestamp_ns \n FROM \n scalar_tap_latest_ravs \n WHERE \n allocation_id = $1 \n AND sender_address = $2\n ) \n SELECT \n COUNT(*), \n MAX(id), \n SUM(value) \n FROM \n scalar_tap_receipts \n WHERE \n allocation_id = $1 \n AND sender_address = $2 \n AND CASE WHEN (\n SELECT \n timestamp_ns :: NUMERIC \n FROM \n rav\n ) IS NOT NULL THEN timestamp_ns > (\n SELECT \n timestamp_ns :: NUMERIC \n FROM \n rav\n ) ELSE TRUE END\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "max", + "type_info": "Int8" + }, + { + "ordinal": 2, + "name": "sum", + "type_info": "Numeric" + } + ], + "parameters": { + "Left": [ + "Bpchar", + "Bpchar" + ] + }, + "nullable": [ + null, + null, + null + ] + }, + "hash": "0e46530ba12da19cedaebaab66bddcfb586bc049fa2d61740c400ae64d33d3b8" +} diff --git a/.sqlx/query-1fdec6cc247605be5d1991db42d0a64bf03831f535c6f8766f9ebea7b26d18dc.json b/.sqlx/query-1fdec6cc247605be5d1991db42d0a64bf03831f535c6f8766f9ebea7b26d18dc.json new file mode 100644 index 00000000..47709ffb --- /dev/null +++ b/.sqlx/query-1fdec6cc247605be5d1991db42d0a64bf03831f535c6f8766f9ebea7b26d18dc.json @@ -0,0 +1,26 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO scalar_tap_receipts (\n allocation_id, sender_address, timestamp_ns, value, receipt\n )\n VALUES ($1, $2, $3, $4, $5)\n RETURNING id\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Bpchar", + "Bpchar", + "Numeric", + "Numeric", + "Json" + ] + }, + "nullable": [ + false + ] + }, + "hash": "1fdec6cc247605be5d1991db42d0a64bf03831f535c6f8766f9ebea7b26d18dc" +} diff --git a/.sqlx/query-26e4dda54e43d15e3c747982c6c021befec9a5efd2f9d39078d9b45f79c1f31a.json b/.sqlx/query-26e4dda54e43d15e3c747982c6c021befec9a5efd2f9d39078d9b45f79c1f31a.json new file mode 100644 index 00000000..c27abc7a --- /dev/null +++ b/.sqlx/query-26e4dda54e43d15e3c747982c6c021befec9a5efd2f9d39078d9b45f79c1f31a.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO scalar_tap_latest_ravs (\n allocation_id, sender_address, rav\n )\n VALUES ($1, $2, $3)\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Bpchar", + "Bpchar", + "Json" + ] + }, + "nullable": [] + }, + "hash": "26e4dda54e43d15e3c747982c6c021befec9a5efd2f9d39078d9b45f79c1f31a" +} diff --git a/.sqlx/query-500a0fe538960d8e455537a6591eee486bfc78faac6376100e4b3fbe54cdbeb2.json b/.sqlx/query-500a0fe538960d8e455537a6591eee486bfc78faac6376100e4b3fbe54cdbeb2.json new file mode 100644 index 00000000..69a9fe5f --- /dev/null +++ b/.sqlx/query-500a0fe538960d8e455537a6591eee486bfc78faac6376100e4b3fbe54cdbeb2.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT receipt\n FROM scalar_tap_receipts\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "receipt", + "type_info": "Json" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + false + ] + }, + "hash": "500a0fe538960d8e455537a6591eee486bfc78faac6376100e4b3fbe54cdbeb2" +} diff --git a/.sqlx/query-8e0a94a7385212c8bd515a77671159932331aaf7fe2fd7fdbb5df04485ec91ec.json b/.sqlx/query-8e0a94a7385212c8bd515a77671159932331aaf7fe2fd7fdbb5df04485ec91ec.json new file mode 100644 index 00000000..6a926de1 --- /dev/null +++ b/.sqlx/query-8e0a94a7385212c8bd515a77671159932331aaf7fe2fd7fdbb5df04485ec91ec.json @@ -0,0 +1,12 @@ +{ + "db_name": "PostgreSQL", + "query": "\n DELETE FROM scalar_tap_receipts\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [] + }, + "nullable": [] + }, + "hash": "8e0a94a7385212c8bd515a77671159932331aaf7fe2fd7fdbb5df04485ec91ec" +} diff --git a/.sqlx/query-aede274abf6f8fabe510f7765fc4315bab48aeb9ddd1ed80486f22511c39c92e.json b/.sqlx/query-aede274abf6f8fabe510f7765fc4315bab48aeb9ddd1ed80486f22511c39c92e.json new file mode 100644 index 00000000..07dfbd7b --- /dev/null +++ b/.sqlx/query-aede274abf6f8fabe510f7765fc4315bab48aeb9ddd1ed80486f22511c39c92e.json @@ -0,0 +1,30 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT id, receipt\n FROM scalar_tap_receipts\n WHERE allocation_id = $1 AND sender_address = $2 AND $3::numrange @> timestamp_ns\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "receipt", + "type_info": "Json" + } + ], + "parameters": { + "Left": [ + "Bpchar", + "Bpchar", + "NumRange" + ] + }, + "nullable": [ + false, + false + ] + }, + "hash": "aede274abf6f8fabe510f7765fc4315bab48aeb9ddd1ed80486f22511c39c92e" +} diff --git a/.sqlx/query-b3726609aaf6bbeb8a08cbcc10f3226c8a792538eed5ae1397f5515ef43f52af.json b/.sqlx/query-b3726609aaf6bbeb8a08cbcc10f3226c8a792538eed5ae1397f5515ef43f52af.json new file mode 100644 index 00000000..6a189434 --- /dev/null +++ b/.sqlx/query-b3726609aaf6bbeb8a08cbcc10f3226c8a792538eed5ae1397f5515ef43f52af.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO scalar_tap_latest_ravs (allocation_id, sender_address, rav)\n VALUES ($1, $2, $3)\n ON CONFLICT (allocation_id, sender_address)\n DO UPDATE SET rav = $3\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Bpchar", + "Bpchar", + "Json" + ] + }, + "nullable": [] + }, + "hash": "b3726609aaf6bbeb8a08cbcc10f3226c8a792538eed5ae1397f5515ef43f52af" +} diff --git a/.sqlx/query-c3e88c5a56db17eb8e1f8056c58f5c57d44af2722c379e4528596e9b041242d4.json b/.sqlx/query-c3e88c5a56db17eb8e1f8056c58f5c57d44af2722c379e4528596e9b041242d4.json new file mode 100644 index 00000000..7596f9d3 --- /dev/null +++ b/.sqlx/query-c3e88c5a56db17eb8e1f8056c58f5c57d44af2722c379e4528596e9b041242d4.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "\n DELETE FROM scalar_tap_receipts\n WHERE allocation_id = $1 AND sender_address = $2 AND $3::numrange @> timestamp_ns\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Bpchar", + "Bpchar", + "NumRange" + ] + }, + "nullable": [] + }, + "hash": "c3e88c5a56db17eb8e1f8056c58f5c57d44af2722c379e4528596e9b041242d4" +} diff --git a/.sqlx/query-c4014f5e68d5a00d47138828d941d3afe596556e730b48fb31edfaf14096d3dd.json b/.sqlx/query-c4014f5e68d5a00d47138828d941d3afe596556e730b48fb31edfaf14096d3dd.json new file mode 100644 index 00000000..66daf74b --- /dev/null +++ b/.sqlx/query-c4014f5e68d5a00d47138828d941d3afe596556e730b48fb31edfaf14096d3dd.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT rav\n FROM scalar_tap_latest_ravs\n WHERE allocation_id = $1 AND sender_address = $2\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "rav", + "type_info": "Json" + } + ], + "parameters": { + "Left": [ + "Bpchar", + "Bpchar" + ] + }, + "nullable": [ + false + ] + }, + "hash": "c4014f5e68d5a00d47138828d941d3afe596556e730b48fb31edfaf14096d3dd" +} diff --git a/.sqlx/query-d7ac08a02062e6cc1a252567431b3f102b32750519586a393c3a72c297f10d7e.json b/.sqlx/query-d7ac08a02062e6cc1a252567431b3f102b32750519586a393c3a72c297f10d7e.json new file mode 100644 index 00000000..028b60d7 --- /dev/null +++ b/.sqlx/query-d7ac08a02062e6cc1a252567431b3f102b32750519586a393c3a72c297f10d7e.json @@ -0,0 +1,26 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT DISTINCT allocation_id, sender_address\n FROM scalar_tap_receipts\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "allocation_id", + "type_info": "Bpchar" + }, + { + "ordinal": 1, + "name": "sender_address", + "type_info": "Bpchar" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + false, + false + ] + }, + "hash": "d7ac08a02062e6cc1a252567431b3f102b32750519586a393c3a72c297f10d7e" +} diff --git a/.sqlx/query-dc8cea825babfaadfc1845481c1a24471d24c11c957f986144ed04694024e922.json b/.sqlx/query-dc8cea825babfaadfc1845481c1a24471d24c11c957f986144ed04694024e922.json new file mode 100644 index 00000000..fb6987e7 --- /dev/null +++ b/.sqlx/query-dc8cea825babfaadfc1845481c1a24471d24c11c957f986144ed04694024e922.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT count(*)\n FROM scalar_tap_receipts\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "dc8cea825babfaadfc1845481c1a24471d24c11c957f986144ed04694024e922" +} diff --git a/Cargo.lock b/Cargo.lock index 483418c2..ae9ddb87 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,9 +14,9 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ "gimli", ] @@ -29,9 +29,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aes" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" dependencies = [ "cfg-if", "cipher", @@ -44,7 +44,7 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ - "getrandom 0.2.9", + "getrandom 0.2.10", "once_cell", "version_check", ] @@ -56,16 +56,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" dependencies = [ "cfg-if", - "getrandom 0.2.9", + "getrandom 0.2.10", "once_cell", "version_check", ] [[package]] name = "aho-corasick" -version = "1.0.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] @@ -98,9 +98,9 @@ dependencies = [ [[package]] name = "alloy-rlp" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f938f00332d63a5b0ac687bd6f46d03884638948921d9f8b50c59563d421ae25" +checksum = "cc0fac0fc16baf1f63f78b47c3d24718f3619b0714076f6a02957d808d52cbef" dependencies = [ "arrayvec", "bytes", @@ -119,7 +119,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.38", "syn-solidity", "tiny-keccak", ] @@ -153,30 +153,29 @@ dependencies = [ [[package]] name = "anstream" -version = "0.3.2" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", - "is-terminal", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "anstyle-parse" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" +checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" dependencies = [ "utf8parse", ] @@ -187,17 +186,17 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" dependencies = [ - "windows-sys 0.48.0", + "windows-sys", ] [[package]] name = "anstyle-wincon" -version = "1.0.1" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" dependencies = [ "anstyle", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -336,20 +335,11 @@ dependencies = [ "rand 0.8.5", ] -[[package]] -name = "array-init" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23589ecb866b460d3a0f1278834750268c607e8e28a1b982c907219f3178cd72" -dependencies = [ - "nodrop", -] - [[package]] name = "arrayvec" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "ascii" @@ -443,7 +433,7 @@ dependencies = [ "futures-util", "handlebars", "http", - "indexmap 2.0.0", + "indexmap 2.0.2", "mime", "multer", "num-traits", @@ -466,7 +456,7 @@ checksum = "c91ac174c05670edffb720bc376b9d4c274c3d127ac08ed3d38144c9415502cd" dependencies = [ "async-graphql 4.0.16", "async-trait", - "axum", + "axum 0.5.17", "bytes", "futures-util", "http-body", @@ -504,7 +494,7 @@ dependencies = [ "proc-macro2", "quote", "strum 0.25.0", - "syn 2.0.28", + "syn 2.0.38", "thiserror", ] @@ -551,7 +541,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7d74240f9daa8c1e8f73e9cfcc338d20a88d00bbeb83ded49ce8e5b4dcec0f5" dependencies = [ "bytes", - "indexmap 2.0.0", + "indexmap 2.0.2", "serde", "serde_json", ] @@ -575,18 +565,18 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.38", ] [[package]] name = "async-trait" -version = "0.1.73" +version = "0.1.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.38", ] [[package]] @@ -673,7 +663,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acee9fd5073ab6b045a275b3e709c163dd36c90685219cb21804a147b58dba43" dependencies = [ "async-trait", - "axum-core", + "axum-core 0.2.9", "base64 0.13.1", "bitflags 1.3.2", "bytes", @@ -683,7 +673,7 @@ dependencies = [ "http-body", "hyper", "itoa", - "matchit", + "matchit 0.5.0", "memchr", "mime", "percent-encoding", @@ -691,7 +681,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sha-1", + "sha-1 0.10.1", "sync_wrapper", "tokio", "tokio-tungstenite 0.17.2", @@ -701,6 +691,38 @@ dependencies = [ "tower-service", ] +[[package]] +name = "axum" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +dependencies = [ + "async-trait", + "axum-core 0.3.4", + "bitflags 1.3.2", + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "itoa", + "matchit 0.7.3", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", +] + [[package]] name = "axum-core" version = "0.2.9" @@ -717,11 +739,28 @@ dependencies = [ "tower-service", ] +[[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", + "http-body", + "mime", + "rustversion", + "tower-layer", + "tower-service", +] + [[package]] name = "backtrace" -version = "0.3.68" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" dependencies = [ "addr2line", "cc", @@ -746,9 +785,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.2" +version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" +checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" [[package]] name = "base64ct" @@ -758,9 +797,18 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "bech32" -version = "0.7.3" +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 = "2dabbe35f96fb9507f7330793dc490461b2962659ac5d427181e451a623751d1" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" +dependencies = [ + "serde", +] [[package]] name = "bigdecimal" @@ -773,15 +821,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - [[package]] name = "bit-set" version = "0.5.3" @@ -805,23 +844,13 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" dependencies = [ "serde", ] -[[package]] -name = "bitvec" -version = "0.17.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41262f11d771fd4a61aa3ce019fca363b4b6c282fca9da2a31186d3965a47a5c" -dependencies = [ - "either", - "radium 0.3.0", -] - [[package]] name = "bitvec" version = "1.0.1" @@ -829,7 +858,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ "funty", - "radium 0.7.0", + "radium", "tap", "wyz", ] @@ -859,7 +888,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b" dependencies = [ "borsh-derive", - "hashbrown 0.12.3", + "hashbrown 0.13.2", ] [[package]] @@ -899,27 +928,29 @@ dependencies = [ [[package]] name = "bs58" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" +checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896" dependencies = [ - "sha2 0.9.9", + "sha2", + "tinyvec", ] [[package]] -name = "bs58" -version = "0.5.0" +name = "bstr" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896" +checksum = "c79ad7fb2dd38f3dabd76b09c6a5a20c038fc0213ef1e9afd30eb777f120f019" dependencies = [ - "tinyvec", + "memchr", + "serde", ] [[package]] name = "bumpalo" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "by_address" @@ -957,15 +988,15 @@ dependencies = [ [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" dependencies = [ "serde", ] @@ -993,9 +1024,9 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.4" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c530edf18f37068ac2d977409ed5cd50d53d73bc653c7647b48eb78976ac9ae2" +checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" dependencies = [ "serde", ] @@ -1008,9 +1039,9 @@ checksum = "7b02b629252fe8ef6460461409564e2c21d0c8e77e0944f3d189ff06c4e932ad" [[package]] name = "cargo-platform" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" +checksum = "12024c4645c97566567129c204f65d5815a8c9aecf30fcbe682b2fe034996d36" dependencies = [ "serde", ] @@ -1023,7 +1054,7 @@ checksum = "e7daec1a2a2129eeba1644b220b4647ec537b0b5d4bfd6876fcc5a540056b592" dependencies = [ "camino", "cargo-platform", - "semver 1.0.17", + "semver 1.0.20", "serde", "serde_json", "thiserror", @@ -1031,11 +1062,12 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.79" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ "jobserver", + "libc", ] [[package]] @@ -1046,15 +1078,15 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.26" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", "serde", - "winapi", + "windows-targets", ] [[package]] @@ -1069,98 +1101,92 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.1" +version = "4.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ed2379f8603fa2b7509891660e802b88c70a79a6427a70abb5968054de2c28" +checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956" dependencies = [ "clap_builder", "clap_derive", - "once_cell", ] [[package]] name = "clap_builder" -version = "4.3.1" +version = "4.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72394f3339a76daf211e57d4bcb374410f3965dcc606dd0e03738c7888766980" +checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45" dependencies = [ "anstream", "anstyle", - "bitflags 1.3.2", "clap_lex", "strsim", ] [[package]] name = "clap_derive" -version = "4.3.1" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59e9ef9a08ee1c0e1f2e162121665ac45ac3783b0f897db7244ae75ad9a8f65b" +checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.38", ] [[package]] name = "clap_lex" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" +checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" [[package]] name = "coins-bip32" -version = "0.8.3" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b30a84aab436fcb256a2ab3c80663d8aec686e6bae12827bb05fef3e1e439c9f" +checksum = "3b6be4a5df2098cd811f3194f64ddb96c267606bffd9689ac7b0160097b01ad3" dependencies = [ - "bincode", - "bs58 0.4.0", + "bs58", "coins-core", "digest 0.10.7", - "getrandom 0.2.9", "hmac", "k256", - "lazy_static", "serde", - "sha2 0.10.7", + "sha2", "thiserror", ] [[package]] name = "coins-bip39" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84f4d04ee18e58356accd644896aeb2094ddeafb6a713e056cef0c0a8e468c15" +checksum = "3db8fba409ce3dc04f7d804074039eb68b960b0829161f8e06c95fea3f122528" dependencies = [ - "bitvec 0.17.4", + "bitvec", "coins-bip32", - "getrandom 0.2.9", "hmac", "once_cell", - "pbkdf2 0.12.1", + "pbkdf2 0.12.2", "rand 0.8.5", - "sha2 0.10.7", + "sha2", "thiserror", ] [[package]] name = "coins-core" -version = "0.8.3" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b949a1c63fb7eb591eb7ba438746326aedf0ae843e51ec92ba6bec5bb382c4f" +checksum = "5286a0843c21f8367f7be734f89df9b822e0321d8bcce8d6e735aadff7d74979" dependencies = [ - "base64 0.21.2", + "base64 0.21.4", "bech32", - "bs58 0.4.0", + "bs58", "digest 0.10.7", "generic-array", "hex", "ripemd", "serde", "serde_derive", - "sha2 0.10.7", + "sha2", "sha3", "thiserror", ] @@ -1186,9 +1212,9 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" +checksum = "f057a694a54f12365049b0958a1685bb52d567f5593b355fbf685838e873d400" dependencies = [ "crossbeam-utils", ] @@ -1207,9 +1233,9 @@ dependencies = [ [[package]] name = "const-hex" -version = "1.8.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08849ed393c907c90016652a01465a12d86361cd38ad2a7de026c56a520cc259" +checksum = "c37be52ef5e3b394db27a2341010685ad5103c72ac15ce2e9420a7e8f93f342c" dependencies = [ "cfg-if", "cpufeatures", @@ -1219,24 +1245,24 @@ dependencies = [ [[package]] name = "const-oid" -version = "0.9.2" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" +checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" [[package]] name = "const_format" -version = "0.2.31" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c990efc7a285731f9a4378d81aff2f0e85a2c8781a05ef0f8baa8dac54d0ff48" +checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673" dependencies = [ "const_format_proc_macros", ] [[package]] name = "const_format_proc_macros" -version = "0.2.31" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e026b6ce194a874cb9cf32cd5772d1ef9767cc8fcb5765948d74f37a9d8b2bf6" +checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" dependencies = [ "proc-macro2", "quote", @@ -1273,9 +1299,9 @@ checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "cpufeatures" -version = "0.2.7" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" dependencies = [ "libc", ] @@ -1327,9 +1353,9 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.14" +version = "0.9.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" dependencies = [ "autocfg", "cfg-if", @@ -1350,9 +1376,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.15" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if", ] @@ -1365,9 +1391,9 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "crypto-bigint" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15" +checksum = "740fe28e594155f10cfc383984cbefd529d7396050557148f79cb0f621204124" dependencies = [ "generic-array", "rand_core 0.6.4", @@ -1439,7 +1465,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.28", + "syn 2.0.38", ] [[package]] @@ -1461,17 +1487,17 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core 0.20.3", "quote", - "syn 2.0.28", + "syn 2.0.38", ] [[package]] name = "dashmap" -version = "5.4.0" +version = "5.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", - "hashbrown 0.12.3", + "hashbrown 0.14.2", "lock_api", "once_cell", "parking_lot_core", @@ -1498,21 +1524,31 @@ dependencies = [ [[package]] name = "deadpool-runtime" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaa37046cc0f6c3cc6090fbdbf73ef0b8ef4cfcc37f6befc0020f63e8cf121e1" +checksum = "63dfa964fe2a66f3fde91fc70b267fe193d822c7e603e2a675a49a7f46ad3f49" [[package]] name = "der" -version = "0.7.6" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56acb310e15652100da43d130af8d97b509e95af61aab1c5a7939ef24337ee17" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" dependencies = [ "const-oid", "pem-rfc7468", "zeroize", ] +[[package]] +name = "deranged" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" +dependencies = [ + "powerfmt", + "serde", +] + [[package]] name = "derivative" version = "2.2.0" @@ -1612,7 +1648,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -1640,9 +1676,9 @@ checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" [[package]] name = "ecdsa" -version = "0.16.7" +version = "0.16.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0997c976637b606099b9985693efa3581e84e41f5c11ba5255f88711058ad428" +checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" dependencies = [ "der", "digest 0.10.7", @@ -1654,18 +1690,18 @@ dependencies = [ [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" dependencies = [ "serde", ] [[package]] name = "elliptic-curve" -version = "0.13.5" +version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" +checksum = "d97ca172ae9dc9f9b779a6e3a65d308f2af74e5b8c921299075bdb4a0370e914" dependencies = [ "base16ct", "crypto-bigint", @@ -1691,20 +1727,20 @@ dependencies = [ [[package]] name = "encoding_rs" -version = "0.8.32" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" dependencies = [ "cfg-if", ] [[package]] name = "enr" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0be7b2ac146c1f99fe245c02d16af0696450d8e06c135db75e10eeb9e642c20d" +checksum = "fe81b5c06ecfdbc71dd845216f225f53b62a10cb8a16c946836a3467f701d05b" dependencies = [ - "base64 0.21.2", + "base64 0.21.4", "bytes", "hex", "k256", @@ -1712,7 +1748,6 @@ dependencies = [ "rand 0.8.5", "rlp", "serde", - "serde-hex", "sha3", "zeroize", ] @@ -1738,23 +1773,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" dependencies = [ - "cc", "libc", + "windows-sys", ] [[package]] @@ -1765,7 +1789,7 @@ checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" dependencies = [ "cfg-if", "home", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -1784,7 +1808,7 @@ dependencies = [ "scrypt", "serde", "serde_json", - "sha2 0.10.7", + "sha2", "sha3", "thiserror", "uuid 0.8.2", @@ -1904,8 +1928,8 @@ dependencies = [ "reqwest", "serde", "serde_json", - "syn 2.0.28", - "toml 0.7.4", + "syn 2.0.38", + "toml 0.7.8", "walkdir", ] @@ -1922,7 +1946,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.28", + "syn 2.0.38", ] [[package]] @@ -1948,7 +1972,7 @@ dependencies = [ "serde", "serde_json", "strum 0.25.0", - "syn 2.0.28", + "syn 2.0.38", "tempfile", "thiserror", "tiny-keccak", @@ -1963,7 +1987,7 @@ checksum = "0e53451ea4a8128fbce33966da71132cf9e1040dcfd2a2084fd7733ada7b2045" dependencies = [ "ethers-core", "reqwest", - "semver 1.0.17", + "semver 1.0.20", "serde", "serde_json", "thiserror", @@ -2005,7 +2029,7 @@ checksum = "6838fa110e57d572336178b7c79e94ff88ef976306852d8cb87d9e5b1fc7c0b5" dependencies = [ "async-trait", "auto_impl", - "base64 0.21.2", + "base64 0.21.4", "bytes", "const-hex", "enr", @@ -2024,7 +2048,7 @@ dependencies = [ "serde_json", "thiserror", "tokio", - "tokio-tungstenite 0.20.0", + "tokio-tungstenite 0.20.1", "tracing", "tracing-futures", "url", @@ -2048,7 +2072,7 @@ dependencies = [ "eth-keystore", "ethers-core", "rand 0.8.5", - "sha2 0.10.7", + "sha2", "thiserror", "tracing", ] @@ -2072,7 +2096,7 @@ dependencies = [ "path-slash", "rayon", "regex", - "semver 1.0.17", + "semver 1.0.20", "serde", "serde_json", "solang-parser", @@ -2133,9 +2157,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "fastrlp" @@ -2167,8 +2191,8 @@ dependencies = [ "darling 0.20.3", "proc-macro2", "quote", - "syn 2.0.28", - "uuid 1.4.1", + "syn 2.0.38", + "uuid 1.5.0", ] [[package]] @@ -2213,9 +2237,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.26" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" dependencies = [ "crc32fast", "miniz_oxide", @@ -2370,7 +2394,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.38", ] [[package]] @@ -2446,22 +2470,20 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", - "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", - "wasm-bindgen", ] [[package]] name = "gimli" -version = "0.27.3" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" [[package]] name = "glob" @@ -2469,6 +2491,19 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "globset" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759c97c1e17c55525b57192c06a267cda0ac5210b222d6b82189a2338fa1c13d" +dependencies = [ + "aho-corasick", + "bstr", + "fnv", + "log", + "regex", +] + [[package]] name = "gloo-timers" version = "0.2.6" @@ -2484,7 +2519,7 @@ dependencies = [ [[package]] name = "graphql" version = "0.1.0" -source = "git+https://github.com/edgeandnode/toolshed?branch=main#82313926248990a9c823efe4c9e6b6a7890c7678" +source = "git+https://github.com/edgeandnode/toolshed?branch=main#e9f344633ef0d78a3209fac295de57c6397bd009" dependencies = [ "firestorm", "graphql-parser", @@ -2514,9 +2549,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.19" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d357c7ae988e7d2182f7d7871d0b963962420b0678b0997ce7de72001aeab782" +checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" dependencies = [ "bytes", "fnv", @@ -2556,9 +2591,18 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.3", +] + +[[package]] +name = "hashbrown" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" dependencies = [ "ahash 0.8.3", "allocator-api2", @@ -2579,17 +2623,16 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" dependencies = [ - "hashbrown 0.14.0", + "hashbrown 0.14.2", ] [[package]] name = "headers" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" +checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" dependencies = [ - "base64 0.13.1", - "bitflags 1.3.2", + "base64 0.21.4", "bytes", "headers-core", "http", @@ -2627,9 +2670,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" [[package]] name = "hex" @@ -2670,7 +2713,7 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -2697,9 +2740,9 @@ dependencies = [ [[package]] name = "http-range-header" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" +checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" [[package]] name = "http-types" @@ -2730,9 +2773,9 @@ checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humantime" @@ -2766,13 +2809,16 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.24.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0646026eb1b3eea4cd9ba47912ea5ce9cc07713d105b1a14698f4e6433d348b7" +checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" dependencies = [ + "futures-util", "http", "hyper", + "log", "rustls", + "rustls-native-certs", "tokio", "tokio-rustls", ] @@ -2792,16 +2838,16 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.57" +version = "0.1.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows", + "windows-core", ] [[package]] @@ -2898,7 +2944,7 @@ dependencies = [ "serde", "serde_json", "sqlx", - "tap_core", + "tap_core 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "test-log", "tokio", "toolshed", @@ -2906,6 +2952,39 @@ dependencies = [ "wiremock", ] +[[package]] +name = "indexer_tap_agent" +version = "0.1.0" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "anyhow", + "async-trait", + "clap", + "confy", + "dotenvy", + "ethereum-types", + "ethers-signers", + "eventuals", + "indexer-common", + "jsonrpsee 0.20.2", + "lazy_static", + "reqwest", + "serde", + "serde_json", + "serde_yaml", + "sqlx", + "tap_aggregator", + "tap_core 0.6.0 (git+https://github.com/semiotic-ai/timeline-aggregation-protocol.git?rev=882ca394444b451538908b9996bf7d45869a1bb9)", + "tempfile", + "thiserror", + "tokio", + "toolshed", + "tracing", + "tracing-subscriber", + "wiremock", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -2919,12 +2998,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.0.0" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" dependencies = [ "equivalent", - "hashbrown 0.14.0", + "hashbrown 0.14.2", "serde", ] @@ -2952,33 +3031,21 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi 0.3.1", - "libc", - "windows-sys 0.48.0", -] - [[package]] name = "ipnet" -version = "2.7.2" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" [[package]] name = "is-terminal" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ - "hermit-abi 0.3.1", - "io-lifetimes", - "rustix 0.37.19", - "windows-sys 0.48.0", + "hermit-abi 0.3.3", + "rustix", + "windows-sys", ] [[package]] @@ -3001,72 +3068,235 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "jobserver" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.63" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] [[package]] -name = "jsonwebtoken" -version = "8.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" -dependencies = [ - "base64 0.21.2", - "pem", - "ring", - "serde", - "serde_json", - "simple_asn1", -] - -[[package]] -name = "k256" -version = "0.13.1" +name = "jsonrpsee" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +checksum = "1822d18e4384a5e79d94dc9e4d1239cfa9fad24e55b44d2efeff5b394c9fece4" dependencies = [ - "cfg-if", - "ecdsa", - "elliptic-curve", - "once_cell", - "sha2 0.10.7", - "signature", + "jsonrpsee-core 0.18.2", + "jsonrpsee-proc-macros 0.18.2", + "jsonrpsee-server", + "jsonrpsee-types 0.18.2", + "tracing", ] [[package]] -name = "keccak" -version = "0.1.4" +name = "jsonrpsee" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +checksum = "de902baa44bf34a58b1a4906f8b840d7d60dcec5f41fe08b4dbc14cf9efa821c" dependencies = [ - "cpufeatures", + "jsonrpsee-core 0.20.2", + "jsonrpsee-http-client", + "jsonrpsee-proc-macros 0.20.2", + "jsonrpsee-types 0.20.2", + "tracing", ] [[package]] -name = "keccak-hash" -version = "0.10.0" +name = "jsonrpsee-core" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b286e6b663fb926e1eeb68528e69cb70ed46c6d65871a21b2215ae8154c6d3c" +checksum = "64c6832a55f662b5a6ecc844db24b8b9c387453f923de863062c60ce33d62b81" dependencies = [ - "primitive-types", + "anyhow", + "async-trait", + "beef", + "futures-util", + "globset", + "hyper", + "jsonrpsee-types 0.18.2", + "parking_lot", + "rand 0.8.5", + "rustc-hash", + "serde", + "serde_json", + "soketto", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "jsonrpsee-core" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51f45d37af23707750136379f6799e76ebfcf2d425ec4e36d0deb7921da5e65c" +dependencies = [ + "anyhow", + "async-trait", + "beef", + "futures-util", + "hyper", + "jsonrpsee-types 0.20.2", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "jsonrpsee-http-client" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02308562f2e8162a32f8d6c3dc19c29c858d5d478047c886a5c3c25b5f7fa868" +dependencies = [ + "async-trait", + "hyper", + "hyper-rustls", + "jsonrpsee-core 0.20.2", + "jsonrpsee-types 0.20.2", + "serde", + "serde_json", + "thiserror", + "tokio", + "tower", + "tracing", + "url", +] + +[[package]] +name = "jsonrpsee-proc-macros" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6027ac0b197ce9543097d02a290f550ce1d9432bf301524b013053c0b75cc94" +dependencies = [ + "heck", + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "jsonrpsee-proc-macros" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26b3675a943d083d0bf6e367ec755dccec56c41888afa13b191c1c4ff87c652" +dependencies = [ + "heck", + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "jsonrpsee-server" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f06661d1a6b6e5b85469dc9c29acfbb9b3bb613797a6fd10a3ebb8a70754057" +dependencies = [ + "futures-util", + "hyper", + "jsonrpsee-core 0.18.2", + "jsonrpsee-types 0.18.2", + "serde", + "serde_json", + "soketto", + "tokio", + "tokio-stream", + "tokio-util", + "tower", + "tracing", +] + +[[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.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05eaff23af19f10ba6fbb76519bed6da4d3b9bbaef13d39b7c2b6c14e532d27e" +dependencies = [ + "anyhow", + "beef", + "serde", + "serde_json", + "thiserror", + "tracing", +] + +[[package]] +name = "jsonwebtoken" +version = "8.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" +dependencies = [ + "base64 0.21.4", + "pem", + "ring", + "serde", + "serde_json", + "simple_asn1", +] + +[[package]] +name = "k256" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2", + "signature", +] + +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "keccak-hash" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b286e6b663fb926e1eeb68528e69cb70ed46c6d65871a21b2215ae8154c6d3c" +dependencies = [ + "primitive-types", "tiny-keccak", ] @@ -3109,15 +3339,15 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.147" +version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "libm" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "libsqlite3-sys" @@ -3132,21 +3362,15 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.3.8" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" - -[[package]] -name = "linux-raw-sys" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" +checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" [[package]] name = "lock_api" -version = "0.4.9" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", @@ -3164,7 +3388,7 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a83fb7698b3643a0e34f9ae6f2e8f0178c0fd42f8b59d493aa271ff3a5bf21" dependencies = [ - "hashbrown 0.14.0", + "hashbrown 0.14.2", ] [[package]] @@ -3192,31 +3416,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73cbba799671b762df5a175adf59ce145165747bb891505c43d09aefbbf38beb" [[package]] -name = "maybe-uninit" -version = "2.0.0" +name = "matchit" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" [[package]] name = "md-5" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" dependencies = [ + "cfg-if", "digest 0.10.7", ] [[package]] name = "memchr" -version = "2.6.3" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "memoffset" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" dependencies = [ "autocfg", ] @@ -3304,7 +3529,7 @@ checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -3355,12 +3580,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" -[[package]] -name = "nodrop" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" - [[package]] name = "nom" version = "7.1.3" @@ -3383,9 +3602,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" dependencies = [ "autocfg", "num-integer", @@ -3405,7 +3624,7 @@ dependencies = [ "num-iter", "num-traits", "rand 0.8.5", - "smallvec 1.10.0", + "smallvec", "zeroize", ] @@ -3432,9 +3651,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", "libm", @@ -3446,7 +3665,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.1", + "hermit-abi 0.3.3", "libc", ] @@ -3468,14 +3687,14 @@ dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.38", ] [[package]] name = "object" -version = "0.31.1" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" dependencies = [ "memchr", ] @@ -3519,11 +3738,11 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.54" +version = "0.10.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69b3f656a17a6cbc115b5c7a40c616947d213ba182135b014d6051b73ab6f019" +checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.1", "cfg-if", "foreign-types", "libc", @@ -3540,7 +3759,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.38", ] [[package]] @@ -3551,9 +3770,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.88" +version = "0.9.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ce0f250f34a308dcfdbb351f511359857d4ed2134ba715a4eadd46e1ffd617" +checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" dependencies = [ "cc", "libc", @@ -3632,12 +3851,12 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "parity-scale-codec" -version = "3.5.0" +version = "3.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ddb756ca205bd108aee3c62c6d3c994e1df84a59b9d6d4a5ea42ee1fd5a9a28" +checksum = "0dec8a8073036902368c2cdc0387e85ff9a37054d7e7c98e592145e0c92cd4fb" dependencies = [ "arrayvec", - "bitvec 1.0.1", + "bitvec", "byte-slice-cast", "impl-trait-for-tuples", "parity-scale-codec-derive", @@ -3646,9 +3865,9 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.1.4" +version = "3.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b" +checksum = "312270ee71e1cd70289dacf597cab7b207aa107d2f28191c2ae45b2ece18a260" dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", @@ -3658,9 +3877,9 @@ dependencies = [ [[package]] name = "parking" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] name = "parking_lot" @@ -3674,15 +3893,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.7" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.2.16", - "smallvec 1.10.0", - "windows-sys 0.45.0", + "redox_syscall 0.4.1", + "smallvec", + "windows-targets", ] [[package]] @@ -3717,14 +3936,14 @@ dependencies = [ "digest 0.10.7", "hmac", "password-hash", - "sha2 0.10.7", + "sha2", ] [[package]] name = "pbkdf2" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0ca0b5a68607598bf3bad68f32227a8164f6254833f84eafaac409cd6746c31" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ "digest 0.10.7", "hmac", @@ -3756,19 +3975,20 @@ checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pest" -version = "2.6.0" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e68e84bfb01f0507134eac1e9b410a12ba379d064eab48c50ba4ce329a527b70" +checksum = "c022f1e7b65d6a24c0dbbd5fb344c66881bc01f3e5ae74a1c8100f2f985d98a4" dependencies = [ + "memchr", "thiserror", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.5.7" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be99c4c1d2fc2769b1d00239431d711d08f6efedcecb8b6e30707160aee99c15" +checksum = "35513f630d46400a977c4cb58f78e1bfbe01434316e60c37d27b9ad6139c66d8" dependencies = [ "pest", "pest_generator", @@ -3776,36 +3996,36 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.5.7" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e56094789873daa36164de2e822b3888c6ae4b4f9da555a1103587658c805b1e" +checksum = "bc9fc1b9e7057baba189b5c626e2d6f40681ae5b6eb064dc7c7834101ec8123a" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.38", ] [[package]] name = "pest_meta" -version = "2.5.7" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6733073c7cff3d8459fda0e42f13a047870242aed8b509fe98000928975f359e" +checksum = "1df74e9e7ec4053ceb980e7c0c8bd3594e977fde1af91daba9c928e8e8c6708d" dependencies = [ "once_cell", "pest", - "sha2 0.10.7", + "sha2", ] [[package]] name = "petgraph" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" +checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", - "indexmap 1.9.3", + "indexmap 2.0.2", ] [[package]] @@ -3820,35 +4040,35 @@ dependencies = [ [[package]] name = "phf" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" dependencies = [ "phf_macros", - "phf_shared 0.11.1", + "phf_shared 0.11.2", ] [[package]] name = "phf_generator" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1181c94580fa345f50f19d738aaa39c0ed30a600d95cb2d3e23f94266f14fbf" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" dependencies = [ - "phf_shared 0.11.1", + "phf_shared 0.11.2", "rand 0.8.5", ] [[package]] name = "phf_macros" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92aacdc5f16768709a569e913f7451034034178b05bdc8acda226659a3dccc66" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" dependencies = [ "phf_generator", - "phf_shared 0.11.1", + "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.38", ] [[package]] @@ -3862,31 +4082,31 @@ dependencies = [ [[package]] name = "phf_shared" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" dependencies = [ "siphasher", ] [[package]] name = "pin-project" -version = "1.1.0" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.0" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.38", ] [[package]] @@ -3934,14 +4154,20 @@ version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e30165d31df606f5726b090ec7592c308a0eaf61721ff64c9a3018e344a8753e" dependencies = [ - "portable-atomic 1.3.3", + "portable-atomic 1.4.3", ] [[package]] name = "portable-atomic" -version = "1.3.3" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "767eb9f07d4a5ebcb39bbf2d452058a93c011373abf6832e24194a1c3f004794" +checksum = "31114a898e107c51bb1609ffaf55a0e011cf6a4d7f1170d0015a165082c0338b" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" @@ -3957,19 +4183,19 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "prettyplease" -version = "0.2.6" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b69d39aab54d069e7f2fe8cb970493e7834601ca2d8c65fd7bbd183578080d1" +checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" dependencies = [ "proc-macro2", - "syn 2.0.28", + "syn 2.0.38", ] [[package]] name = "primitive-types" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" dependencies = [ "fixed-hash", "impl-codec", @@ -4024,9 +4250,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] @@ -4048,19 +4274,19 @@ dependencies = [ [[package]] name = "proptest" -version = "1.2.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" +checksum = "7c003ac8c77cb07bb74f5f198bce836a689bcd5a42574612bf14d17bfd08c20e" dependencies = [ "bit-set", - "bitflags 1.3.2", - "byteorder", + "bit-vec", + "bitflags 2.4.1", "lazy_static", "num-traits", "rand 0.8.5", "rand_chacha 0.3.1", "rand_xorshift", - "regex-syntax 0.6.29", + "regex-syntax 0.7.5", "rusty-fork", "tempfile", "unarray", @@ -4116,19 +4342,13 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.32" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] -[[package]] -name = "radium" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "def50a86306165861203e7f84ecffbbdfdea79f0e51039b33de1e952358c47ac" - [[package]] name = "radium" version = "0.7.0" @@ -4194,7 +4414,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.9", + "getrandom 0.2.10", ] [[package]] @@ -4226,9 +4446,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" dependencies = [ "either", "rayon-core", @@ -4236,14 +4456,12 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" dependencies = [ - "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", - "num_cpus", ] [[package]] @@ -4264,27 +4482,36 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_users" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ - "getrandom 0.2.9", + "getrandom 0.2.10", "redox_syscall 0.2.16", "thiserror", ] [[package]] name = "regex" -version = "1.9.5" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.3.8", - "regex-syntax 0.7.5", + "regex-automata 0.4.3", + "regex-syntax 0.8.2", ] [[package]] @@ -4298,13 +4525,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.8" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.7.5", + "regex-syntax 0.8.2", ] [[package]] @@ -4319,22 +4546,28 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + [[package]] name = "rend" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581008d2099240d37fb08d77ad713bcaec2c4d89d50b5b21a8bb1996bbab68ab" +checksum = "a2571463863a6bd50c32f94402933f03457a3fbaf697a707c5be741e459f08fd" dependencies = [ "bytecheck", ] [[package]] name = "reqwest" -version = "0.11.20" +version = "0.11.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" +checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" dependencies = [ - "base64 0.21.2", + "base64 0.21.4", "bytes", "encoding_rs", "futures-core", @@ -4358,6 +4591,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", + "system-configuration", "tokio", "tokio-native-tls", "tokio-rustls", @@ -4366,7 +4600,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots 0.25.2", + "webpki-roots", "winreg", ] @@ -4416,7 +4650,7 @@ version = "0.7.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0200c8230b013893c0b2d6213d6ec64ed2b9be2e0e016682b7224ff82cff5c58" dependencies = [ - "bitvec 1.0.1", + "bitvec", "bytecheck", "hashbrown 0.12.3", "ptr_meta", @@ -4424,7 +4658,7 @@ dependencies = [ "rkyv_derive", "seahash", "tinyvec", - "uuid 1.4.1", + "uuid 1.5.0", ] [[package]] @@ -4559,6 +4793,12 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc-hex" version = "2.1.0" @@ -4580,34 +4820,20 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.17", -] - -[[package]] -name = "rustix" -version = "0.37.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys 0.3.8", - "windows-sys 0.48.0", + "semver 1.0.20", ] [[package]] name = "rustix" -version = "0.38.13" +version = "0.38.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7db8590df6dfcd144d22afd1b83b36c21a18d7cbc1dc4bb5295a8712e9eb662" +checksum = "67ce50cb2e16c2903e30d1cbccfd8387a74b9d4c938b6a4c5ec6cc7556f7a8a0" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "errno", "libc", - "linux-raw-sys 0.4.7", - "windows-sys 0.48.0", + "linux-raw-sys", + "windows-sys", ] [[package]] @@ -4618,34 +4844,36 @@ checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" dependencies = [ "log", "ring", - "rustls-webpki 0.101.5", + "rustls-webpki", "sct", ] [[package]] -name = "rustls-pemfile" -version = "1.0.2" +name = "rustls-native-certs" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" dependencies = [ - "base64 0.21.2", + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework", ] [[package]] -name = "rustls-webpki" -version = "0.100.1" +name = "rustls-pemfile" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b" +checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" dependencies = [ - "ring", - "untrusted", + "base64 0.21.4", ] [[package]] name = "rustls-webpki" -version = "0.101.5" +version = "0.101.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45a27e3b59326c16e23d30aeb7a36a24cc0d29e71d68ff611cdfb4a01d013bed" +checksum = "3c7d5dece342910d9ba34d259310cae3e0154b873b35408b787b59bce53d34fe" dependencies = [ "ring", "untrusted", @@ -4653,9 +4881,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "rusty-fork" @@ -4671,9 +4899,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "salsa20" @@ -4695,9 +4923,9 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.7.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b569c32c806ec3abdf3b5869fb8bf1e0d275a7c1c9b0b05603d9464632649edf" +checksum = "35c0a159d0c45c12b20c5a844feb1fe4bea86e28f17b92a5f0c42193634d3782" dependencies = [ "cfg-if", "derive_more", @@ -4707,9 +4935,9 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.6.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53012eae69e5aa5c14671942a5dd47de59d4cdcff8532a6dd0e081faf1119482" +checksum = "912e55f6d20e0e80d63733872b40e1227c0bce1e1ab81ba67d696339bfd7fd29" dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", @@ -4719,18 +4947,18 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys 0.42.0", + "windows-sys", ] [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "scrypt" @@ -4741,7 +4969,7 @@ dependencies = [ "hmac", "pbkdf2 0.11.0", "salsa20", - "sha2 0.10.7", + "sha2", ] [[package]] @@ -4762,9 +4990,9 @@ checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" [[package]] name = "sec1" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0aec48e813d6b90b15f0b8948af3c63483992dee44c03e9930b3eebdabe046e" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ "base16ct", "der", @@ -4794,9 +5022,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.9.1" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ "bitflags 1.3.2", "core-foundation", @@ -4807,9 +5035,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys", "libc", @@ -4826,9 +5054,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.17" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" dependencies = [ "serde", ] @@ -4856,33 +5084,22 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.188" +version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" dependencies = [ "serde_derive", ] -[[package]] -name = "serde-hex" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca37e3e4d1b39afd7ff11ee4e947efae85adfddf4841787bfa47c470e96dc26d" -dependencies = [ - "array-init", - "serde", - "smallvec 0.6.14", -] - [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.38", ] [[package]] @@ -4896,6 +5113,16 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335" +dependencies = [ + "itoa", + "serde", +] + [[package]] name = "serde_qs" version = "0.8.5" @@ -4909,9 +5136,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93107647184f6027e3b7dcb2e11034cf95ffa1e3a682c67951963ac69c1c007d" +checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" dependencies = [ "serde", ] @@ -4930,15 +5157,15 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ca3b16a3d82c4088f343b7480a93550b3eabe1a358569c2dfe38bbcead07237" +checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23" dependencies = [ - "base64 0.21.2", + "base64 0.21.4", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.0.0", + "indexmap 2.0.2", "serde", "serde_json", "serde_with_macros", @@ -4947,14 +5174,27 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e6be15c453eb305019bfa438b1593c731f36a289a7853f7707ee29e870b3b3c" +checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788" dependencies = [ "darling 0.20.3", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.38", +] + +[[package]] +name = "serde_yaml" +version = "0.9.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a49e178e4452f45cb61d0cd8cebc1b0fafd3e41929e996cef79aa3aca91f574" +dependencies = [ + "indexmap 2.0.2", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", ] [[package]] @@ -4967,7 +5207,7 @@ dependencies = [ "async-graphql 4.0.16", "async-graphql-axum", "autometrics", - "axum", + "axum 0.5.17", "cargo-husky", "clap", "confy", @@ -4990,14 +5230,14 @@ dependencies = [ "serde_json", "sha3", "sqlx", - "tap_core", + "tap_core 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "test-log", "thiserror", "tokio", - "toml 0.7.4", + "toml 0.7.8", "toolshed", "tower", - "tower-http 0.4.0", + "tower-http 0.4.4", "tracing", "tracing-subscriber", "wiremock", @@ -5005,20 +5245,22 @@ dependencies = [ [[package]] name = "sha-1" -version = "0.10.1" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" +checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" dependencies = [ + "block-buffer 0.9.0", "cfg-if", "cpufeatures", - "digest 0.10.7", + "digest 0.9.0", + "opaque-debug", ] [[package]] -name = "sha1" -version = "0.10.5" +name = "sha-1" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" dependencies = [ "cfg-if", "cpufeatures", @@ -5026,23 +5268,21 @@ dependencies = [ ] [[package]] -name = "sha2" -version = "0.9.9" +name = "sha1" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ - "block-buffer 0.9.0", "cfg-if", "cpufeatures", - "digest 0.9.0", - "opaque-debug", + "digest 0.10.7", ] [[package]] name = "sha2" -version = "0.10.7" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", @@ -5061,9 +5301,9 @@ dependencies = [ [[package]] name = "sharded-slab" -version = "0.1.4" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ "lazy_static", ] @@ -5107,9 +5347,9 @@ dependencies = [ [[package]] name = "siphasher" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "sketches-ddsketch" @@ -5119,27 +5359,18 @@ checksum = "68a406c1882ed7f29cd5e248c9848a80e7cb6ae0fea82346d2746f2f941c07e1" [[package]] name = "slab" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "smallvec" -version = "0.6.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0" -dependencies = [ - "maybe-uninit", -] - -[[package]] -name = "smallvec" -version = "1.10.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" [[package]] name = "smol_str" @@ -5167,7 +5398,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys", +] + +[[package]] +name = "soketto" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d1c5305e39e09653383c2c7244f2f78b3bcae37cf50c64cb4789c9f5096ec2" +dependencies = [ + "base64 0.13.1", + "bytes", + "futures", + "http", + "httparse", + "log", + "rand 0.8.5", + "sha-1 0.9.8", ] [[package]] @@ -5256,7 +5503,7 @@ dependencies = [ "futures-util", "hashlink", "hex", - "indexmap 2.0.0", + "indexmap 2.0.2", "log", "memchr", "once_cell", @@ -5265,8 +5512,8 @@ dependencies = [ "rust_decimal", "serde", "serde_json", - "sha2 0.10.7", - "smallvec 1.10.0", + "sha2", + "smallvec", "sqlformat", "thiserror", "time", @@ -5304,7 +5551,7 @@ dependencies = [ "quote", "serde", "serde_json", - "sha2 0.10.7", + "sha2", "sqlx-core", "sqlx-mysql", "sqlx-postgres", @@ -5322,9 +5569,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "864b869fdf56263f4c95c45483191ea0af340f9f3e3e7b4d57a61c7c87a970db" dependencies = [ "atoi", - "base64 0.21.2", + "base64 0.21.4", "bigdecimal", - "bitflags 2.4.0", + "bitflags 2.4.1", "byteorder", "bytes", "crc", @@ -5350,8 +5597,8 @@ dependencies = [ "rust_decimal", "serde", "sha1", - "sha2 0.10.7", - "smallvec 1.10.0", + "sha2", + "smallvec", "sqlx-core", "stringprep", "thiserror", @@ -5367,9 +5614,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb7ae0e6a97fb3ba33b23ac2671a5ce6e3cabe003f451abd5a56e7951d975624" dependencies = [ "atoi", - "base64 0.21.2", + "base64 0.21.4", "bigdecimal", - "bitflags 2.4.0", + "bitflags 2.4.1", "byteorder", "crc", "dotenvy", @@ -5393,8 +5640,8 @@ dependencies = [ "serde", "serde_json", "sha1", - "sha2 0.10.7", - "smallvec 1.10.0", + "sha2", + "smallvec", "sqlx-core", "stringprep", "thiserror", @@ -5467,6 +5714,9 @@ name = "strum" version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +dependencies = [ + "strum_macros 0.24.3", +] [[package]] name = "strum" @@ -5474,7 +5724,7 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" dependencies = [ - "strum_macros 0.25.2", + "strum_macros 0.25.3", ] [[package]] @@ -5492,15 +5742,15 @@ dependencies = [ [[package]] name = "strum_macros" -version = "0.25.2" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad8d03b598d3d0fff69bf533ee3ef19b8eeb342729596df84bcc7e1f96ec4059" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ "heck", "proc-macro2", "quote", "rustversion", - "syn 2.0.28", + "syn 2.0.38", ] [[package]] @@ -5520,10 +5770,10 @@ dependencies = [ "hex", "once_cell", "reqwest", - "semver 1.0.17", + "semver 1.0.20", "serde", "serde_json", - "sha2 0.10.7", + "sha2", "thiserror", "url", "zip", @@ -5542,9 +5792,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.28" +version = "2.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" dependencies = [ "proc-macro2", "quote", @@ -5560,7 +5810,7 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.38", ] [[package]] @@ -5569,12 +5819,61 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tap" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "tap_aggregator" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e29a877040a076bcc3c3a442d2bef48456d9840b5db7c82512d65d249284a71" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "anyhow", + "axum 0.6.20", + "clap", + "ethereum-types", + "ethers-core", + "ethers-signers", + "futures-util", + "jsonrpsee 0.18.2", + "lazy_static", + "log", + "prometheus", + "ruint", + "serde", + "serde_json", + "strum 0.24.1", + "tap_core 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio", + "tracing-subscriber", +] + [[package]] name = "tap_core" version = "0.6.0" @@ -5600,6 +5899,30 @@ dependencies = [ "tokio", ] +[[package]] +name = "tap_core" +version = "0.6.0" +source = "git+https://github.com/semiotic-ai/timeline-aggregation-protocol.git?rev=882ca394444b451538908b9996bf7d45869a1bb9#882ca394444b451538908b9996bf7d45869a1bb9" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "anyhow", + "async-trait", + "ethereum-types", + "ethers", + "ethers-contract", + "ethers-contract-derive", + "ethers-core", + "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.8.0" @@ -5607,10 +5930,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" dependencies = [ "cfg-if", - "fastrand 2.0.0", + "fastrand 2.0.1", "redox_syscall 0.3.5", - "rustix 0.38.13", - "windows-sys 0.48.0", + "rustix", + "windows-sys", ] [[package]] @@ -5626,42 +5949,42 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64" dependencies = [ "winapi-util", ] [[package]] name = "test-log" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9601d162c1d77e62c1ea0bc8116cd1caf143ce3af947536c3c9052a1677fe0c" +checksum = "f66edd6b6cd810743c0c71e1d085e92b01ce6a72782032e3f794c8284fe4bcdd" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.38", ] [[package]] name = "thiserror" -version = "1.0.49" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.49" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.38", ] [[package]] @@ -5676,11 +5999,13 @@ dependencies = [ [[package]] name = "time" -version = "0.3.21" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3403384eaacbca9923fa06940178ac13e4edb725486d70e8e15881d0c836cc" +checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" dependencies = [ + "deranged", "itoa", + "powerfmt", "serde", "time-core", "time-macros", @@ -5688,15 +6013,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.9" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" +checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" dependencies = [ "time-core", ] @@ -5727,9 +6052,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.32.0" +version = "1.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" +checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" dependencies = [ "backtrace", "bytes", @@ -5741,7 +6066,7 @@ dependencies = [ "signal-hook-registry", "socket2 0.5.4", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -5752,7 +6077,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.38", ] [[package]] @@ -5767,9 +6092,9 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.24.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0d409377ff5b1e3ca6437aa86c1eb7d40c134bfec254e44c830defa92669db5" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ "rustls", "tokio", @@ -5800,24 +6125,24 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b2dbec703c26b00d74844519606ef15d09a7d6857860f84ad223dec002ddea2" +checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" dependencies = [ "futures-util", "log", "rustls", "tokio", "tokio-rustls", - "tungstenite 0.20.0", - "webpki-roots 0.23.1", + "tungstenite 0.20.1", + "webpki-roots", ] [[package]] name = "tokio-util" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d" dependencies = [ "bytes", "futures-core", @@ -5839,9 +6164,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.7.4" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6135d499e69981f9ff0ef2167955a5333c35e36f6937d382974566b3d5b94ec" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" dependencies = [ "serde", "serde_spanned", @@ -5851,20 +6176,20 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.19.10" +version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 1.9.3", + "indexmap 2.0.2", "serde", "serde_spanned", "toml_datetime", @@ -5874,12 +6199,12 @@ dependencies = [ [[package]] name = "toolshed" version = "0.2.2" -source = "git+https://github.com/edgeandnode/toolshed?branch=main#82313926248990a9c823efe4c9e6b6a7890c7678" +source = "git+https://github.com/edgeandnode/toolshed?branch=main#e9f344633ef0d78a3209fac295de57c6397bd009" dependencies = [ "alloy-primitives", "alloy-sol-types", "async-graphql 6.0.7", - "bs58 0.5.0", + "bs58", "ethers-core", "firestorm", "graphql-parser", @@ -5928,11 +6253,11 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.4.0" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d1d42a9b3f3ec46ba828e8d376aec14592ea199f70a06a548587ecd1c4ab658" +checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.1", "bytes", "futures-core", "futures-util", @@ -5959,11 +6284,10 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "log", "pin-project-lite", "tracing-attributes", @@ -5972,20 +6296,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.24" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.38", ] [[package]] name = "tracing-core" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", "valuable", @@ -6035,7 +6359,7 @@ dependencies = [ "serde", "serde_json", "sharded-slab", - "smallvec 1.10.0", + "smallvec", "thread_local", "tracing", "tracing-core", @@ -6062,7 +6386,7 @@ dependencies = [ "httparse", "log", "rand 0.8.5", - "sha-1", + "sha-1 0.10.1", "thiserror", "url", "utf-8", @@ -6070,9 +6394,9 @@ dependencies = [ [[package]] name = "tungstenite" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e862a1c4128df0112ab625f55cd5c934bcb4312ba80b39ae4b4835a3fd58e649" +checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" dependencies = [ "byteorder", "bytes", @@ -6090,15 +6414,15 @@ dependencies = [ [[package]] name = "typenum" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ucd-trie" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" [[package]] name = "uint" @@ -6126,9 +6450,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.9" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" @@ -6166,6 +6490,12 @@ dependencies = [ "void", ] +[[package]] +name = "unsafe-libyaml" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa" + [[package]] name = "untrusted" version = "0.7.1" @@ -6202,17 +6532,17 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" dependencies = [ - "getrandom 0.2.9", + "getrandom 0.2.10", "serde", ] [[package]] name = "uuid" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" +checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc" dependencies = [ - "getrandom 0.2.9", + "getrandom 0.2.10", ] [[package]] @@ -6250,15 +6580,15 @@ dependencies = [ [[package]] name = "waker-fn" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" +checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" [[package]] name = "walkdir" -version = "2.3.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" dependencies = [ "same-file", "winapi-util", @@ -6266,11 +6596,10 @@ dependencies = [ [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -6294,9 +6623,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -6304,24 +6633,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.38", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.36" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d1985d03709c53167ce907ff394f5316aa22cb4e12761295c5dc57dacb6297e" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" dependencies = [ "cfg-if", "js-sys", @@ -6331,9 +6660,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -6341,42 +6670,33 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.38", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "web-sys" -version = "0.3.63" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", ] -[[package]] -name = "webpki-roots" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03058f88386e5ff5310d9111d53f48b17d732b401aeb83a8d5190f2ac459338" -dependencies = [ - "rustls-webpki 0.100.1", -] - [[package]] name = "webpki-roots" version = "0.25.2" @@ -6407,9 +6727,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ "winapi", ] @@ -6421,36 +6741,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" -dependencies = [ - "windows-targets 0.48.0", -] - -[[package]] -name = "windows-sys" -version = "0.42.0" +name = "windows-core" +version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", + "windows-targets", ] [[package]] @@ -6459,128 +6755,71 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.0", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-targets", ] [[package]] name = "windows-targets" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_msvc" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_i686_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_x86_64_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_msvc" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "winnow" -version = "0.4.6" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699" +checksum = "a3b801d0e0a6726477cc207f60162da452f3a95adb368399bef20a946e06f65c" dependencies = [ "memchr", ] @@ -6592,7 +6831,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ "cfg-if", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -6603,7 +6842,7 @@ checksum = "c6f71803d3a1c80377a06221e0530be02035d5b3e854af56c6ece7ac20ac441d" dependencies = [ "assert-json-diff", "async-trait", - "base64 0.21.2", + "base64 0.21.4", "deadpool", "futures", "futures-timer", @@ -6668,7 +6907,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.38", ] [[package]] @@ -6712,11 +6951,10 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.8+zstd.1.5.5" +version = "2.0.9+zstd.1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" +checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" dependencies = [ "cc", - "libc", "pkg-config", ] diff --git a/Cargo.toml b/Cargo.toml index c0b50447..61b4060f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ members = [ "common", "service", + "tap_agent", ] resolver = "2" diff --git a/common/src/tap_manager.rs b/common/src/tap_manager.rs index ba4a3e18..78db52c8 100644 --- a/common/src/tap_manager.rs +++ b/common/src/tap_manager.rs @@ -7,7 +7,7 @@ use anyhow::anyhow; use ethers_core::types::U256; use eventuals::Eventual; use sqlx::{types::BigDecimal, PgPool}; -use std::{collections::HashMap, sync::Arc}; +use std::{collections::HashMap, str::FromStr, sync::Arc}; use tap_core::tap_manager::SignedReceipt; use tracing::error; @@ -82,14 +82,20 @@ impl TapManager { // TODO: consider doing this in another async task to avoid slowing down the paid query flow. sqlx::query!( r#" - INSERT INTO scalar_tap_receipts (allocation_id, timestamp_ns, receipt) - VALUES ($1, $2, $3) + INSERT INTO scalar_tap_receipts (allocation_id, sender_address, timestamp_ns, value, receipt) + VALUES ($1, $2, $3, $4, $5) "#, format!("{:?}", allocation_id) .strip_prefix("0x") .unwrap() .to_owned(), + receipt_signer + .to_string() + .strip_prefix("0x") + .unwrap() + .to_owned(), BigDecimal::from(receipt.message.timestamp_ns), + BigDecimal::from_str(&receipt.message.value.to_string())?, serde_json::to_value(receipt).map_err(|e| anyhow!(e))? ) .execute(&self.pgpool) @@ -116,7 +122,6 @@ mod test { use tap_core::tap_manager::SignedReceipt; use tap_core::{eip_712_signed_message::EIP712SignedMessage, tap_receipt::Receipt}; - use toolshed::thegraph::DeploymentId; use crate::test_vectors; @@ -166,8 +171,7 @@ mod test { .unwrap() } - #[ignore] - #[sqlx::test] + #[sqlx::test(migrations = "../migrations")] async fn test_verify_and_store_receipt(pgpool: PgPool) { // Listen to pg_notify events let mut listener = PgListener::connect_with(&pgpool).await.unwrap(); @@ -186,7 +190,7 @@ mod test { let allocation = Allocation { id: allocation_id, subgraph_deployment: SubgraphDeployment { - id: DeploymentId::from_str("QmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA").unwrap(), + id: *test_vectors::NETWORK_SUBGRAPH_DEPLOYMENT, denied_at: None, }, status: AllocationStatus::Active, @@ -206,10 +210,8 @@ mod test { )); // Mock escrow accounts - let escrow_accounts = Eventual::from_value(HashMap::from_iter(vec![( - *test_vectors::INDEXER_ADDRESS, - U256::from(123), - )])); + let escrow_accounts = + Eventual::from_value(HashMap::from_iter(vec![(keys().1, U256::from(123))])); let tap_manager = TapManager::new(pgpool.clone(), indexer_allocations, escrow_accounts, domain); diff --git a/migrations/20230912220523_tap_receipts.up.sql b/migrations/20230912220523_tap_receipts.up.sql index a9670d98..c7cd9c22 100644 --- a/migrations/20230912220523_tap_receipts.up.sql +++ b/migrations/20230912220523_tap_receipts.up.sql @@ -1,7 +1,10 @@ CREATE TABLE IF NOT EXISTS scalar_tap_receipts ( id BIGSERIAL PRIMARY KEY, allocation_id CHAR(40) NOT NULL, + sender_address CHAR(40) NOT NULL, timestamp_ns NUMERIC(20) NOT NULL, + -- signature CHAR(130) NOT NULL, + value NUMERIC(39) NOT NULL, receipt JSON NOT NULL ); @@ -9,7 +12,7 @@ CREATE FUNCTION scalar_tap_receipt_notify() RETURNS trigger AS $$ BEGIN - PERFORM pg_notify('scalar_tap_receipt_notification', format('{"id": %s, "allocation_id": "%s", "timestamp_ns": %s}', NEW.id, NEW.allocation_id, NEW.timestamp_ns)); + PERFORM pg_notify('scalar_tap_receipt_notification', format('{"id": %s, "allocation_id": "%s", "sender_address": "%s", "timestamp_ns": %s, "value": %s}', NEW.id, NEW.allocation_id, NEW.sender_address, NEW.timestamp_ns, NEW.value)); RETURN NEW; END; $$ LANGUAGE 'plpgsql'; diff --git a/migrations/20230915230734_tap_ravs.down.sql b/migrations/20230915230734_tap_ravs.down.sql new file mode 100644 index 00000000..31dca119 --- /dev/null +++ b/migrations/20230915230734_tap_ravs.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS scalar_tap_ravs CASCADE; diff --git a/migrations/20230915230734_tap_ravs.up.sql b/migrations/20230915230734_tap_ravs.up.sql new file mode 100644 index 00000000..9e4fc11c --- /dev/null +++ b/migrations/20230915230734_tap_ravs.up.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS scalar_tap_latest_ravs ( + allocation_id CHAR(40) NOT NULL, + sender_address CHAR(40) NOT NULL, + rav JSON NOT NULL, + is_last BOOLEAN DEFAULT FALSE NOT NULL, + PRIMARY KEY (allocation_id, sender_address) +); diff --git a/service/src/main.rs b/service/src/main.rs index 18731e3c..6d676b83 100644 --- a/service/src/main.rs +++ b/service/src/main.rs @@ -150,7 +150,6 @@ async fn main() -> Result<(), std::io::Error> { indexer_management_db.clone(), indexer_allocations, escrow_accounts, - // TODO: arguments for eip712_domain should be a config eip712_domain! { name: "Scalar TAP", version: "1", diff --git a/tap_agent/Cargo.toml b/tap_agent/Cargo.toml new file mode 100644 index 00000000..dcf63e78 --- /dev/null +++ b/tap_agent/Cargo.toml @@ -0,0 +1,46 @@ +[package] +name = "indexer_tap_agent" +version = "0.1.0" +edition = "2021" +publish = false + +[[bin]] +name = "indexer_tap_agent" +path = "src/main.rs" + +[dependencies] +alloy-primitives = "0.4.2" +alloy-sol-types = "0.4.2" +anyhow = "1.0.72" +async-trait = "0.1.72" +clap = { version = "4.4.3", features = ["derive", "env"] } +confy = "0.5.1" +dotenvy = "0.15.7" +ethereum-types = "0.14.1" +eventuals = "0.6.7" +indexer-common = { version = "0.1.0", path = "../common" } +jsonrpsee = { version = "0.20.2", features = ["http-client", "macros"] } +lazy_static = "1.4.0" +reqwest = "0.11.20" +serde = "1.0.188" +serde_json = "1.0.104" +serde_yaml = "0.9.25" +sqlx = { version = "0.7.2", features = ["postgres", "runtime-tokio", "bigdecimal", "rust_decimal"] } +tap_aggregator = "0.1.6" +tap_core = { git = "https://github.com/semiotic-ai/timeline-aggregation-protocol.git", rev = "882ca394444b451538908b9996bf7d45869a1bb9" } +thiserror = "1.0.44" +tokio = { version = "1.33.0" } +toolshed = { git = "https://github.com/edgeandnode/toolshed", branch = "main", features = ["graphql"] } +tracing = "0.1.37" +tracing-subscriber = { version = "0.3", features = [ + "env-filter", + "ansi", + "fmt", + "std", + "json", +] } + +[dev-dependencies] +ethers-signers = "2.0.8" +tempfile = "3.8.0" +wiremock = "0.5.19" diff --git a/tap_agent/src/agent.rs b/tap_agent/src/agent.rs new file mode 100644 index 00000000..cd950cdf --- /dev/null +++ b/tap_agent/src/agent.rs @@ -0,0 +1,89 @@ +// Copyright 2023-, GraphOps and Semiotic Labs. +// SPDX-License-Identifier: Apache-2.0 + +use std::time::Duration; + +use alloy_sol_types::eip712_domain; +use indexer_common::prelude::{ + escrow_accounts, indexer_allocations, DeploymentDetails, SubgraphClient, +}; + +use crate::{aggregator_endpoints, config, database, tap::accounts_manager}; + +pub async fn start_agent(config: &'static config::Cli) { + let pgpool = database::connect(&config.postgres).await; + + let http_client = reqwest::Client::new(); + + let network_subgraph = Box::leak(Box::new(SubgraphClient::new( + http_client.clone(), + config + .network_subgraph + .network_subgraph_deployment + .map(|deployment| { + DeploymentDetails::for_graph_node( + &config.indexer_infrastructure.graph_node_status_endpoint, + &config.indexer_infrastructure.graph_node_query_endpoint, + deployment, + ) + }) + .transpose() + .expect("Failed to parse graph node query endpoint and network subgraph deployment"), + DeploymentDetails::for_query_url(&config.network_subgraph.network_subgraph_endpoint) + .expect("Failed to parse network subgraph endpoint"), + ))); + + let indexer_allocations = indexer_allocations( + network_subgraph, + config.ethereum.indexer_address, + 1, + Duration::from_secs(config.network_subgraph.allocation_syncing_interval), + ); + + let escrow_subgraph = Box::leak(Box::new(SubgraphClient::new( + http_client.clone(), + config + .escrow_subgraph + .escrow_subgraph_deployment + .map(|deployment| { + DeploymentDetails::for_graph_node( + &config.indexer_infrastructure.graph_node_status_endpoint, + &config.indexer_infrastructure.graph_node_query_endpoint, + deployment, + ) + }) + .transpose() + .expect("Failed to parse graph node query endpoint and escrow subgraph deployment"), + DeploymentDetails::for_query_url(&config.escrow_subgraph.escrow_subgraph_endpoint) + .expect("Failed to parse escrow subgraph endpoint"), + ))); + + let escrow_accounts = escrow_accounts( + escrow_subgraph, + config.ethereum.indexer_address, + Duration::from_secs(config.escrow_subgraph.escrow_syncing_interval), + ); + + // TODO: replace with a proper implementation once the gateway registry contract is ready + let sender_aggregator_endpoints = aggregator_endpoints::load_aggregator_endpoints( + config.tap.sender_aggregator_endpoints_file.clone(), + ); + + let tap_eip712_domain_separator = eip712_domain! { + name: "Scalar TAP", + version: "1", + chain_id: config.receipts.receipts_verifier_chain_id, + verifying_contract: config.receipts.receipts_verifier_address, + }; + + let _accounts_manager = accounts_manager::AccountsManager::new( + config, + pgpool, + indexer_allocations, + escrow_accounts, + escrow_subgraph, + tap_eip712_domain_separator, + sender_aggregator_endpoints, + ) + .await; +} diff --git a/tap_agent/src/aggregator_endpoints.rs b/tap_agent/src/aggregator_endpoints.rs new file mode 100644 index 00000000..ebed55d9 --- /dev/null +++ b/tap_agent/src/aggregator_endpoints.rs @@ -0,0 +1,45 @@ +// Copyright 2023-, GraphOps and Semiotic Labs. +// SPDX-License-Identifier: Apache-2.0 + +use std::collections::HashMap; +use std::fs::File; +use std::io::BufReader; +use std::path::PathBuf; + +use alloy_primitives::Address; + +/// Load a hashmap of sender addresses and their corresponding aggregator endpoints +/// from a yaml file. We're using serde_yaml. +pub fn load_aggregator_endpoints(file_path: PathBuf) -> HashMap { + let file = File::open(file_path).unwrap(); + let reader = BufReader::new(file); + let endpoints: HashMap = serde_yaml::from_reader(reader).unwrap(); + endpoints +} + +#[cfg(test)] +mod tests { + use std::{io::Write, str::FromStr}; + + use super::*; + + /// Test that we can load the aggregator endpoints from a yaml file. + /// The test is going to create a temporary yaml file using tempfile, load it, and + /// check that the endpoints are loaded correctly. + #[test] + fn test_load_aggregator_endpoints() { + let named_temp_file = tempfile::NamedTempFile::new().unwrap(); + let mut temp_file = named_temp_file.reopen().unwrap(); + let yaml = r#" + 0xdeadbeefcafebabedeadbeefcafebabedeadbeef: https://example.com/aggregate-receipts + 0x0123456789abcdef0123456789abcdef01234567: https://other.example.com/aggregate-receipts + "#; + temp_file.write_all(yaml.as_bytes()).unwrap(); + let endpoints = load_aggregator_endpoints(named_temp_file.path().to_path_buf()); + assert_eq!( + endpoints + .get(&Address::from_str("0xdeadbeefcafebabedeadbeefcafebabedeadbeef").unwrap()), + Some(&"https://example.com/aggregate-receipts".to_string()) + ); + } +} diff --git a/tap_agent/src/config.rs b/tap_agent/src/config.rs new file mode 100644 index 00000000..56975691 --- /dev/null +++ b/tap_agent/src/config.rs @@ -0,0 +1,320 @@ +// Copyright 2023-, GraphOps and Semiotic Labs. +// SPDX-License-Identifier: Apache-2.0 + +use std::path::PathBuf; + +use alloy_primitives::Address; +use clap::{command, Args, Parser, ValueEnum}; +use dotenvy::dotenv; +use serde::{Deserialize, Serialize}; +use toolshed::thegraph::DeploymentId; +use tracing::subscriber::{set_global_default, SetGlobalDefaultError}; +use tracing_subscriber::{EnvFilter, FmtSubscriber}; + +#[derive(Clone, Debug, Parser, Serialize, Deserialize, Default)] +#[clap( + name = "indexer-tap-agent", + about = "Agent that manages Timeline Aggregation Protocol (TAP) receipts as well \ + as Receipt Aggregate Voucher (RAV) requests for an indexer." +)] +#[command(author, version, about, long_about = None, arg_required_else_help = true)] +pub struct Cli { + #[command(flatten)] + pub ethereum: Ethereum, + #[command(flatten)] + pub receipts: Receipts, + #[command(flatten)] + pub indexer_infrastructure: IndexerInfrastructure, + #[command(flatten)] + pub postgres: Postgres, + #[command(flatten)] + pub network_subgraph: NetworkSubgraph, + #[command(flatten)] + pub escrow_subgraph: EscrowSubgraph, + #[command(flatten)] + pub tap: Tap, + #[arg( + short, + value_name = "config", + env = "CONFIG", + help = "Indexer service configuration file (YAML format)" + )] + pub config: Option, +} + +#[derive(Clone, Debug, Args, Serialize, Deserialize, Default)] +#[group(required = true, multiple = true)] +pub struct Ethereum { + #[clap( + long, + value_name = "mnemonic", + env = "MNEMONIC", + help = "Mnemonic for the operator wallet" + )] + pub mnemonic: String, + #[clap( + long, + value_name = "indexer-address", + env = "INDEXER_ADDRESS", + help = "Ethereum address of the indexer" + )] + pub indexer_address: Address, +} + +#[derive(Clone, Debug, Args, Serialize, Deserialize, Default)] +#[group(required = true, multiple = true)] +pub struct Receipts { + #[clap( + long, + value_name = "receipts-verifier-chain-id", + env = "RECEIPTS_VERIFIER_CHAIN_ID", + help = "Scalar TAP verifier chain ID" + )] + pub receipts_verifier_chain_id: u64, + #[clap( + long, + value_name = "receipts-verifier-address", + env = "RECEIPTS_VERIFIER_ADDRESS", + help = "Scalar TAP verifier contract address" + )] + pub receipts_verifier_address: Address, +} + +#[derive(Clone, Debug, Args, Serialize, Deserialize, Default)] +#[group(required = true, multiple = true)] +pub struct IndexerInfrastructure { + #[clap( + long, + value_name = "metrics-port", + env = "METRICS_PORT", + default_value_t = 7300, + help = "Port to serve Prometheus metrics at" + )] + pub metrics_port: u16, + #[clap( + long, + value_name = "graph-node-query-endpoint", + env = "GRAPH_NODE_QUERY_ENDPOINT", + default_value_t = String::from("http://0.0.0.0:8000"), + help = "Graph node GraphQL HTTP service endpoint", + )] + pub graph_node_query_endpoint: String, + #[clap( + long, + value_name = "graph-node-status-endpoint", + env = "GRAPH_NODE_STATUS_ENDPOINT", + default_value_t = String::from("http://0.0.0.0:8030"), + help = "Graph node endpoint for the index node server", + )] + pub graph_node_status_endpoint: String, + #[clap( + long, + value_name = "log-level", + env = "LOG_LEVEL", + value_enum, + help = "Log level in RUST_LOG format" + )] + pub log_level: Option, +} + +#[derive(Clone, Debug, Args, Serialize, Deserialize, Default)] +#[group(required = true, multiple = true)] +pub struct Postgres { + #[clap( + long, + value_name = "postgres-host", + env = "POSTGRES_HOST", + default_value_t = String::from("http://0.0.0.0/"), + help = "Postgres host", + )] + pub postgres_host: String, + #[clap( + long, + value_name = "postgres-port", + env = "POSTGRES_PORT", + default_value_t = 5432, + help = "Postgres port" + )] + pub postgres_port: usize, + #[clap( + long, + value_name = "postgres-database", + env = "POSTGRES_DATABASE", + help = "Postgres database name" + )] + pub postgres_database: String, + #[clap( + long, + value_name = "postgres-username", + env = "POSTGRES_USERNAME", + default_value_t = String::from("postgres"), + help = "Postgres username", + )] + pub postgres_username: String, + #[clap( + long, + value_name = "postgres-password", + env = "POSTGRES_PASSWORD", + default_value_t = String::from(""), + help = "Postgres password", + )] + pub postgres_password: String, +} + +#[derive(Clone, Debug, Args, Serialize, Deserialize, Default)] +#[group(required = true, multiple = true)] +pub struct NetworkSubgraph { + #[clap( + long, + value_name = "network-subgraph-deployment", + env = "NETWORK_SUBGRAPH_DEPLOYMENT", + help = "Network subgraph deployment" + )] + pub network_subgraph_deployment: Option, + #[clap( + long, + value_name = "network-subgraph-endpoint", + env = "NETWORK_SUBGRAPH_ENDPOINT", + default_value_t = String::from("https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-goerli"), + help = "Endpoint to query the network subgraph from", + )] + pub network_subgraph_endpoint: String, + #[clap( + long, + value_name = "allocation-syncing-interval", + env = "ALLOCATION_SYNCING_INTERVAL", + default_value_t = 120_000, + help = "Interval (in ms) for syncing indexer allocations from the network" + )] + pub allocation_syncing_interval: u64, +} + +#[derive(Clone, Debug, Args, Serialize, Deserialize, Default)] +#[group(required = true, multiple = true)] +pub struct EscrowSubgraph { + #[clap( + long, + value_name = "escrow-subgraph-deployment", + env = "ESCROW_SUBGRAPH_DEPLOYMENT", + help = "Escrow subgraph deployment" + )] + pub escrow_subgraph_deployment: Option, + #[clap( + long, + value_name = "escrow-subgraph-endpoint", + env = "ESCROW_SUBGRAPH_ENDPOINT", + // TODO: + // default_value_t = String::from("https://api.thegraph.com/subgraphs/name/?????????????"), + help = "Endpoint to query the network subgraph from" + )] + pub escrow_subgraph_endpoint: String, + #[clap( + long, + value_name = "escrow-syncing-interval", + env = "ESCROW_SYNCING_INTERVAL", + default_value_t = 120_000, + help = "Interval (in ms) for syncing indexer escrow accounts from the escrow subgraph" + )] + pub escrow_syncing_interval: u64, +} + +#[derive(Clone, Debug, Args, Serialize, Deserialize, Default)] +#[group(required = true, multiple = true)] +pub struct Tap { + #[clap( + long, + value_name = "rav-request-trigger-value", + env = "RAV_REQUEST_TRIGGER_VALUE", + help = "Value of unaggregated fees that triggers a RAV request (in GRT wei).", + default_value_t = 10_000_000_000_000_000_000 // 10 GRT + )] + pub rav_request_trigger_value: u64, + #[clap( + long, + value_name = "rav-request-timestamp-buffer", + env = "RAV_REQUEST_TIMESTAMP_BUFFER", + help = "Buffer (in ns) to add between the current time and the timestamp of the \ + last unaggregated fee when triggering a RAV request.", + default_value_t = 1_000_000_000 // 1 second + )] + pub rav_request_timestamp_buffer_ns: u64, + + // TODO: Remove this whenever the the gateway registry is ready + #[clap( + long, + value_name = "sender-aggregator-endpoints", + env = "SENDER_AGGREGATOR_ENDPOINTS", + help = "YAML file with a map of sender addresses to aggregator endpoints." + )] + pub sender_aggregator_endpoints_file: PathBuf, +} + +/// Sets up tracing, allows log level to be set from the environment variables +fn init_tracing(format: String) -> Result<(), SetGlobalDefaultError> { + let filter = EnvFilter::from_default_env(); + let subscriber_builder: tracing_subscriber::fmt::SubscriberBuilder< + tracing_subscriber::fmt::format::DefaultFields, + tracing_subscriber::fmt::format::Format, + EnvFilter, + > = FmtSubscriber::builder().with_env_filter(filter); + match format.as_str() { + "json" => set_global_default(subscriber_builder.json().finish()), + "full" => set_global_default(subscriber_builder.finish()), + "compact" => set_global_default(subscriber_builder.compact().finish()), + _ => set_global_default(subscriber_builder.with_ansi(true).pretty().finish()), + } +} + +impl Cli { + /// Parse config arguments If environmental variable for config is set to a valid + /// config file path, then parse from config Otherwise parse from command line + /// arguments + pub fn args() -> Self { + dotenv().ok(); + let cli = if let Ok(file_path) = std::env::var("config") { + confy::load_path::(file_path.clone()) + .unwrap_or_else(|_| panic!("Parse config file at {}", file_path.clone())) + } else { + Cli::parse() + // Potentially store it for the user let _ = confy::store_path("./args.toml", + // cli.clone()); + }; + + // Enables tracing under RUST_LOG variable + if let Some(log_setting) = &cli.indexer_infrastructure.log_level { + std::env::set_var("RUST_LOG", log_setting); + }; + + // add a LogFormat to config + init_tracing("pretty".to_string()).expect( + "Could not set up global default subscriber for logger, check \ + environmental variable `RUST_LOG` or the CLI input `log-level`", + ); + cli + } +} + +#[derive(Debug, thiserror::Error)] +pub enum ConfigError { + #[error("Validate the input: {0}")] + ValidateInput(String), + #[error("Generate JSON representation of the config file: {0}")] + GenerateJson(serde_json::Error), + #[error("Toml file error: {0}")] + ReadStr(std::io::Error), + #[error("Unknown error: {0}")] + Other(anyhow::Error), +} + +#[derive( + Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize, Default, +)] +pub enum LogLevel { + Trace, + #[default] + Debug, + Info, + Warn, + Error, + Fatal, +} diff --git a/tap_agent/src/database.rs b/tap_agent/src/database.rs new file mode 100644 index 00000000..9cd8d90e --- /dev/null +++ b/tap_agent/src/database.rs @@ -0,0 +1,32 @@ +// Copyright 2023-, GraphOps and Semiotic Labs. +// SPDX-License-Identifier: Apache-2.0 + +use std::time::Duration; + +use sqlx::{postgres::PgPoolOptions, PgPool}; +use tracing::debug; + +use crate::config; + +pub async fn connect(config: &config::Postgres) -> PgPool { + let url = format!( + "postgresql://{}:{}@{}:{}/{}", + config.postgres_username, + config.postgres_password, + config.postgres_host, + config.postgres_port, + config.postgres_database + ); + debug!( + postgres_host = tracing::field::debug(&config.postgres_host), + postgres_port = tracing::field::debug(&config.postgres_port), + postgres_database = tracing::field::debug(&config.postgres_database), + "Connecting to database" + ); + PgPoolOptions::new() + .max_connections(50) + .acquire_timeout(Duration::from_secs(3)) + .connect(&url) + .await + .expect("Could not connect to DATABASE_URL") +} diff --git a/tap_agent/src/main.rs b/tap_agent/src/main.rs new file mode 100644 index 00000000..df4d3be2 --- /dev/null +++ b/tap_agent/src/main.rs @@ -0,0 +1,44 @@ +// Copyright 2023-, GraphOps and Semiotic Labs. +// SPDX-License-Identifier: Apache-2.0 + +use anyhow::Result; +use lazy_static::lazy_static; +use tokio::signal::unix::{signal, SignalKind}; +use tracing::{debug, info}; + +use crate::config::Cli; + +mod agent; +mod aggregator_endpoints; +mod config; +mod database; +mod tap; + +lazy_static! { + pub static ref CONFIG: Cli = Cli::args(); +} + +#[tokio::main] +async fn main() -> Result<()> { + // Parse basic configurations, also initializes logging. + lazy_static::initialize(&CONFIG); + debug!("Config: {:?}", *CONFIG); + + agent::start_agent(&CONFIG).await; + info!("TAP Agent started."); + + // Have tokio wait for SIGTERM or SIGINT. + let mut signal_sigint = signal(SignalKind::interrupt())?; + let mut signal_sigterm = signal(SignalKind::terminate())?; + tokio::select! { + _ = signal_sigint.recv() => debug !("Received SIGINT."), + _ = signal_sigterm.recv() => debug !("Received SIGTERM."), + } + + // If we're here, we've received a signal to exit. + info!("Shutting down..."); + + // Stop the server and wait for it to finish gracefully. + debug!("Goodbye!"); + Ok(()) +} diff --git a/tap_agent/src/tap/account.rs b/tap_agent/src/tap/account.rs new file mode 100644 index 00000000..03c5a69e --- /dev/null +++ b/tap_agent/src/tap/account.rs @@ -0,0 +1,857 @@ +// Copyright 2023-, GraphOps and Semiotic Labs. +// SPDX-License-Identifier: Apache-2.0 + +use std::{collections::HashMap, sync::Arc}; + +use alloy_primitives::Address; +use alloy_sol_types::Eip712Domain; +use anyhow::ensure; +use ethereum_types::U256; +use eventuals::Eventual; +use indexer_common::prelude::SubgraphClient; +use jsonrpsee::{core::client::ClientT, http_client::HttpClientBuilder, rpc_params}; +use sqlx::PgPool; +use tap_aggregator::jsonrpsee_helpers::JsonRpcResponse; +use tap_core::{ + eip_712_signed_message::EIP712SignedMessage, + receipt_aggregate_voucher::ReceiptAggregateVoucher, tap_manager::RAVRequest, + tap_receipt::ReceiptCheck, +}; +use tokio::{ + sync::{Mutex, MutexGuard}, + task::JoinHandle, +}; +use tracing::{error, warn}; + +use super::accounts_manager::NewReceiptNotification; +use crate::{ + config::{self}, + tap::{ + escrow_adapter::EscrowAdapter, rav_storage_adapter::RAVStorageAdapter, + receipt_checks_adapter::ReceiptChecksAdapter, + receipt_storage_adapter::ReceiptStorageAdapter, + }, +}; + +type TapManager = tap_core::tap_manager::Manager< + EscrowAdapter, + ReceiptChecksAdapter, + ReceiptStorageAdapter, + RAVStorageAdapter, +>; + +#[derive(Default, Debug)] +struct UnaggregatedFees { + pub value: u128, + pub last_id: u64, +} + +#[derive(Debug, PartialEq, Clone, Copy)] +pub enum State { + Running, + LastRavPending, + Finished, +} + +struct Inner { + pgpool: PgPool, + tap_manager: TapManager, + allocation_id: Address, + sender: Address, + sender_aggregator_endpoint: String, + unaggregated_fees: Arc>, + state: Arc>, + config: &'static config::Cli, +} + +/// An Account is the relationship between the allocation and the sender in the context of a single +/// allocation. +/// +/// Manages the lifecycle of Scalar TAP for the Account, including: +/// - Monitoring new receipts and keeping track of the unaggregated fees. +/// - Requesting RAVs from the sender's TAP aggregator once the unaggregated fees reach a certain +/// threshold. +/// - Requesting the last RAV from the sender's TAP aggregator (on Account EOL) +pub struct Account { + inner: Arc, + rav_requester_task: Arc>>>, +} + +impl Account { + pub fn new( + config: &'static config::Cli, + pgpool: PgPool, + allocation_id: Address, + sender: Address, + escrow_accounts: Eventual>, + escrow_subgraph: &'static SubgraphClient, + escrow_adapter: EscrowAdapter, + tap_eip712_domain_separator: Eip712Domain, + sender_aggregator_endpoint: String, + ) -> Self { + let required_checks = vec![ + ReceiptCheck::CheckUnique, + ReceiptCheck::CheckAllocationId, + ReceiptCheck::CheckTimestamp, + // ReceiptCheck::CheckValue, + ReceiptCheck::CheckSignature, + ReceiptCheck::CheckAndReserveEscrow, + ]; + + let receipt_checks_adapter = ReceiptChecksAdapter::new( + config, + pgpool.clone(), + // TODO: Implement query appraisals. + None, + allocation_id, + escrow_accounts.clone(), + escrow_subgraph, + sender, + ); + let receipt_storage_adapter = ReceiptStorageAdapter::new( + pgpool.clone(), + allocation_id, + sender, + required_checks.clone(), + ); + let rav_storage_adapter = RAVStorageAdapter::new(pgpool.clone(), allocation_id, sender); + let tap_manager = TapManager::new( + tap_eip712_domain_separator.clone(), + escrow_adapter, + receipt_checks_adapter, + rav_storage_adapter, + receipt_storage_adapter, + required_checks, + 0, + ); + Self { + inner: Arc::new(Inner { + pgpool, + tap_manager, + allocation_id, + sender, + sender_aggregator_endpoint, + unaggregated_fees: Arc::new(Mutex::new(UnaggregatedFees::default())), + state: Arc::new(Mutex::new(State::Running)), + config, + }), + rav_requester_task: Arc::new(Mutex::new(None)), + } + } + + pub async fn handle_new_receipt_notification( + &self, + new_receipt_notification: NewReceiptNotification, + ) { + // If we're in the last rav pending state, we don't want to process any new receipts. + if self.state().await == State::LastRavPending { + error!( + "Received a new receipt notification for allocation {} and sender {} while \ + the last RAV is pending. This should not have happened since this allocation \ + and/or sender is not eligible anymore.", + self.inner.allocation_id, self.inner.sender + ); + return; + } + + let mut unaggregated_fees = self.inner.unaggregated_fees.lock().await; + + // Else we already processed that receipt, most likely from pulling the receipts + // from the database. + if new_receipt_notification.id > unaggregated_fees.last_id { + unaggregated_fees.value = unaggregated_fees + .value + .checked_add(new_receipt_notification.value) + .unwrap_or_else(|| { + // This should never happen, but if it does, we want to know about it. + error!( + "Overflow when adding receipt value {} to total unaggregated fees {} for \ + allocation {} and sender {}. Setting total unaggregated fees to u128::MAX.", + new_receipt_notification.value, + unaggregated_fees.value, + new_receipt_notification.allocation_id, + new_receipt_notification.sender_address + ); + u128::MAX + }); + unaggregated_fees.last_id = new_receipt_notification.id; + + let mut rav_requester_task = self.rav_requester_task.lock().await; + // TODO: consider making the trigger per sender, instead of per (sender, allocation). + if unaggregated_fees.value >= self.inner.config.tap.rav_request_trigger_value.into() + && !Self::rav_requester_task_is_running(&rav_requester_task) + { + *rav_requester_task = Some(tokio::spawn(Self::rav_requester(self.inner.clone()))); + } + } + } + + pub async fn start_last_rav_request(&self) { + *(self.inner.state.lock().await) = State::LastRavPending; + let mut rav_requester_task = self.rav_requester_task.lock().await; + if !Self::rav_requester_task_is_running(&rav_requester_task) { + *rav_requester_task = Some(tokio::spawn(Self::rav_requester(self.inner.clone()))); + } + } + + /// Delete obsolete receipts in the DB w.r.t. the last RAV in DB, then update the tap manager + /// with the latest unaggregated fees from the database. + pub async fn update_unaggregated_fees(&self) -> Result<(), anyhow::Error> { + Self::update_unaggregated_fees_static(&self.inner).await + } + + /// Delete obsolete receipts in the DB w.r.t. the last RAV in DB, then update the tap manager + /// with the latest unaggregated fees from the database. + async fn update_unaggregated_fees_static(inner: &Inner) -> Result<(), anyhow::Error> { + inner.tap_manager.remove_obsolete_receipts().await?; + + // TODO: Get `rav.timestamp_ns` from the TAP Manager's RAV storage adapter instead? + let res = sqlx::query!( + r#" + WITH rav AS ( + SELECT + rav -> 'message' ->> 'timestamp_ns' AS timestamp_ns + FROM + scalar_tap_latest_ravs + WHERE + allocation_id = $1 + AND sender_address = $2 + ) + SELECT + COUNT(*), + MAX(id), + SUM(value) + FROM + scalar_tap_receipts + WHERE + allocation_id = $1 + AND sender_address = $2 + AND CASE WHEN ( + SELECT + timestamp_ns :: NUMERIC + FROM + rav + ) IS NOT NULL THEN timestamp_ns > ( + SELECT + timestamp_ns :: NUMERIC + FROM + rav + ) ELSE TRUE END + "#, + inner + .allocation_id + .to_string() + .strip_prefix("0x") + .unwrap() + .to_owned(), + inner + .sender + .to_string() + .strip_prefix("0x") + .unwrap() + .to_owned() + ) + .fetch_one(&inner.pgpool) + .await?; + + let mut unaggregated_fees = inner.unaggregated_fees.lock().await; + + // `COUNT(*)` will always return a value, so we don't need to check for `None`. + match res.count.unwrap() { + 0 => { + unaggregated_fees.last_id = 0; + unaggregated_fees.value = 0; + } + // If the count is non-zero, then `MAX(id)` and `SUM(value)` will be non-null. + // If they are null, then something is extremely wrong with the database. + _ => { + ensure!( + res.max.is_some(), + "MAX(id) is null but the receipt COUNT(*) is not zero" + ); + ensure!( + res.sum.is_some(), + "SUM(value) is null, but the receipt COUNT(*) is not zero" + ); + + unaggregated_fees.last_id = res.max.unwrap().try_into()?; + unaggregated_fees.value = res.sum.unwrap().to_string().parse::()?; + } + } + + // TODO: check if we need to run a RAV request here. + + Ok(()) + } + + /// Request a RAV from the sender's TAP aggregator. + /// Will remove the aggregated receipts from the database if successful. + async fn rav_requester(inner: Arc) { + Self::rav_requester_try(&inner).await.unwrap_or_else(|e| { + error!( + "Error while requesting a RAV for allocation {} and sender {}: {:?}", + inner.allocation_id, inner.sender, e + ); + }); + } + + async fn rav_requester_try(inner: &Arc) -> anyhow::Result<()> { + loop { + // TODO: limit the number of receipts to aggregate per request. + let RAVRequest { + valid_receipts, + previous_rav, + invalid_receipts: _, + expected_rav, + } = inner + .tap_manager + .create_rav_request(inner.config.tap.rav_request_timestamp_buffer_ns) + .await?; + + // TODO: Request compression and response decompression. Also a fancy user agent? + let client = HttpClientBuilder::default().build(&inner.sender_aggregator_endpoint)?; + + // TODO: Add a timeout. + let response: JsonRpcResponse> = client + .request( + "aggregate_receipts", + rpc_params!( + "0.0", // TODO: Set the version in a smarter place. + valid_receipts, + previous_rav + ), + ) + .await?; + + if let Some(warnings) = response.warnings { + warn!("Warnings from sender's TAP aggregator: {:?}", warnings); + } + + inner + .tap_manager + .verify_and_store_rav(expected_rav, response.data) + .await?; + + // TODO: Handle invalid receipts + + // This is not the fastest way to do this, but it's the easiest. + // Note: we rely on the unaggregated_fees lock to make sure we don't miss any receipt + // notifications during this. + // TODO: If needed, faster alternative? + Self::update_unaggregated_fees_static(inner).await?; + + let unaggregated_fees = inner.unaggregated_fees.lock().await; + if unaggregated_fees.value < inner.config.tap.rav_request_trigger_value.into() { + break; + } else { + // Go right back to requesting a RAV and warn the user. + warn!( + "Unaggregated fees for allocation {} and sender {} are {} right \ + after the RAV request. This is a sign that the TAP agent can't keep \ + up with the rate of new receipts. Consider increasing the \ + `rav_request_trigger_value` in the TAP agent config. It could also be \ + a sign that the sender's TAP aggregator is too slow.", + inner.allocation_id, inner.sender, unaggregated_fees.value + ); + } + } + + let mut state = inner.state.lock().await; + if *state == State::LastRavPending { + // Mark the last RAV as last in the DB as a cue for the indexer-agent. + let updated_rows = sqlx::query!( + r#" + UPDATE scalar_tap_latest_ravs + SET is_last = true + WHERE allocation_id = $1 AND sender_address = $2 + RETURNING * + "#, + inner + .allocation_id + .to_string() + .strip_prefix("0x") + .unwrap() + .to_owned(), + inner + .sender + .to_string() + .strip_prefix("0x") + .unwrap() + .to_owned(), + ) + .fetch_all(&inner.pgpool) + .await?; + + // Make sure exactly one row was updated. + if updated_rows.len() != 1 { + anyhow::bail!( + "Expected exactly one row to be updated in the latest RAVs table, \ + but {} were updated.", + updated_rows.len() + ); + } + + *state = State::Finished; + }; + anyhow::Ok(()) + } + + fn rav_requester_task_is_running( + rav_requester_task_lock: &MutexGuard<'_, Option>>, + ) -> bool { + if let Some(handle) = rav_requester_task_lock.as_ref() { + !handle.is_finished() + } else { + false + } + } + + pub async fn state(&self) -> State { + *self.inner.state.lock().await + } +} + +impl Drop for Account { + /// Trying to make sure the RAV requester task is dropped when the account is dropped. + fn drop(&mut self) { + let rav_requester_task = self.rav_requester_task.clone(); + + tokio::spawn(async move { + let mut rav_requester_task = rav_requester_task.lock().await; + if let Some(rav_requester_task) = rav_requester_task.take() { + rav_requester_task.abort(); + } + }); + } +} + +#[cfg(test)] +mod tests { + + use indexer_common::subgraph_client::DeploymentDetails; + use serde_json::json; + use tap_aggregator::server::run_server; + use tap_core::tap_manager::SignedRAV; + use wiremock::{ + matchers::{body_string_contains, method}, + Mock, MockServer, ResponseTemplate, + }; + + use super::*; + use crate::tap::test_utils::{ + create_rav, create_received_receipt, store_rav, store_receipt, ALLOCATION_ID, INDEXER, + SENDER, TAP_EIP712_DOMAIN_SEPARATOR, + }; + + const DUMMY_URL: &str = "http://localhost:1234"; + + async fn create_account( + pgpool: PgPool, + sender_aggregator_endpoint: String, + escrow_subgraph_endpoint: &str, + ) -> Account { + let config = Box::leak(Box::new(config::Cli { + config: None, + ethereum: config::Ethereum { + indexer_address: INDEXER.1, + ..Default::default() + }, + tap: config::Tap { + rav_request_trigger_value: 100, + rav_request_timestamp_buffer_ns: 1000, + ..Default::default() + }, + ..Default::default() + })); + + let escrow_subgraph = Box::leak(Box::new(SubgraphClient::new( + reqwest::Client::new(), + None, + DeploymentDetails::for_query_url(escrow_subgraph_endpoint).unwrap(), + ))); + + let (mut escrow_accounts_writer, escrow_accounts_eventual) = + Eventual::>::new(); + escrow_accounts_writer.write(HashMap::from([(SENDER.1, 1000.into())])); + + let escrow_adapter = EscrowAdapter::new(escrow_accounts_eventual.clone()); + + Account::new( + config, + pgpool.clone(), + *ALLOCATION_ID, + SENDER.1, + escrow_accounts_eventual, + escrow_subgraph, + escrow_adapter, + TAP_EIP712_DOMAIN_SEPARATOR.clone(), + sender_aggregator_endpoint, + ) + } + + /// Test that the account correctly updates the unaggregated fees from the database when there + /// is no RAV in the database. + /// + /// The account should consider all receipts found for the allocation and sender. + #[sqlx::test(migrations = "../migrations")] + async fn test_update_unaggregated_fees_no_rav(pgpool: PgPool) { + let account = create_account(pgpool.clone(), DUMMY_URL.to_string(), DUMMY_URL).await; + + // Add receipts to the database. + for i in 1..10 { + let receipt = + create_received_receipt(&ALLOCATION_ID, &SENDER.0, i, i, i.into(), i).await; + store_receipt(&pgpool, receipt.signed_receipt()) + .await + .unwrap(); + } + + // Let the account update the unaggregated fees from the database. + account.update_unaggregated_fees().await.unwrap(); + + // Check that the unaggregated fees are correct. + assert_eq!(account.inner.unaggregated_fees.lock().await.value, 45u128); + } + + /// Test that the account correctly updates the unaggregated fees from the database when there + /// is a RAV in the database as well as receipts which timestamp are lesser and greater than + /// the RAV's timestamp. + /// + /// The account should only consider receipts with a timestamp greater than the RAV's timestamp. + #[sqlx::test(migrations = "../migrations")] + async fn test_update_unaggregated_fees_with_rav(pgpool: PgPool) { + let account = create_account(pgpool.clone(), DUMMY_URL.to_string(), DUMMY_URL).await; + + // Add the RAV to the database. + // This RAV has timestamp 4. The Account should only consider receipts with a timestamp + // greater than 4. + let signed_rav = create_rav(*ALLOCATION_ID, SENDER.0.clone(), 4, 10).await; + store_rav(&pgpool, signed_rav, SENDER.1).await.unwrap(); + + // Add receipts to the database. + for i in 1..10 { + let receipt = + create_received_receipt(&ALLOCATION_ID, &SENDER.0, i, i, i.into(), i).await; + store_receipt(&pgpool, receipt.signed_receipt()) + .await + .unwrap(); + } + + // Let the account update the unaggregated fees from the database. + account.update_unaggregated_fees().await.unwrap(); + + // Check that the unaggregated fees are correct. + assert_eq!(account.inner.unaggregated_fees.lock().await.value, 35u128); + } + + /// Test that the account correctly ignores new receipt notifications with an ID lower than + /// the last receipt ID processed (be it from the DB or from a prior receipt notification). + #[sqlx::test(migrations = "../migrations")] + async fn test_handle_new_receipt_notification(pgpool: PgPool) { + let account = create_account(pgpool.clone(), DUMMY_URL.to_string(), DUMMY_URL).await; + + // Add receipts to the database. + let mut expected_unaggregated_fees = 0u128; + for i in 10..20 { + let receipt = + create_received_receipt(&ALLOCATION_ID, &SENDER.0, i, i, i.into(), i).await; + store_receipt(&pgpool, receipt.signed_receipt()) + .await + .unwrap(); + expected_unaggregated_fees += u128::from(i); + } + + account.update_unaggregated_fees().await.unwrap(); + + // Check that the unaggregated fees are correct. + assert_eq!( + account.inner.unaggregated_fees.lock().await.value, + expected_unaggregated_fees + ); + + // Send a new receipt notification that has a lower ID than the last loaded from the DB. + // The last ID in the DB should be 10, since we added 10 receipts to the empty receipts + // table + let new_receipt_notification = NewReceiptNotification { + allocation_id: *ALLOCATION_ID, + sender_address: SENDER.1, + id: 10, + timestamp_ns: 19, + value: 19, + }; + account + .handle_new_receipt_notification(new_receipt_notification) + .await; + + // Check that the unaggregated fees have *not* increased. + assert_eq!( + account.inner.unaggregated_fees.lock().await.value, + expected_unaggregated_fees + ); + + // Send a new receipt notification. + let new_receipt_notification = NewReceiptNotification { + allocation_id: *ALLOCATION_ID, + sender_address: SENDER.1, + id: 30, + timestamp_ns: 20, + value: 20, + }; + account + .handle_new_receipt_notification(new_receipt_notification) + .await; + expected_unaggregated_fees += 20; + + // Check that the unaggregated fees are correct. + assert_eq!( + account.inner.unaggregated_fees.lock().await.value, + expected_unaggregated_fees + ); + + // Send a new receipt notification that has a lower ID than the previous one. + let new_receipt_notification = NewReceiptNotification { + allocation_id: *ALLOCATION_ID, + sender_address: SENDER.1, + id: 25, + timestamp_ns: 19, + value: 19, + }; + account + .handle_new_receipt_notification(new_receipt_notification) + .await; + + // Check that the unaggregated fees have *not* increased. + assert_eq!( + account.inner.unaggregated_fees.lock().await.value, + expected_unaggregated_fees + ); + } + + #[sqlx::test(migrations = "../migrations")] + async fn test_rav_requester_manual(pgpool: PgPool) { + // Start a TAP aggregator server. + let (handle, aggregator_endpoint) = run_server( + 0, + SENDER.0.clone(), + TAP_EIP712_DOMAIN_SEPARATOR.clone(), + 100 * 1024, + 100 * 1024, + 1, + ) + .await + .unwrap(); + + // Start a mock graphql server using wiremock + let mock_server = MockServer::start().await; + + // Mock result for TAP redeem txs for (allocation, sender) pair. + mock_server + .register( + Mock::given(method("POST")) + .and(body_string_contains("transactions")) + .respond_with( + ResponseTemplate::new(200) + .set_body_json(json!({ "data": { "transactions": []}})), + ), + ) + .await; + + // Create an account. + let account = create_account( + pgpool.clone(), + "http://".to_owned() + &aggregator_endpoint.to_string(), + &mock_server.uri(), + ) + .await; + + // Add receipts to the database. + for i in 0..10 { + let receipt = + create_received_receipt(&ALLOCATION_ID, &SENDER.0, i, i + 1, i.into(), i).await; + store_receipt(&pgpool, receipt.signed_receipt()) + .await + .unwrap(); + } + + // Let the account update the unaggregated fees from the database. + account.update_unaggregated_fees().await.unwrap(); + + // Trigger a RAV request manually. + Account::rav_requester_try(&account.inner).await.unwrap(); + + // Stop the TAP aggregator server. + handle.stop().unwrap(); + handle.stopped().await; + } + + #[sqlx::test(migrations = "../migrations")] + async fn test_rav_requester_auto(pgpool: PgPool) { + // Start a TAP aggregator server. + let (handle, aggregator_endpoint) = run_server( + 0, + SENDER.0.clone(), + TAP_EIP712_DOMAIN_SEPARATOR.clone(), + 100 * 1024, + 100 * 1024, + 1, + ) + .await + .unwrap(); + + // Start a mock graphql server using wiremock + let mock_server = MockServer::start().await; + + // Mock result for TAP redeem txs for (allocation, sender) pair. + mock_server + .register( + Mock::given(method("POST")) + .and(body_string_contains("transactions")) + .respond_with( + ResponseTemplate::new(200) + .set_body_json(json!({ "data": { "transactions": []}})), + ), + ) + .await; + + // Create an account. + let account = create_account( + pgpool.clone(), + "http://".to_owned() + &aggregator_endpoint.to_string(), + &mock_server.uri(), + ) + .await; + + // Add receipts to the database and call the `handle_new_receipt_notification` method + // correspondingly. + let mut total_value = 0; + let mut trigger_value = 0; + for i in 0..10 { + // These values should be enough to trigger a RAV request at i == 7 since we set the + // `rav_request_trigger_value` to 100. + let value = (i + 10) as u128; + + let receipt = + create_received_receipt(&ALLOCATION_ID, &SENDER.0, i, i + 1, value, i).await; + store_receipt(&pgpool, receipt.signed_receipt()) + .await + .unwrap(); + account + .handle_new_receipt_notification(NewReceiptNotification { + allocation_id: *ALLOCATION_ID, + sender_address: SENDER.1, + id: i, + timestamp_ns: i + 1, + value, + }) + .await; + + total_value += value; + if total_value >= 100 && trigger_value == 0 { + trigger_value = total_value; + } + } + + // Wait for the RAV requester to finish. + while Account::rav_requester_task_is_running(&account.rav_requester_task.lock().await) { + tokio::time::sleep(std::time::Duration::from_millis(100)).await; + } + + // Get the latest RAV from the database. + let latest_rav = sqlx::query!( + r#" + SELECT rav + FROM scalar_tap_latest_ravs + WHERE allocation_id = $1 AND sender_address = $2 + "#, + ALLOCATION_ID + .to_string() + .strip_prefix("0x") + .unwrap() + .to_owned(), + SENDER.1.to_string().strip_prefix("0x").unwrap().to_owned() + ) + .fetch_optional(&pgpool) + .await + .map(|r| r.map(|r| r.rav)) + .unwrap(); + + let latest_rav = latest_rav + .map(|r| serde_json::from_value::(r).unwrap()) + .unwrap(); + + // Check that the latest RAV value is correct. + assert!(latest_rav.message.value_aggregate >= trigger_value); + + // Check that the unaggregated fees value is reduced. + assert!(account.inner.unaggregated_fees.lock().await.value <= trigger_value); + + // Reset the total value and trigger value. + total_value = account.inner.unaggregated_fees.lock().await.value; + trigger_value = 0; + + // Add more receipts + for i in 10..20 { + let value = (i + 10) as u128; + + let receipt = + create_received_receipt(&ALLOCATION_ID, &SENDER.0, i, i + 1, i.into(), i).await; + store_receipt(&pgpool, receipt.signed_receipt()) + .await + .unwrap(); + + account + .handle_new_receipt_notification(NewReceiptNotification { + allocation_id: *ALLOCATION_ID, + sender_address: SENDER.1, + id: i, + timestamp_ns: i + 1, + value, + }) + .await; + + total_value += value; + if total_value >= 100 && trigger_value == 0 { + trigger_value = total_value; + } + } + + // Wait for the RAV requester to finish. + while Account::rav_requester_task_is_running(&account.rav_requester_task.lock().await) { + tokio::time::sleep(std::time::Duration::from_millis(100)).await; + } + + // Get the latest RAV from the database. + let latest_rav = sqlx::query!( + r#" + SELECT rav + FROM scalar_tap_latest_ravs + WHERE allocation_id = $1 AND sender_address = $2 + "#, + ALLOCATION_ID + .to_string() + .strip_prefix("0x") + .unwrap() + .to_owned(), + SENDER.1.to_string().strip_prefix("0x").unwrap().to_owned() + ) + .fetch_optional(&pgpool) + .await + .map(|r| r.map(|r| r.rav)) + .unwrap(); + + let latest_rav = latest_rav + .map(|r| serde_json::from_value::(r).unwrap()) + .unwrap(); + + // Check that the latest RAV value is correct. + + assert!(latest_rav.message.value_aggregate >= trigger_value); + + // Check that the unaggregated fees value is reduced. + assert!(account.inner.unaggregated_fees.lock().await.value <= trigger_value); + + // Stop the TAP aggregator server. + handle.stop().unwrap(); + handle.stopped().await; + } +} diff --git a/tap_agent/src/tap/accounts_manager.rs b/tap_agent/src/tap/accounts_manager.rs new file mode 100644 index 00000000..b67a7078 --- /dev/null +++ b/tap_agent/src/tap/accounts_manager.rs @@ -0,0 +1,422 @@ +// Copyright 2023-, GraphOps and Semiotic Labs. +// SPDX-License-Identifier: Apache-2.0 + +use std::{collections::HashMap, str::FromStr, sync::Arc}; + +use alloy_primitives::Address; +use alloy_sol_types::Eip712Domain; +use anyhow::anyhow; +use anyhow::Result; +use ethereum_types::U256; +use eventuals::{Eventual, EventualExt, PipeHandle}; +use indexer_common::prelude::{Allocation, SubgraphClient}; +use serde::Deserialize; +use sqlx::{postgres::PgListener, PgPool}; +use tokio::sync::RwLock; +use tracing::{error, warn}; + +use super::account::Account; +use super::escrow_adapter::EscrowAdapter; +use crate::config; + +#[derive(Deserialize, Debug)] +pub struct NewReceiptNotification { + pub id: u64, + pub allocation_id: Address, + pub sender_address: Address, + pub timestamp_ns: u64, + pub value: u128, +} + +pub struct AccountsManager { + _inner: Arc, + new_receipts_watcher_handle: tokio::task::JoinHandle<()>, + _eligible_allocations_senders_pipe: PipeHandle, +} + +#[derive(Clone)] +struct Inner { + config: &'static config::Cli, + pgpool: PgPool, + /// Map of (allocation_id, sender_address) to Account. + accounts: Arc>>, + indexer_allocations: Eventual>, + escrow_accounts: Eventual>, + escrow_subgraph: &'static SubgraphClient, + escrow_adapter: EscrowAdapter, + tap_eip712_domain_separator: Eip712Domain, + sender_aggregator_endpoints: HashMap, +} + +impl AccountsManager { + pub async fn new( + config: &'static config::Cli, + pgpool: PgPool, + indexer_allocations: Eventual>, + escrow_accounts: Eventual>, + escrow_subgraph: &'static SubgraphClient, + tap_eip712_domain_separator: Eip712Domain, + sender_aggregator_endpoints: HashMap, + ) -> Self { + let escrow_adapter = EscrowAdapter::new(escrow_accounts.clone()); + + let inner = Arc::new(Inner { + config, + pgpool, + accounts: Arc::new(RwLock::new(HashMap::new())), + indexer_allocations, + escrow_accounts, + escrow_subgraph, + escrow_adapter, + tap_eip712_domain_separator, + sender_aggregator_endpoints, + }); + + Self::update_accounts( + &inner, + inner + .indexer_allocations + .value() + .await + .expect("Should get indexer allocations from Eventual"), + inner + .escrow_accounts + .value() + .await + .expect("Should get escrow accounts from Eventual"), + ) + .await + .expect("Should be able to update accounts"); + + // Listen to pg_notify events. We start it before updating the unaggregated_fees for + // all accounts, so that we don't miss any receipts. PG will buffer the notifications + // until we start consuming them with `new_receipts_watcher`. + let mut pglistener = PgListener::connect_with(&inner.pgpool.clone()) + .await + .unwrap(); + pglistener + .listen("scalar_tap_receipt_notification") + .await + .expect( + "should be able to subscribe to Postgres Notify events on the channel \ + 'scalar_tap_receipt_notification'", + ); + + let mut accounts_write_lock = inner.accounts.write().await; + + // Create accounts for all outstanding receipts in the database, because they may + // be linked to allocations that are not eligible anymore, but still need to get + // aggregated. + sqlx::query!( + r#" + SELECT DISTINCT allocation_id, sender_address + FROM scalar_tap_receipts + "# + ) + .fetch_all(&inner.pgpool) + .await + .unwrap() + .into_iter() + .for_each(|row| { + let allocation_id = Address::from_str(&row.allocation_id) + .expect("allocation_id should be a valid address"); + let sender = Address::from_str(&row.sender_address) + .expect("sender_address should be a valid address"); + + // Only create a account if it doesn't exist yet. + if let std::collections::hash_map::Entry::Vacant(e) = + accounts_write_lock.entry((allocation_id, sender)) + { + e.insert(Account::new( + config, + inner.pgpool.clone(), + allocation_id, + sender, + inner.escrow_accounts.clone(), + inner.escrow_subgraph, + inner.escrow_adapter.clone(), + inner.tap_eip712_domain_separator.clone(), + inner + .sender_aggregator_endpoints + .get(&sender) + .unwrap() + .clone(), + )); + } + }); + + // Update the unaggregated_fees for all accounts by pulling the receipts from the + // database. + for account in accounts_write_lock.values() { + account + .update_unaggregated_fees() + .await + .expect("should be able to update unaggregated_fees"); + } + + drop(accounts_write_lock); + + // Start the new_receipts_watcher task that will consume from the `pglistener` + let new_receipts_watcher_handle = tokio::spawn(Self::new_receipts_watcher( + pglistener, + inner.accounts.clone(), + )); + + // Start the eligible_allocations_senders_pipe that watches for changes in eligible senders + // and allocations and updates the accounts accordingly. + let inner_clone = inner.clone(); + let eligible_allocations_senders_pipe = eventuals::join(( + inner.indexer_allocations.clone(), + inner.escrow_accounts.clone(), + )) + .pipe_async(move |(indexer_allocations, escrow_accounts)| { + let inner = inner_clone.clone(); + async move { + Self::update_accounts(&inner, indexer_allocations, escrow_accounts) + .await + .unwrap_or_else(|e| { + error!("Error while updating accounts: {:?}", e); + }); + } + }); + + Self { + _inner: inner, + new_receipts_watcher_handle, + _eligible_allocations_senders_pipe: eligible_allocations_senders_pipe, + } + } + + /// Continuously listens for new receipt notifications from Postgres and forwards them to the + /// corresponding account. + async fn new_receipts_watcher( + mut pglistener: PgListener, + accounts: Arc>>, + ) { + loop { + // TODO: recover from errors or shutdown the whole program? + let pg_notification = pglistener.recv().await.expect( + "should be able to receive Postgres Notify events on the channel \ + 'scalar_tap_receipt_notification'", + ); + let new_receipt_notification: NewReceiptNotification = + serde_json::from_str(pg_notification.payload()).expect( + "should be able to deserialize the Postgres Notify event payload as a \ + NewReceiptNotification", + ); + + if let Some(account) = accounts.read().await.get(&( + new_receipt_notification.allocation_id, + new_receipt_notification.sender_address, + )) { + account + .handle_new_receipt_notification(new_receipt_notification) + .await; + } else { + warn!( + "No account found for allocation_id {} and sender_address {} to process \ + new receipt notification. This should not happen.", + new_receipt_notification.allocation_id, new_receipt_notification.sender_address + ); + } + } + } + + async fn update_accounts( + inner: &Inner, + indexer_allocations: HashMap, + escrow_accounts: HashMap, + ) -> Result<()> { + let eligible_allocations: Vec
= indexer_allocations.keys().copied().collect(); + let senders: Vec
= escrow_accounts.keys().copied().collect(); + let mut accounts_write = inner.accounts.write().await; + + // Create accounts for all currently eligible (allocation, sender) + for allocation_id in &eligible_allocations { + for sender in &senders { + // Only create an account if it doesn't exist yet. + if let std::collections::hash_map::Entry::Vacant(e) = + accounts_write.entry((*allocation_id, *sender)) + { + e.insert(Account::new( + inner.config, + inner.pgpool.clone(), + *allocation_id, + *sender, + inner.escrow_accounts.clone(), + inner.escrow_subgraph, + inner.escrow_adapter.clone(), + inner.tap_eip712_domain_separator.clone(), + inner + .sender_aggregator_endpoints + .get(sender) + .ok_or_else(|| { + anyhow!("No sender_aggregator_endpoint found for sender {}", sender) + })? + .clone(), + )); + } + } + } + + // Trigger a last rav request for all accounts that correspond to ineligible + // (allocations, sender). + for ((allocation_id, sender), account) in accounts_write.iter() { + if !eligible_allocations.contains(allocation_id) || !senders.contains(sender) { + account.start_last_rav_request().await + } + } + + // TODO: remove accounts that are finished. Ideally done in another async task? + + Ok(()) + } +} + +impl Drop for AccountsManager { + fn drop(&mut self) { + // Abort the notification watcher on drop. Otherwise it may panic because the PgPool could + // get dropped before. (Observed in tests) + self.new_receipts_watcher_handle.abort(); + } +} + +#[cfg(test)] +mod tests { + use indexer_common::{ + prelude::{AllocationStatus, SubgraphDeployment}, + subgraph_client::DeploymentDetails, + }; + use serde_json::json; + use toolshed::thegraph::DeploymentId; + use wiremock::{ + matchers::{body_string_contains, method}, + Mock, MockServer, ResponseTemplate, + }; + + use crate::tap::{ + account::State, + test_utils::{INDEXER, SENDER, TAP_EIP712_DOMAIN_SEPARATOR}, + }; + + use super::*; + + #[sqlx::test(migrations = "../migrations")] + async fn test_account_creation_and_eol(pgpool: PgPool) { + let config = Box::leak(Box::new(config::Cli { + config: None, + ethereum: config::Ethereum { + indexer_address: INDEXER.1, + ..Default::default() + }, + tap: config::Tap { + rav_request_trigger_value: 100, + rav_request_timestamp_buffer_ns: 1000, + ..Default::default() + }, + ..Default::default() + })); + + let (mut indexer_allocations_writer, indexer_allocations_eventual) = + Eventual::>::new(); + indexer_allocations_writer.write(HashMap::new()); + + let (mut escrow_accounts_writer, escrow_accounts_eventual) = + Eventual::>::new(); + escrow_accounts_writer.write(HashMap::new()); + + // Mock escrow subgraph. + let mock_server = MockServer::start().await; + mock_server + .register( + Mock::given(method("POST")) + .and(body_string_contains("transactions")) + .respond_with( + ResponseTemplate::new(200) + .set_body_json(json!({ "data": { "transactions": []}})), + ), + ) + .await; + let escrow_subgraph = Box::leak(Box::new(SubgraphClient::new( + reqwest::Client::new(), + None, + DeploymentDetails::for_query_url(&mock_server.uri()).unwrap(), + ))); + + let accounts = AccountsManager::new( + config, + pgpool.clone(), + indexer_allocations_eventual, + escrow_accounts_eventual, + escrow_subgraph, + TAP_EIP712_DOMAIN_SEPARATOR.clone(), + HashMap::from([(SENDER.1, String::from("http://localhost:8000"))]), + ) + .await; + + let allocation_id = + Address::from_str("0xdd975e30aafebb143e54d215db8a3e8fd916a701").unwrap(); + + // Add an allocation to the indexer_allocations Eventual. + indexer_allocations_writer.write(HashMap::from([( + allocation_id, + Allocation { + id: allocation_id, + indexer: INDEXER.1, + allocated_tokens: U256::from_str("601726452999999979510903").unwrap(), + created_at_block_hash: + "0x99d3fbdc0105f7ccc0cd5bb287b82657fe92db4ea8fb58242dafb90b1c6e2adf".to_string(), + created_at_epoch: 953, + closed_at_epoch: None, + subgraph_deployment: SubgraphDeployment { + id: DeploymentId( + "0xcda7fa0405d6fd10721ed13d18823d24b535060d8ff661f862b26c23334f13bf" + .parse() + .unwrap(), + ), + denied_at: Some(0), + }, + status: AllocationStatus::Null, + closed_at_epoch_start_block_hash: None, + previous_epoch_start_block_hash: None, + poi: None, + query_fee_rebates: None, + query_fees_collected: None, + }, + )])); + + // Add an escrow account to the escrow_accounts Eventual. + escrow_accounts_writer.write(HashMap::from([(SENDER.1, U256::from_str("1000").unwrap())])); + + // Wait for the account to be created. + tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; + + // Check that the account was created. + assert!(accounts + ._inner + .accounts + .write() + .await + .contains_key(&(allocation_id, SENDER.1))); + + // Remove the escrow account from the escrow_accounts Eventual. + escrow_accounts_writer.write(HashMap::new()); + + // Wait a bit + tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; + + // Check that the account state is last_rav_pending + assert_eq!( + accounts + ._inner + .accounts + .read() + .await + .get(&(allocation_id, SENDER.1)) + .unwrap() + .state() + .await, + State::LastRavPending + ); + } +} diff --git a/tap_agent/src/tap/escrow_adapter.rs b/tap_agent/src/tap/escrow_adapter.rs new file mode 100644 index 00000000..5a0a62ab --- /dev/null +++ b/tap_agent/src/tap/escrow_adapter.rs @@ -0,0 +1,154 @@ +// Copyright 2023-, GraphOps and Semiotic Labs. +// SPDX-License-Identifier: Apache-2.0 + +use std::{collections::HashMap, sync::Arc}; + +use alloy_primitives::Address; +use async_trait::async_trait; +use ethereum_types::U256; +use eventuals::Eventual; +use tap_core::adapters::escrow_adapter::EscrowAdapter as EscrowAdapterTrait; +use thiserror::Error; +use tokio::sync::RwLock; + +/// The EscrowAdapter is used to track the available escrow for all senders. It is updated when +/// receipt checks are finalized (right before a RAV request). +/// +/// It is to be shared between all Account instances. Note that it is Arc internally, so it can be +/// shared through clones. +/// +/// It is not used to track unaggregated fees (yet?), because we are currently batch finalizing +/// receipt checks only when we need to send a RAV request. +#[derive(Clone)] +pub struct EscrowAdapter { + escrow_accounts: Eventual>, + sender_pending_fees: Arc>>, +} + +#[derive(Debug, Error)] +pub enum AdapterError { + #[error("Error in EscrowAdapter: {error}")] + AdapterError { error: String }, +} + +impl EscrowAdapter { + pub fn new(escrow_accounts: Eventual>) -> Self { + Self { + escrow_accounts, + sender_pending_fees: Arc::new(RwLock::new(HashMap::new())), + } + } +} + +#[async_trait] +impl EscrowAdapterTrait for EscrowAdapter { + type AdapterError = AdapterError; + + async fn get_available_escrow(&self, sender: Address) -> Result { + let balance = self + .escrow_accounts + .value() + .await + .map_err(|e| AdapterError::AdapterError { + error: format!("Could not get escrow balance from eventual: {:?}.", e), + })? + .get(&sender) + .ok_or(AdapterError::AdapterError { + error: format!( + "Gateway {} not found in escrow balances map, could not get available escrow.", + sender + ) + .to_string(), + })? + .to_owned(); + let balance: u128 = balance.try_into().map_err(|_| AdapterError::AdapterError { + error: format!( + "Gateway {} escrow balance is too large to fit in u128, \ + could not get available escrow.", + sender + ) + .to_string(), + })?; + + let fees = self + .sender_pending_fees + .read() + .await + .get(&sender) + .copied() + .unwrap_or(0); + Ok(balance - fees) + } + + async fn subtract_escrow(&self, sender: Address, value: u128) -> Result<(), AdapterError> { + let current_available_escrow = self.get_available_escrow(sender).await?; + let mut fees_write = self.sender_pending_fees.write().await; + let fees = fees_write.entry(sender).or_insert(0); + if current_available_escrow < value { + return Err(AdapterError::AdapterError { + error: format!( + "Gateway {} does not have enough escrow to subtract {} from {}.", + sender, value, *fees + ) + .to_string(), + }); + } + *fees += value; + Ok(()) + } +} + +#[cfg(test)] +mod test { + use super::*; + use ethereum_types::U256; + + use std::str::FromStr; + + #[tokio::test] + async fn test_subtract_escrow() { + let sender = Address::from_str("0xdeadbeefcafebabedeadbeefcafebabadeadbeef").unwrap(); + let escrow_accounts: Eventual> = + Eventual::from_value(HashMap::from([(sender, U256::from(1000))])); + let sender_pending_fees = Arc::new(RwLock::new(HashMap::new())); + sender_pending_fees.write().await.insert(sender, 500); + + let adapter = EscrowAdapter { + escrow_accounts, + sender_pending_fees, + }; + adapter + .subtract_escrow(sender, 500) + .await + .expect("Subtract escrow."); + let available_escrow = adapter + .get_available_escrow(sender) + .await + .expect("Get available escrow."); + assert_eq!(available_escrow, 0); + } + + #[tokio::test] + async fn test_subtract_escrow_overflow() { + let sender = Address::from_str("0xdeadbeefcafebabedeadbeefcafebabadeadbeef").unwrap(); + let escrow_accounts: Eventual> = + Eventual::from_value(HashMap::from([(sender, U256::from(1000))])); + let sender_pending_fees = Arc::new(RwLock::new(HashMap::new())); + sender_pending_fees.write().await.insert(sender, 500); + + let adapter = EscrowAdapter { + escrow_accounts, + sender_pending_fees, + }; + adapter + .subtract_escrow(sender, 250) + .await + .expect("Subtract escrow."); + assert!(adapter.subtract_escrow(sender, 251).await.is_err()); + let available_escrow = adapter + .get_available_escrow(sender) + .await + .expect("Get available escrow."); + assert_eq!(available_escrow, 250); + } +} diff --git a/tap_agent/src/tap/mod.rs b/tap_agent/src/tap/mod.rs new file mode 100644 index 00000000..d648d3da --- /dev/null +++ b/tap_agent/src/tap/mod.rs @@ -0,0 +1,12 @@ +// Copyright 2023-, GraphOps and Semiotic Labs. +// SPDX-License-Identifier: Apache-2.0 + +mod account; +pub mod accounts_manager; +mod escrow_adapter; +mod rav_storage_adapter; +mod receipt_checks_adapter; +mod receipt_storage_adapter; + +#[cfg(test)] +pub mod test_utils; diff --git a/tap_agent/src/tap/rav_storage_adapter.rs b/tap_agent/src/tap/rav_storage_adapter.rs new file mode 100644 index 00000000..f2dcd578 --- /dev/null +++ b/tap_agent/src/tap/rav_storage_adapter.rs @@ -0,0 +1,155 @@ +// Copyright 2023-, GraphOps and Semiotic Labs. +// SPDX-License-Identifier: Apache-2.0 + +use alloy_primitives::Address; +use anyhow::Result; +use async_trait::async_trait; +use sqlx::PgPool; +use tap_core::adapters::rav_storage_adapter::RAVStorageAdapter as RAVStorageAdapterTrait; +use tap_core::tap_manager::SignedRAV; +use thiserror::Error; + +#[derive(Debug)] +pub struct RAVStorageAdapter { + pgpool: PgPool, + allocation_id: Address, + sender: Address, +} + +#[derive(Debug, Error)] +pub enum AdapterError { + #[error("Error in RAVStorageAdapter: {error}")] + AdapterError { error: String }, +} + +#[async_trait] +impl RAVStorageAdapterTrait for RAVStorageAdapter { + type AdapterError = AdapterError; + + async fn update_last_rav(&self, rav: SignedRAV) -> Result<(), Self::AdapterError> { + let _fut = sqlx::query!( + r#" + INSERT INTO scalar_tap_latest_ravs (allocation_id, sender_address, rav) + VALUES ($1, $2, $3) + ON CONFLICT (allocation_id, sender_address) + DO UPDATE SET rav = $3 + "#, + self.allocation_id + .to_string() + .strip_prefix("0x") + .unwrap() + .to_owned(), + self.sender + .to_string() + .strip_prefix("0x") + .unwrap() + .to_owned(), + serde_json::to_value(rav).map_err(|e| AdapterError::AdapterError { + error: e.to_string() + })? + ) + .execute(&self.pgpool) + .await + .map_err(|e| AdapterError::AdapterError { + error: e.to_string(), + })?; + Ok(()) + } + + async fn last_rav(&self) -> Result, Self::AdapterError> { + let latest_rav = sqlx::query!( + r#" + SELECT rav + FROM scalar_tap_latest_ravs + WHERE allocation_id = $1 AND sender_address = $2 + "#, + self.allocation_id + .to_string() + .strip_prefix("0x") + .unwrap() + .to_owned(), + self.sender + .to_string() + .strip_prefix("0x") + .unwrap() + .to_owned() + ) + .fetch_optional(&self.pgpool) + .await + .map(|r| r.map(|r| r.rav)) + .map_err(|e| AdapterError::AdapterError { + error: e.to_string(), + })?; + match latest_rav { + Some(latest_rav) => { + Ok( + serde_json::from_value(latest_rav).map_err(|e| AdapterError::AdapterError { + error: e.to_string(), + }), + )? + } + None => Ok(None), + } + } +} + +impl RAVStorageAdapter { + pub fn new(pgpool: PgPool, allocation_id: Address, sender: Address) -> Self { + Self { + pgpool, + allocation_id, + sender, + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::tap::test_utils::{create_rav, ALLOCATION_ID, SENDER}; + use tap_core::adapters::rav_storage_adapter::RAVStorageAdapter as RAVStorageAdapterTrait; + + #[sqlx::test(migrations = "../migrations")] + async fn update_and_retrieve_rav(pool: PgPool) { + let timestamp_ns = u64::MAX - 10; + let value_aggregate = u128::MAX; + let rav_storage_adapter = RAVStorageAdapter::new(pool.clone(), *ALLOCATION_ID, SENDER.1); + + // Insert a rav + let mut new_rav = create_rav( + *ALLOCATION_ID, + SENDER.0.clone(), + timestamp_ns, + value_aggregate, + ) + .await; + rav_storage_adapter + .update_last_rav(new_rav.clone()) + .await + .unwrap(); + + // Should trigger a retrieve_last_rav So eventually the last rav should be the one + // we inserted + let last_rav = rav_storage_adapter.last_rav().await.unwrap(); + assert_eq!(new_rav, last_rav.unwrap()); + + // Update the RAV 3 times in quick succession + for i in 0..3 { + new_rav = create_rav( + *ALLOCATION_ID, + SENDER.0.clone(), + timestamp_ns + i, + value_aggregate - (i as u128), + ) + .await; + rav_storage_adapter + .update_last_rav(new_rav.clone()) + .await + .unwrap(); + } + + // Check that the last rav is the last one we inserted + let last_rav = rav_storage_adapter.last_rav().await.unwrap(); + assert_eq!(new_rav, last_rav.unwrap()); + } +} diff --git a/tap_agent/src/tap/receipt_checks_adapter.rs b/tap_agent/src/tap/receipt_checks_adapter.rs new file mode 100644 index 00000000..1bd7026d --- /dev/null +++ b/tap_agent/src/tap/receipt_checks_adapter.rs @@ -0,0 +1,205 @@ +// Copyright 2023-, GraphOps and Semiotic Labs. +// SPDX-License-Identifier: Apache-2.0 + +use std::{collections::HashMap, sync::Arc, time::Duration}; + +use alloy_primitives::Address; +use async_trait::async_trait; +use ethereum_types::U256; +use eventuals::{timer, Eventual, EventualExt}; +use indexer_common::subgraph_client::SubgraphClient; +use serde_json::json; +use sqlx::PgPool; +use tap_core::adapters::receipt_checks_adapter::ReceiptChecksAdapter as ReceiptChecksAdapterTrait; +use tap_core::{eip_712_signed_message::EIP712SignedMessage, tap_receipt::Receipt}; +use thiserror::Error; +use tokio::{sync::RwLock, time::sleep}; +use tracing::error; + +use crate::config; + +pub struct ReceiptChecksAdapter { + query_appraisals: Option>>>, + allocation_id: Address, + escrow_accounts: Eventual>, + tap_allocation_redeemed: Eventual, +} + +impl ReceiptChecksAdapter { + pub fn new( + config: &'static config::Cli, + _pgpool: PgPool, + query_appraisals: Option>>>, + allocation_id: Address, + escrow_accounts: Eventual>, + escrow_subgraph: &'static SubgraphClient, + sender_id: Address, + ) -> Self { + let tap_allocation_redeemed = Self::tap_allocation_redeemed_eventual( + allocation_id, + sender_id, + config.ethereum.indexer_address, + escrow_subgraph, + config.escrow_subgraph.escrow_syncing_interval, + ); + Self { + query_appraisals, + allocation_id, + escrow_accounts, + tap_allocation_redeemed, + } + } +} + +#[derive(Debug, Error)] +pub enum AdapterError { + #[error("Error in ReceiptChecksAdapter: {error}")] + AdapterError { error: String }, +} + +#[async_trait] +impl ReceiptChecksAdapterTrait for ReceiptChecksAdapter { + type AdapterError = AdapterError; + + /// Should not be called. Will panic if called. This method is unneeded for the + /// moment. + /// + /// Since we don't do on the fly receipt checking, we don't need to check for + /// uniqueness with this method. The uniqueness check will be carried out by + /// `tap_core::tap_manager::Manager` on the basis of the receipts to be aggregated. + async fn is_unique( + &self, + _receipt: &EIP712SignedMessage, + _receipt_id: u64, + ) -> Result { + panic!("Not implemented."); + } + + async fn is_valid_allocation_id( + &self, + allocation_id: Address, + ) -> Result { + // TODO: Remove the if block below? Each TAP Monitor is specific to an allocation + // ID. So the receipts that are received here should already have been filtered by + // allocation ID. + if allocation_id != self.allocation_id { + return Ok(false); + }; + + // Check that the allocation ID is not redeemed yet for this consumer + match self.tap_allocation_redeemed.value().await { + Ok(redeemed) => Ok(!redeemed), + Err(e) => Err(AdapterError::AdapterError { + error: format!( + "Could not get allocation escrow redemption status from eventual: {:?}", + e + ), + }), + } + } + + async fn is_valid_value(&self, value: u128, query_id: u64) -> Result { + let query_appraisals = self.query_appraisals.as_ref().expect( + "Query appraisals should be initialized. The opposite should never happen when \ + receipts value checking is enabled.", + ); + let query_appraisals_read = query_appraisals.read().await; + let appraised_value = + query_appraisals_read + .get(&query_id) + .ok_or_else(|| AdapterError::AdapterError { + error: "No appraised value found for query".to_string(), + })?; + if value != *appraised_value { + return Ok(false); + } + Ok(true) + } + + async fn is_valid_gateway_id(&self, gateway_id: Address) -> Result { + let escrow_accounts = + self.escrow_accounts + .value() + .await + .map_err(|e| AdapterError::AdapterError { + error: format!("Could not get escrow accounts from eventual: {:?}", e), + })?; + + Ok(escrow_accounts + .get(&gateway_id) + .map_or(false, |balance| *balance > U256::from(0))) + } +} + +impl ReceiptChecksAdapter { + fn tap_allocation_redeemed_eventual( + allocation_id: Address, + sender_address: Address, + indexer_address: Address, + escrow_subgraph: &'static SubgraphClient, + escrow_subgraph_polling_interval: u64, + ) -> Eventual { + #[derive(serde::Deserialize)] + struct AllocationResponse { + #[allow(dead_code)] + id: String, + } + + #[derive(serde::Deserialize)] + struct TransactionsResponse { + transactions: Vec, + } + + timer(Duration::from_secs(escrow_subgraph_polling_interval)).map_with_retry( + move |_| async move { + let response = escrow_subgraph + .query::(&json!({ + "query": r#" + query ( + $sender_id: ID!, + $receiver_id: ID!, + $allocation_id: String! + ) { + transactions( + where: { + and: [ + { type: "redeem" } + { sender_: { id: $sender_id } } + { receiver_: { id: $receiver_id } } + { allocationID: $allocation_id } + ] + } + ) { + allocationID + sender { + id + } + } + } + "#, + "variables": { + "sender_id": sender_address.to_string(), + "receiver_id": indexer_address.to_string(), + "allocation_id": allocation_id.to_string(), + } + })) + .await + .map_err(|e| e.to_string())?; + let response = response.data.ok_or_else(|| { + format!( + "No data found in escrow subgraph response for allocation {} and sender {}", + allocation_id, sender_address + ) + })?; + Ok(!response.transactions.is_empty()) + }, + move |error: String| { + error!( + "Failed to check the escrow redeem status for allocation {} and sender {}: {}", + allocation_id, sender_address, error + ); + sleep(Duration::from_secs(escrow_subgraph_polling_interval).div_f32(2.)) + }, + ) + } +} diff --git a/tap_agent/src/tap/receipt_storage_adapter.rs b/tap_agent/src/tap/receipt_storage_adapter.rs new file mode 100644 index 00000000..4d911659 --- /dev/null +++ b/tap_agent/src/tap/receipt_storage_adapter.rs @@ -0,0 +1,608 @@ +// Copyright 2023-, GraphOps and Semiotic Labs. +// SPDX-License-Identifier: Apache-2.0 + +use std::{ + num::TryFromIntError, + ops::{Bound, RangeBounds}, +}; + +use alloy_primitives::Address; +use async_trait::async_trait; + +use sqlx::{postgres::types::PgRange, types::BigDecimal, PgPool}; +use tap_core::adapters::receipt_storage_adapter::ReceiptStorageAdapter as ReceiptStorageAdapterTrait; +use tap_core::{ + tap_manager::SignedReceipt, + tap_receipt::{ReceiptCheck, ReceivedReceipt}, +}; +use thiserror::Error; +use tracing::error; + +#[derive(Debug)] +pub struct ReceiptStorageAdapter { + pgpool: PgPool, + allocation_id: Address, + sender: Address, + required_checks: Vec, +} + +#[derive(Debug, Error)] +pub enum AdapterError { + #[error("Error in ReceiptStorageAdapter: {error}")] + AdapterError { error: String }, +} + +impl From for AdapterError { + fn from(error: TryFromIntError) -> Self { + AdapterError::AdapterError { + error: error.to_string(), + } + } +} + +impl From for AdapterError { + fn from(error: sqlx::Error) -> Self { + AdapterError::AdapterError { + error: error.to_string(), + } + } +} + +impl From for AdapterError { + fn from(error: serde_json::Error) -> Self { + AdapterError::AdapterError { + error: error.to_string(), + } + } +} + +/// convert Bound`` to Bound`` +fn u64_bound_to_bigdecimal_bound(bound: Bound<&u64>) -> Bound { + match bound { + Bound::Included(val) => Bound::Included(BigDecimal::from(*val)), + Bound::Excluded(val) => Bound::Excluded(BigDecimal::from(*val)), + Bound::Unbounded => Bound::Unbounded, + } +} + +/// convert RangeBounds`` to PgRange`` +fn rangebounds_to_pgrange>(range: R) -> PgRange { + // Test for empty ranges. Because the PG range type does not behave the same as + // Rust's range type when start > end. + if match (range.start_bound(), range.end_bound()) { + (Bound::Included(start), Bound::Included(end)) => start > end, + (Bound::Included(start), Bound::Excluded(end)) => start >= end, + (Bound::Excluded(start), Bound::Included(end)) => start >= end, + (Bound::Excluded(start), Bound::Excluded(end)) => start >= end || *start == end - 1, + _ => false, + } { + // Return an empty PG range. + return PgRange::::from(BigDecimal::from(0)..BigDecimal::from(0)); + } + PgRange::::from(( + u64_bound_to_bigdecimal_bound(range.start_bound()), + u64_bound_to_bigdecimal_bound(range.end_bound()), + )) +} + +#[async_trait] +impl ReceiptStorageAdapterTrait for ReceiptStorageAdapter { + type AdapterError = AdapterError; + + /// We don't need this method in TAP Agent. + async fn store_receipt(&self, _receipt: ReceivedReceipt) -> Result { + panic!("Not implemented"); + } + + async fn retrieve_receipts_in_timestamp_range + Send>( + &self, + timestamp_range_ns: R, + ) -> Result, Self::AdapterError> { + let records = sqlx::query!( + r#" + SELECT id, receipt + FROM scalar_tap_receipts + WHERE allocation_id = $1 AND sender_address = $2 AND $3::numrange @> timestamp_ns + "#, + self.allocation_id + .to_string() + .strip_prefix("0x") + .unwrap() + .to_owned(), + self.sender + .to_string() + .strip_prefix("0x") + .unwrap() + .to_owned(), + rangebounds_to_pgrange(timestamp_range_ns) + ) + .fetch_all(&self.pgpool) + .await?; + records + .into_iter() + .map(|record| { + let id: u64 = record.id.try_into()?; + let signed_receipt: SignedReceipt = serde_json::from_value(record.receipt)?; + let received_receipt = + ReceivedReceipt::new(signed_receipt, id, &self.required_checks); + Ok((id, received_receipt)) + }) + .collect() + } + + /// We don't need this method in TAP Agent. + async fn update_receipt_by_id( + &self, + _receipt_id: u64, + _receipt: ReceivedReceipt, + ) -> Result<(), Self::AdapterError> { + panic!("Not implemented"); + } + + async fn remove_receipts_in_timestamp_range + Send>( + &self, + timestamp_ns: R, + ) -> Result<(), Self::AdapterError> { + sqlx::query!( + r#" + DELETE FROM scalar_tap_receipts + WHERE allocation_id = $1 AND sender_address = $2 AND $3::numrange @> timestamp_ns + "#, + self.allocation_id + .to_string() + .strip_prefix("0x") + .unwrap() + .to_owned(), + self.sender + .to_string() + .strip_prefix("0x") + .unwrap() + .to_owned(), + rangebounds_to_pgrange(timestamp_ns) + ) + .execute(&self.pgpool) + .await?; + Ok(()) + } +} + +impl ReceiptStorageAdapter { + pub fn new( + pgpool: PgPool, + allocation_id: Address, + sender: Address, + required_checks: Vec, + ) -> Self { + Self { + pgpool, + allocation_id, + sender, + required_checks, + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::tap::test_utils::{ + create_received_receipt, store_receipt, ALLOCATION_ID, ALLOCATION_ID_IRRELEVANT, SENDER, + SENDER_IRRELEVANT, TAP_EIP712_DOMAIN_SEPARATOR, + }; + use anyhow::Result; + use serde_json::Value; + use sqlx::PgPool; + use tap_core::tap_receipt::get_full_list_of_checks; + + /// This function compares a local receipts vector filter by timestamp range (we assume that the stdlib + /// implementation is correct) with the receipts vector retrieved from the database using + /// retrieve_receipts_in_timestamp_range. + async fn retrieve_range_and_check + Send>( + storage_adapter: &ReceiptStorageAdapter, + received_receipt_vec: &[(u64, ReceivedReceipt)], + range: R, + ) -> Result<()> { + // Filtering the received receipts by timestamp range + let received_receipt_vec: Vec<(u64, ReceivedReceipt)> = received_receipt_vec + .iter() + .filter(|(_, received_receipt)| { + range.contains(&received_receipt.signed_receipt().message.timestamp_ns) + && (received_receipt.signed_receipt().message.allocation_id + == storage_adapter.allocation_id) + && (received_receipt + .signed_receipt() + .recover_signer(&TAP_EIP712_DOMAIN_SEPARATOR) + .unwrap() + == storage_adapter.sender) + }) + .cloned() + .collect(); + + // Retrieving receipts in timestamp range from the database, convert to json Value + let recovered_received_receipt_vec = storage_adapter + .retrieve_receipts_in_timestamp_range(range) + .await? + .iter() + .map(|(id, received_receipt)| { + let mut received_receipt = serde_json::to_value(received_receipt).unwrap(); + + // We set all query_id to 0 because we don't care about it in this test and it + // makes it easier. + received_receipt["query_id"] = Value::from(0u64); + (*id, received_receipt) + }) + .collect::>(); + + // Check length + assert_eq!( + recovered_received_receipt_vec.len(), + received_receipt_vec.len() + ); + + // Checking that the receipts in recovered_received_receipt_vec are the same as + // the ones in received_receipt_vec + assert!(received_receipt_vec.iter().all(|(id, received_receipt)| { + recovered_received_receipt_vec + .contains(&(*id, serde_json::to_value(received_receipt).unwrap())) + })); + Ok(()) + } + + async fn remove_range_and_check + Send>( + storage_adapter: &ReceiptStorageAdapter, + received_receipt_vec: &[ReceivedReceipt], + range: R, + ) -> Result<()> { + // Storing the receipts + let mut received_receipt_id_vec = Vec::new(); + for received_receipt in received_receipt_vec.iter() { + received_receipt_id_vec.push( + store_receipt(&storage_adapter.pgpool, received_receipt.signed_receipt()) + .await + .unwrap(), + ); + } + + // zip the 2 vectors together + let received_receipt_vec = received_receipt_id_vec + .into_iter() + .zip(received_receipt_vec.iter()) + .collect::>(); + + // Remove the received receipts by timestamp range for the correct (allocation_id, + // sender) + let received_receipt_vec: Vec<(u64, &ReceivedReceipt)> = received_receipt_vec + .iter() + .filter(|(_, received_receipt)| { + if (received_receipt.signed_receipt().message.allocation_id + == storage_adapter.allocation_id) + && (received_receipt + .signed_receipt() + .recover_signer(&TAP_EIP712_DOMAIN_SEPARATOR) + .unwrap() + == storage_adapter.sender) + { + !range.contains(&received_receipt.signed_receipt().message.timestamp_ns) + } else { + true + } + // !range.contains(&received_receipt.signed_receipt().message.timestamp_ns) + }) + .cloned() + .collect(); + + // Removing the received receipts in timestamp range from the database + storage_adapter + .remove_receipts_in_timestamp_range(range) + .await?; + + // Retrieving all receipts in DB (including irrelevant ones) + let records = sqlx::query!( + r#" + SELECT receipt + FROM scalar_tap_receipts + "# + ) + .fetch_all(&storage_adapter.pgpool) + .await?; + + // Check length + assert_eq!(records.len(), received_receipt_vec.len()); + + // Retrieving all receipts in DB (including irrelevant ones) + let recovered_received_receipt_set: Vec = records + .into_iter() + .map(|record| { + let signed_receipt: SignedReceipt = serde_json::from_value(record.receipt).unwrap(); + let reveived_receipt = + ReceivedReceipt::new(signed_receipt, 0, &get_full_list_of_checks()); + serde_json::to_value(reveived_receipt).unwrap() + }) + .collect(); + + // Check values recovered_received_receipt_set contains values received_receipt_vec + assert!(received_receipt_vec.iter().all(|(_, received_receipt)| { + recovered_received_receipt_set + .contains(&serde_json::to_value(received_receipt).unwrap()) + })); + + // Removing all the receipts in the DB + sqlx::query!( + r#" + DELETE FROM scalar_tap_receipts + "# + ) + .execute(&storage_adapter.pgpool) + .await?; + + // Checking that there are no receipts left + let scalar_tap_receipts_db_count: i64 = sqlx::query!( + r#" + SELECT count(*) + FROM scalar_tap_receipts + "# + ) + .fetch_one(&storage_adapter.pgpool) + .await? + .count + .unwrap(); + assert_eq!(scalar_tap_receipts_db_count, 0); + Ok(()) + } + + #[sqlx::test(migrations = "../migrations")] + async fn retrieve_receipts_in_timestamp_range(pgpool: PgPool) { + let storage_adapter = ReceiptStorageAdapter::new( + pgpool.clone(), + *ALLOCATION_ID, + SENDER.1, + get_full_list_of_checks(), + ); + + // Creating 10 receipts with timestamps 42 to 51 + let mut received_receipt_vec = Vec::new(); + for i in 0..10 { + received_receipt_vec.push( + create_received_receipt( + &ALLOCATION_ID, + &SENDER.0, + i + 684, + i + 42, + (i + 124).into(), + 0, + ) + .await, + ); + + // Adding irrelevant receipts to make sure they are not retrieved + received_receipt_vec.push( + create_received_receipt( + &ALLOCATION_ID_IRRELEVANT, + &SENDER.0, + i + 684, + i + 42, + (i + 124).into(), + 0, + ) + .await, + ); + received_receipt_vec.push( + create_received_receipt( + &ALLOCATION_ID, + &SENDER_IRRELEVANT.0, + i + 684, + i + 42, + (i + 124).into(), + 0, + ) + .await, + ); + } + + // Storing the receipts + let mut received_receipt_id_vec = Vec::new(); + for received_receipt in received_receipt_vec.iter() { + received_receipt_id_vec.push( + store_receipt(&pgpool, received_receipt.signed_receipt()) + .await + .unwrap(), + ); + } + + // zip the 2 vectors together + let received_receipt_vec = received_receipt_id_vec + .into_iter() + .zip(received_receipt_vec.into_iter()) + .collect::>(); + + macro_rules! test_ranges{ + ($($arg: expr), +) => { + { + $( + assert!( + retrieve_range_and_check(&storage_adapter, &received_receipt_vec, $arg) + .await + .is_ok()); + )+ + } + }; + } + + #[allow(clippy::reversed_empty_ranges)] + { + test_ranges!( + .., + ..41, + ..42, + ..43, + ..50, + ..51, + ..52, + ..=41, + ..=42, + ..=43, + ..=50, + ..=51, + ..=52, + 21..=41, + 21..=42, + 21..=43, + 21..=50, + 21..=51, + 21..=52, + 41..=41, + 41..=42, + 41..=43, + 41..=50, + 50..=48, + 41..=51, + 41..=52, + 51..=51, + 51..=52, + 21..41, + 21..42, + 21..43, + 21..50, + 21..51, + 21..52, + 41..41, + 41..42, + 41..43, + 41..50, + 50..48, + 41..51, + 41..52, + 51..51, + 51..52, + 41.., + 42.., + 43.., + 50.., + 51.., + 52.., + (Bound::Excluded(42), Bound::Excluded(43)), + (Bound::Excluded(43), Bound::Excluded(43)), + (Bound::Excluded(43), Bound::Excluded(44)), + (Bound::Excluded(43), Bound::Excluded(45)) + ); + } + } + + #[sqlx::test(migrations = "../migrations")] + async fn remove_receipts_in_timestamp_range(pgpool: PgPool) { + let storage_adapter = + ReceiptStorageAdapter::new(pgpool, *ALLOCATION_ID, SENDER.1, get_full_list_of_checks()); + + // Creating 10 receipts with timestamps 42 to 51 + let mut received_receipt_vec = Vec::new(); + for i in 0..10 { + received_receipt_vec.push( + create_received_receipt( + &ALLOCATION_ID, + &SENDER.0, + i + 684, + i + 42, + (i + 124).into(), + 0, + ) + .await, + ); + + // Adding irrelevant receipts to make sure they are not retrieved + received_receipt_vec.push( + create_received_receipt( + &ALLOCATION_ID_IRRELEVANT, + &SENDER.0, + i + 684, + i + 42, + (i + 124).into(), + 0, + ) + .await, + ); + received_receipt_vec.push( + create_received_receipt( + &ALLOCATION_ID, + &SENDER_IRRELEVANT.0, + i + 684, + i + 42, + (i + 124).into(), + 0, + ) + .await, + ); + } + + macro_rules! test_ranges{ + ($($arg: expr), +) => { + { + $( + assert!( + remove_range_and_check(&storage_adapter, &received_receipt_vec, $arg) + .await.is_ok() + ); + ) + + } + }; + } + + #[allow(clippy::reversed_empty_ranges)] + { + test_ranges!( + .., + ..41, + ..42, + ..43, + ..50, + ..51, + ..52, + ..=41, + ..=42, + ..=43, + ..=50, + ..=51, + ..=52, + 21..=41, + 21..=42, + 21..=43, + 21..=50, + 21..=51, + 21..=52, + 41..=41, + 41..=42, + 41..=43, + 41..=50, + 50..=48, + 41..=51, + 41..=52, + 51..=51, + 51..=52, + 21..41, + 21..42, + 21..43, + 21..50, + 21..51, + 21..52, + 41..41, + 41..42, + 41..43, + 41..50, + 50..48, + 41..51, + 41..52, + 51..51, + 51..52, + 41.., + 42.., + 43.., + 50.., + 51.., + 52.., + (Bound::Excluded(42), Bound::Excluded(43)), + (Bound::Excluded(43), Bound::Excluded(43)), + (Bound::Excluded(43), Bound::Excluded(44)), + (Bound::Excluded(43), Bound::Excluded(45)) + ); + } + } +} diff --git a/tap_agent/src/tap/test_utils.rs b/tap_agent/src/tap/test_utils.rs new file mode 100644 index 00000000..5bd592f7 --- /dev/null +++ b/tap_agent/src/tap/test_utils.rs @@ -0,0 +1,148 @@ +// Copyright 2023-, GraphOps and Semiotic Labs. +// SPDX-License-Identifier: Apache-2.0 + +use std::str::FromStr; + +use alloy_primitives::Address; +use alloy_sol_types::{eip712_domain, Eip712Domain}; +use anyhow::Result; +use ethers_signers::{coins_bip39::English, LocalWallet, MnemonicBuilder, Signer}; +use lazy_static::lazy_static; +use serde_json; +use sqlx::{types::BigDecimal, PgPool}; +use tap_core::receipt_aggregate_voucher::ReceiptAggregateVoucher; +use tap_core::tap_manager::{SignedRAV, SignedReceipt}; +use tap_core::tap_receipt::{get_full_list_of_checks, ReceivedReceipt}; +use tap_core::{eip_712_signed_message::EIP712SignedMessage, tap_receipt::Receipt}; + +lazy_static! { + pub static ref ALLOCATION_ID: Address = + Address::from_str("0xabababababababababababababababababababab").unwrap(); + pub static ref ALLOCATION_ID_IRRELEVANT: Address = + Address::from_str("0xbcdebcdebcdebcdebcdebcdebcdebcdebcdebcde").unwrap(); + pub static ref SENDER: (LocalWallet, Address) = wallet(0); + pub static ref SENDER_IRRELEVANT: (LocalWallet, Address) = wallet(1); + pub static ref INDEXER: (LocalWallet, Address) = wallet(2); + pub static ref TAP_EIP712_DOMAIN_SEPARATOR: Eip712Domain = eip712_domain! { + name: "TAP", + version: "1", + chain_id: 1, + verifying_contract: Address:: from([0x11u8; 20]), + }; +} + +/// Fixture to generate a wallet and address +pub fn wallet(index: u32) -> (LocalWallet, Address) { + let wallet: LocalWallet = MnemonicBuilder::::default() + .phrase("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about") + .index(index) + .unwrap() + .build() + .unwrap(); + let address = wallet.address(); + (wallet, Address::from_slice(address.as_bytes())) +} + +/// Fixture to generate a signed receipt using the wallet from `keys()` and the +/// given `query_id` and `value` +pub async fn create_received_receipt( + allocation_id: &Address, + sender_wallet: &LocalWallet, + nonce: u64, + timestamp_ns: u64, + value: u128, + query_id: u64, +) -> ReceivedReceipt { + let receipt = EIP712SignedMessage::new( + &TAP_EIP712_DOMAIN_SEPARATOR, + Receipt { + allocation_id: *allocation_id, + nonce, + timestamp_ns, + value, + }, + sender_wallet, + ) + .await + .unwrap(); + ReceivedReceipt::new(receipt, query_id, &get_full_list_of_checks()) +} + +/// Fixture to generate a RAV using the wallet from `keys()` +pub async fn create_rav( + allocation_id: Address, + sender_wallet: LocalWallet, + timestamp_ns: u64, + value_aggregate: u128, +) -> SignedRAV { + EIP712SignedMessage::new( + &TAP_EIP712_DOMAIN_SEPARATOR, + ReceiptAggregateVoucher { + allocation_id, + timestamp_ns, + value_aggregate, + }, + &sender_wallet, + ) + .await + .unwrap() +} + +pub async fn store_receipt(pgpool: &PgPool, signed_receipt: SignedReceipt) -> Result { + let record = sqlx::query!( + r#" + INSERT INTO scalar_tap_receipts ( + allocation_id, sender_address, timestamp_ns, value, receipt + ) + VALUES ($1, $2, $3, $4, $5) + RETURNING id + "#, + signed_receipt + .message + .allocation_id + .to_string() + .strip_prefix("0x") + .unwrap() + .to_owned(), + signed_receipt + .recover_signer(&TAP_EIP712_DOMAIN_SEPARATOR) + .unwrap() + .to_string() + .strip_prefix("0x") + .unwrap() + .to_owned(), + BigDecimal::from(signed_receipt.message.timestamp_ns), + BigDecimal::from_str(&signed_receipt.message.value.to_string())?, + serde_json::to_value(signed_receipt)? + ) + .fetch_one(pgpool) + .await?; + + // id is BIGSERIAL, so it should be safe to cast to u64. + let id: u64 = record.id.try_into()?; + Ok(id) +} + +pub async fn store_rav(pgpool: &PgPool, signed_rav: SignedRAV, sender: Address) -> Result<()> { + sqlx::query!( + r#" + INSERT INTO scalar_tap_latest_ravs ( + allocation_id, sender_address, rav + ) + VALUES ($1, $2, $3) + "#, + signed_rav + .message + .allocation_id + .to_string() + .strip_prefix("0x") + .unwrap() + .to_owned(), + sender.to_string().strip_prefix("0x").unwrap().to_owned(), + serde_json::to_value(signed_rav).unwrap(), + ) + .execute(pgpool) + .await?; + + Ok(()) +} From 755cad059658c6d05f42414a416997722e28eb53 Mon Sep 17 00:00:00 2001 From: Alexis Asseman Date: Thu, 26 Oct 2023 15:23:44 -0700 Subject: [PATCH 02/18] chore: silence one clippy::too_many_arguments Signed-off-by: Alexis Asseman --- tap_agent/src/tap/account.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tap_agent/src/tap/account.rs b/tap_agent/src/tap/account.rs index 03c5a69e..c5796c2d 100644 --- a/tap_agent/src/tap/account.rs +++ b/tap_agent/src/tap/account.rs @@ -78,6 +78,7 @@ pub struct Account { } impl Account { + #[allow(clippy::too_many_arguments)] pub fn new( config: &'static config::Cli, pgpool: PgPool, From 96d0f5b3ad33b813e7130e8c8c81415654ab09d2 Mon Sep 17 00:00:00 2001 From: Alexis Asseman Date: Thu, 26 Oct 2023 18:06:15 -0700 Subject: [PATCH 03/18] ci: add postgres/sqlx to clippy job Signed-off-by: Alexis Asseman --- .github/workflows/tests.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c9b326a1..0f8fffa8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -21,8 +21,22 @@ jobs: clippy: name: cargo clippy runs-on: ubuntu-latest + services: + postgres: + image: postgres:15 + env: + POSTGRES_HOST_AUTH_METHOD: trust + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 container: image: rust:latest + env: + DATABASE_URL: postgres://postgres@postgres:5432 steps: - uses: actions/checkout@v3 - uses: actions/cache@v3 @@ -34,6 +48,10 @@ jobs: ~/.cargo/git/db/ target/ key: ${{ runner.os }}-cargo-clippy-${{ hashFiles('**/Cargo.lock') }} + - name: Install sqlx + run: cargo install sqlx-cli --no-default-features --features postgres + - name: Run the test sqlx migrations + run: cargo sqlx migrate run - run: | rustup component add clippy # Temporarily allowing dead-code, while denying all other warnings From c66e5e546f6ce5e1970f25da3e78047bf35bc046 Mon Sep 17 00:00:00 2001 From: Alexis Asseman Date: Mon, 30 Oct 2023 13:43:34 -0700 Subject: [PATCH 04/18] refactor(tap_agent): trim_start_matches addresses Signed-off-by: Alexis Asseman --- common/src/tap_manager.rs | 6 ++-- tap_agent/src/tap/account.rs | 30 ++++++-------------- tap_agent/src/tap/rav_storage_adapter.rs | 18 +++--------- tap_agent/src/tap/receipt_storage_adapter.rs | 18 +++--------- tap_agent/src/tap/test_utils.rs | 11 +++---- 5 files changed, 22 insertions(+), 61 deletions(-) diff --git a/common/src/tap_manager.rs b/common/src/tap_manager.rs index 78db52c8..50bc8849 100644 --- a/common/src/tap_manager.rs +++ b/common/src/tap_manager.rs @@ -86,13 +86,11 @@ impl TapManager { VALUES ($1, $2, $3, $4, $5) "#, format!("{:?}", allocation_id) - .strip_prefix("0x") - .unwrap() + .trim_start_matches("0x") .to_owned(), receipt_signer .to_string() - .strip_prefix("0x") - .unwrap() + .trim_start_matches("0x") .to_owned(), BigDecimal::from(receipt.message.timestamp_ns), BigDecimal::from_str(&receipt.message.value.to_string())?, diff --git a/tap_agent/src/tap/account.rs b/tap_agent/src/tap/account.rs index c5796c2d..514756f3 100644 --- a/tap_agent/src/tap/account.rs +++ b/tap_agent/src/tap/account.rs @@ -242,15 +242,9 @@ impl Account { inner .allocation_id .to_string() - .strip_prefix("0x") - .unwrap() + .trim_start_matches("0x") .to_owned(), - inner - .sender - .to_string() - .strip_prefix("0x") - .unwrap() - .to_owned() + inner.sender.to_string().trim_start_matches("0x").to_owned() ) .fetch_one(&inner.pgpool) .await?; @@ -370,15 +364,9 @@ impl Account { inner .allocation_id .to_string() - .strip_prefix("0x") - .unwrap() - .to_owned(), - inner - .sender - .to_string() - .strip_prefix("0x") - .unwrap() + .trim_start_matches("0x") .to_owned(), + inner.sender.to_string().trim_start_matches("0x").to_owned(), ) .fetch_all(&inner.pgpool) .await?; @@ -766,10 +754,9 @@ mod tests { "#, ALLOCATION_ID .to_string() - .strip_prefix("0x") - .unwrap() + .trim_start_matches("0x") .to_owned(), - SENDER.1.to_string().strip_prefix("0x").unwrap().to_owned() + SENDER.1.to_string().trim_start_matches("0x").to_owned() ) .fetch_optional(&pgpool) .await @@ -830,10 +817,9 @@ mod tests { "#, ALLOCATION_ID .to_string() - .strip_prefix("0x") - .unwrap() + .trim_start_matches("0x") .to_owned(), - SENDER.1.to_string().strip_prefix("0x").unwrap().to_owned() + SENDER.1.to_string().trim_start_matches("0x").to_owned() ) .fetch_optional(&pgpool) .await diff --git a/tap_agent/src/tap/rav_storage_adapter.rs b/tap_agent/src/tap/rav_storage_adapter.rs index f2dcd578..ad8115fc 100644 --- a/tap_agent/src/tap/rav_storage_adapter.rs +++ b/tap_agent/src/tap/rav_storage_adapter.rs @@ -36,14 +36,9 @@ impl RAVStorageAdapterTrait for RAVStorageAdapter { "#, self.allocation_id .to_string() - .strip_prefix("0x") - .unwrap() - .to_owned(), - self.sender - .to_string() - .strip_prefix("0x") - .unwrap() + .trim_start_matches("0x") .to_owned(), + self.sender.to_string().trim_start_matches("0x").to_owned(), serde_json::to_value(rav).map_err(|e| AdapterError::AdapterError { error: e.to_string() })? @@ -65,14 +60,9 @@ impl RAVStorageAdapterTrait for RAVStorageAdapter { "#, self.allocation_id .to_string() - .strip_prefix("0x") - .unwrap() + .trim_start_matches("0x") .to_owned(), - self.sender - .to_string() - .strip_prefix("0x") - .unwrap() - .to_owned() + self.sender.to_string().trim_start_matches("0x").to_owned() ) .fetch_optional(&self.pgpool) .await diff --git a/tap_agent/src/tap/receipt_storage_adapter.rs b/tap_agent/src/tap/receipt_storage_adapter.rs index 4d911659..615aa54d 100644 --- a/tap_agent/src/tap/receipt_storage_adapter.rs +++ b/tap_agent/src/tap/receipt_storage_adapter.rs @@ -106,14 +106,9 @@ impl ReceiptStorageAdapterTrait for ReceiptStorageAdapter { "#, self.allocation_id .to_string() - .strip_prefix("0x") - .unwrap() - .to_owned(), - self.sender - .to_string() - .strip_prefix("0x") - .unwrap() + .trim_start_matches("0x") .to_owned(), + self.sender.to_string().trim_start_matches("0x").to_owned(), rangebounds_to_pgrange(timestamp_range_ns) ) .fetch_all(&self.pgpool) @@ -150,14 +145,9 @@ impl ReceiptStorageAdapterTrait for ReceiptStorageAdapter { "#, self.allocation_id .to_string() - .strip_prefix("0x") - .unwrap() - .to_owned(), - self.sender - .to_string() - .strip_prefix("0x") - .unwrap() + .trim_start_matches("0x") .to_owned(), + self.sender.to_string().trim_start_matches("0x").to_owned(), rangebounds_to_pgrange(timestamp_ns) ) .execute(&self.pgpool) diff --git a/tap_agent/src/tap/test_utils.rs b/tap_agent/src/tap/test_utils.rs index 5bd592f7..e641f042 100644 --- a/tap_agent/src/tap/test_utils.rs +++ b/tap_agent/src/tap/test_utils.rs @@ -101,15 +101,13 @@ pub async fn store_receipt(pgpool: &PgPool, signed_receipt: SignedReceipt) -> Re .message .allocation_id .to_string() - .strip_prefix("0x") - .unwrap() + .trim_start_matches("0x") .to_owned(), signed_receipt .recover_signer(&TAP_EIP712_DOMAIN_SEPARATOR) .unwrap() .to_string() - .strip_prefix("0x") - .unwrap() + .trim_start_matches("0x") .to_owned(), BigDecimal::from(signed_receipt.message.timestamp_ns), BigDecimal::from_str(&signed_receipt.message.value.to_string())?, @@ -135,10 +133,9 @@ pub async fn store_rav(pgpool: &PgPool, signed_rav: SignedRAV, sender: Address) .message .allocation_id .to_string() - .strip_prefix("0x") - .unwrap() + .trim_start_matches("0x") .to_owned(), - sender.to_string().strip_prefix("0x").unwrap().to_owned(), + sender.to_string().trim_start_matches("0x").to_owned(), serde_json::to_value(signed_rav).unwrap(), ) .execute(pgpool) From 75c90dca8fe3975692945cc483ee9e883b767a39 Mon Sep 17 00:00:00 2001 From: Alexis Asseman Date: Thu, 2 Nov 2023 14:44:30 -0700 Subject: [PATCH 05/18] fix: table name `scalar_tap_ravs` discrepancy Was `scalar_tap_latest_ravs` everywhere except in the down migration. Opted for the shorter of the 2 everywhere. Signed-off-by: Alexis Asseman --- ...ddcfb586bc049fa2d61740c400ae64d33d3b8.json | 35 ------------------- ...021befec9a5efd2f9d39078d9b45f79c1f31a.json | 16 --------- ...952e980b713a79b197f1fe17ea1c6c9d07ec.json} | 4 +-- ...e3ffe3109b63b14aa746eacdb81288f919c16.json | 35 +++++++++++++++++++ ...3226c8a792538eed5ae1397f5515ef43f52af.json | 16 --------- ...da79d651c366e54ed95687e546be1906dc48a.json | 16 +++++++++ ...623b86576e4338009ec2fb4cd1e4b64e9f368.json | 16 +++++++++ ...446de1cc6b352f33825ebc4ae2de03da4263.json} | 6 ++-- ...5adf6a3900d89553bdec541201fd3e282d83.json} | 4 +-- ...397ad89619d6e111741254864e3a1eaeaa7a.json} | 4 +-- migrations/20230915230734_tap_ravs.up.sql | 2 +- tap_agent/src/tap/account.rs | 8 ++--- tap_agent/src/tap/rav_storage_adapter.rs | 4 +-- tap_agent/src/tap/test_utils.rs | 2 +- 14 files changed, 85 insertions(+), 83 deletions(-) delete mode 100644 .sqlx/query-0e46530ba12da19cedaebaab66bddcfb586bc049fa2d61740c400ae64d33d3b8.json delete mode 100644 .sqlx/query-26e4dda54e43d15e3c747982c6c021befec9a5efd2f9d39078d9b45f79c1f31a.json rename .sqlx/{query-c4014f5e68d5a00d47138828d941d3afe596556e730b48fb31edfaf14096d3dd.json => query-276ccf58e47495578a7d6448e75f952e980b713a79b197f1fe17ea1c6c9d07ec.json} (65%) create mode 100644 .sqlx/query-46a50e4bbfb5f7c57bb42fcfa17e3ffe3109b63b14aa746eacdb81288f919c16.json delete mode 100644 .sqlx/query-b3726609aaf6bbeb8a08cbcc10f3226c8a792538eed5ae1397f5515ef43f52af.json create mode 100644 .sqlx/query-b977d0e0ce35557f5829e797975da79d651c366e54ed95687e546be1906dc48a.json create mode 100644 .sqlx/query-bdb90bbff5d073cee9665a99601623b86576e4338009ec2fb4cd1e4b64e9f368.json rename .sqlx/{query-17c73722e03764f1bd30f11d6918c0e4df41a5504a3acc779d82fcdf7fdd4e79.json => query-be914423c719351af8ec785671d8446de1cc6b352f33825ebc4ae2de03da4263.json} (52%) rename .sqlx/{query-089809ab6cd948db1adb1cedff47f5b1ce3c8594b3657436a6cd68d683ef8190.json => query-d47a86f9686f9c72d2d5047d4c315adf6a3900d89553bdec541201fd3e282d83.json} (67%) rename .sqlx/{query-d7ac08a02062e6cc1a252567431b3f102b32750519586a393c3a72c297f10d7e.json => query-f0569836ad31081ca3cf7406ef5d397ad89619d6e111741254864e3a1eaeaa7a.json} (62%) diff --git a/.sqlx/query-0e46530ba12da19cedaebaab66bddcfb586bc049fa2d61740c400ae64d33d3b8.json b/.sqlx/query-0e46530ba12da19cedaebaab66bddcfb586bc049fa2d61740c400ae64d33d3b8.json deleted file mode 100644 index ecc6face..00000000 --- a/.sqlx/query-0e46530ba12da19cedaebaab66bddcfb586bc049fa2d61740c400ae64d33d3b8.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n WITH rav AS (\n SELECT \n rav -> 'message' ->> 'timestamp_ns' AS timestamp_ns \n FROM \n scalar_tap_latest_ravs \n WHERE \n allocation_id = $1 \n AND sender_address = $2\n ) \n SELECT \n COUNT(*), \n MAX(id), \n SUM(value) \n FROM \n scalar_tap_receipts \n WHERE \n allocation_id = $1 \n AND sender_address = $2 \n AND CASE WHEN (\n SELECT \n timestamp_ns :: NUMERIC \n FROM \n rav\n ) IS NOT NULL THEN timestamp_ns > (\n SELECT \n timestamp_ns :: NUMERIC \n FROM \n rav\n ) ELSE TRUE END\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "count", - "type_info": "Int8" - }, - { - "ordinal": 1, - "name": "max", - "type_info": "Int8" - }, - { - "ordinal": 2, - "name": "sum", - "type_info": "Numeric" - } - ], - "parameters": { - "Left": [ - "Bpchar", - "Bpchar" - ] - }, - "nullable": [ - null, - null, - null - ] - }, - "hash": "0e46530ba12da19cedaebaab66bddcfb586bc049fa2d61740c400ae64d33d3b8" -} diff --git a/.sqlx/query-26e4dda54e43d15e3c747982c6c021befec9a5efd2f9d39078d9b45f79c1f31a.json b/.sqlx/query-26e4dda54e43d15e3c747982c6c021befec9a5efd2f9d39078d9b45f79c1f31a.json deleted file mode 100644 index c27abc7a..00000000 --- a/.sqlx/query-26e4dda54e43d15e3c747982c6c021befec9a5efd2f9d39078d9b45f79c1f31a.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO scalar_tap_latest_ravs (\n allocation_id, sender_address, rav\n )\n VALUES ($1, $2, $3)\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Bpchar", - "Bpchar", - "Json" - ] - }, - "nullable": [] - }, - "hash": "26e4dda54e43d15e3c747982c6c021befec9a5efd2f9d39078d9b45f79c1f31a" -} diff --git a/.sqlx/query-c4014f5e68d5a00d47138828d941d3afe596556e730b48fb31edfaf14096d3dd.json b/.sqlx/query-276ccf58e47495578a7d6448e75f952e980b713a79b197f1fe17ea1c6c9d07ec.json similarity index 65% rename from .sqlx/query-c4014f5e68d5a00d47138828d941d3afe596556e730b48fb31edfaf14096d3dd.json rename to .sqlx/query-276ccf58e47495578a7d6448e75f952e980b713a79b197f1fe17ea1c6c9d07ec.json index 66daf74b..e935e7b7 100644 --- a/.sqlx/query-c4014f5e68d5a00d47138828d941d3afe596556e730b48fb31edfaf14096d3dd.json +++ b/.sqlx/query-276ccf58e47495578a7d6448e75f952e980b713a79b197f1fe17ea1c6c9d07ec.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT rav\n FROM scalar_tap_latest_ravs\n WHERE allocation_id = $1 AND sender_address = $2\n ", + "query": "\n SELECT rav\n FROM scalar_tap_ravs\n WHERE allocation_id = $1 AND sender_address = $2\n ", "describe": { "columns": [ { @@ -19,5 +19,5 @@ false ] }, - "hash": "c4014f5e68d5a00d47138828d941d3afe596556e730b48fb31edfaf14096d3dd" + "hash": "276ccf58e47495578a7d6448e75f952e980b713a79b197f1fe17ea1c6c9d07ec" } diff --git a/.sqlx/query-46a50e4bbfb5f7c57bb42fcfa17e3ffe3109b63b14aa746eacdb81288f919c16.json b/.sqlx/query-46a50e4bbfb5f7c57bb42fcfa17e3ffe3109b63b14aa746eacdb81288f919c16.json new file mode 100644 index 00000000..a166ca8d --- /dev/null +++ b/.sqlx/query-46a50e4bbfb5f7c57bb42fcfa17e3ffe3109b63b14aa746eacdb81288f919c16.json @@ -0,0 +1,35 @@ +{ + "db_name": "PostgreSQL", + "query": "\n WITH rav AS (\n SELECT \n rav -> 'message' ->> 'timestamp_ns' AS timestamp_ns \n FROM \n scalar_tap_ravs \n WHERE \n allocation_id = $1 \n AND sender_address = $2\n ) \n SELECT \n COUNT(*), \n MAX(id), \n SUM(value) \n FROM \n scalar_tap_receipts \n WHERE \n allocation_id = $1 \n AND sender_address = $2 \n AND CASE WHEN (\n SELECT \n timestamp_ns :: NUMERIC \n FROM \n rav\n ) IS NOT NULL THEN timestamp_ns > (\n SELECT \n timestamp_ns :: NUMERIC \n FROM \n rav\n ) ELSE TRUE END\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "max", + "type_info": "Int8" + }, + { + "ordinal": 2, + "name": "sum", + "type_info": "Numeric" + } + ], + "parameters": { + "Left": [ + "Bpchar", + "Bpchar" + ] + }, + "nullable": [ + null, + null, + null + ] + }, + "hash": "46a50e4bbfb5f7c57bb42fcfa17e3ffe3109b63b14aa746eacdb81288f919c16" +} diff --git a/.sqlx/query-b3726609aaf6bbeb8a08cbcc10f3226c8a792538eed5ae1397f5515ef43f52af.json b/.sqlx/query-b3726609aaf6bbeb8a08cbcc10f3226c8a792538eed5ae1397f5515ef43f52af.json deleted file mode 100644 index 6a189434..00000000 --- a/.sqlx/query-b3726609aaf6bbeb8a08cbcc10f3226c8a792538eed5ae1397f5515ef43f52af.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO scalar_tap_latest_ravs (allocation_id, sender_address, rav)\n VALUES ($1, $2, $3)\n ON CONFLICT (allocation_id, sender_address)\n DO UPDATE SET rav = $3\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Bpchar", - "Bpchar", - "Json" - ] - }, - "nullable": [] - }, - "hash": "b3726609aaf6bbeb8a08cbcc10f3226c8a792538eed5ae1397f5515ef43f52af" -} diff --git a/.sqlx/query-b977d0e0ce35557f5829e797975da79d651c366e54ed95687e546be1906dc48a.json b/.sqlx/query-b977d0e0ce35557f5829e797975da79d651c366e54ed95687e546be1906dc48a.json new file mode 100644 index 00000000..feb6782f --- /dev/null +++ b/.sqlx/query-b977d0e0ce35557f5829e797975da79d651c366e54ed95687e546be1906dc48a.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO scalar_tap_ravs (\n allocation_id, sender_address, rav\n )\n VALUES ($1, $2, $3)\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Bpchar", + "Bpchar", + "Json" + ] + }, + "nullable": [] + }, + "hash": "b977d0e0ce35557f5829e797975da79d651c366e54ed95687e546be1906dc48a" +} diff --git a/.sqlx/query-bdb90bbff5d073cee9665a99601623b86576e4338009ec2fb4cd1e4b64e9f368.json b/.sqlx/query-bdb90bbff5d073cee9665a99601623b86576e4338009ec2fb4cd1e4b64e9f368.json new file mode 100644 index 00000000..bd28ab7b --- /dev/null +++ b/.sqlx/query-bdb90bbff5d073cee9665a99601623b86576e4338009ec2fb4cd1e4b64e9f368.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO scalar_tap_ravs (allocation_id, sender_address, rav)\n VALUES ($1, $2, $3)\n ON CONFLICT (allocation_id, sender_address)\n DO UPDATE SET rav = $3\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Bpchar", + "Bpchar", + "Json" + ] + }, + "nullable": [] + }, + "hash": "bdb90bbff5d073cee9665a99601623b86576e4338009ec2fb4cd1e4b64e9f368" +} diff --git a/.sqlx/query-17c73722e03764f1bd30f11d6918c0e4df41a5504a3acc779d82fcdf7fdd4e79.json b/.sqlx/query-be914423c719351af8ec785671d8446de1cc6b352f33825ebc4ae2de03da4263.json similarity index 52% rename from .sqlx/query-17c73722e03764f1bd30f11d6918c0e4df41a5504a3acc779d82fcdf7fdd4e79.json rename to .sqlx/query-be914423c719351af8ec785671d8446de1cc6b352f33825ebc4ae2de03da4263.json index 045cc9f5..530511f1 100644 --- a/.sqlx/query-17c73722e03764f1bd30f11d6918c0e4df41a5504a3acc779d82fcdf7fdd4e79.json +++ b/.sqlx/query-be914423c719351af8ec785671d8446de1cc6b352f33825ebc4ae2de03da4263.json @@ -1,16 +1,18 @@ { "db_name": "PostgreSQL", - "query": "\n INSERT INTO scalar_tap_receipts (allocation_id, timestamp_ns, receipt)\n VALUES ($1, $2, $3)\n ", + "query": "\n INSERT INTO scalar_tap_receipts (allocation_id, sender_address, timestamp_ns, value, receipt)\n VALUES ($1, $2, $3, $4, $5)\n ", "describe": { "columns": [], "parameters": { "Left": [ "Bpchar", + "Bpchar", + "Numeric", "Numeric", "Json" ] }, "nullable": [] }, - "hash": "17c73722e03764f1bd30f11d6918c0e4df41a5504a3acc779d82fcdf7fdd4e79" + "hash": "be914423c719351af8ec785671d8446de1cc6b352f33825ebc4ae2de03da4263" } diff --git a/.sqlx/query-089809ab6cd948db1adb1cedff47f5b1ce3c8594b3657436a6cd68d683ef8190.json b/.sqlx/query-d47a86f9686f9c72d2d5047d4c315adf6a3900d89553bdec541201fd3e282d83.json similarity index 67% rename from .sqlx/query-089809ab6cd948db1adb1cedff47f5b1ce3c8594b3657436a6cd68d683ef8190.json rename to .sqlx/query-d47a86f9686f9c72d2d5047d4c315adf6a3900d89553bdec541201fd3e282d83.json index 39c2485c..a63ac2ab 100644 --- a/.sqlx/query-089809ab6cd948db1adb1cedff47f5b1ce3c8594b3657436a6cd68d683ef8190.json +++ b/.sqlx/query-d47a86f9686f9c72d2d5047d4c315adf6a3900d89553bdec541201fd3e282d83.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n UPDATE scalar_tap_latest_ravs\n SET is_last = true\n WHERE allocation_id = $1 AND sender_address = $2\n RETURNING *\n ", + "query": "\n UPDATE scalar_tap_ravs\n SET is_last = true\n WHERE allocation_id = $1 AND sender_address = $2\n RETURNING *\n ", "describe": { "columns": [ { @@ -37,5 +37,5 @@ false ] }, - "hash": "089809ab6cd948db1adb1cedff47f5b1ce3c8594b3657436a6cd68d683ef8190" + "hash": "d47a86f9686f9c72d2d5047d4c315adf6a3900d89553bdec541201fd3e282d83" } diff --git a/.sqlx/query-d7ac08a02062e6cc1a252567431b3f102b32750519586a393c3a72c297f10d7e.json b/.sqlx/query-f0569836ad31081ca3cf7406ef5d397ad89619d6e111741254864e3a1eaeaa7a.json similarity index 62% rename from .sqlx/query-d7ac08a02062e6cc1a252567431b3f102b32750519586a393c3a72c297f10d7e.json rename to .sqlx/query-f0569836ad31081ca3cf7406ef5d397ad89619d6e111741254864e3a1eaeaa7a.json index 028b60d7..1aed43b9 100644 --- a/.sqlx/query-d7ac08a02062e6cc1a252567431b3f102b32750519586a393c3a72c297f10d7e.json +++ b/.sqlx/query-f0569836ad31081ca3cf7406ef5d397ad89619d6e111741254864e3a1eaeaa7a.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT DISTINCT allocation_id, sender_address\n FROM scalar_tap_receipts\n ", + "query": "\n SELECT DISTINCT allocation_id, sender_address\n FROM scalar_tap_receipts\n ", "describe": { "columns": [ { @@ -22,5 +22,5 @@ false ] }, - "hash": "d7ac08a02062e6cc1a252567431b3f102b32750519586a393c3a72c297f10d7e" + "hash": "f0569836ad31081ca3cf7406ef5d397ad89619d6e111741254864e3a1eaeaa7a" } diff --git a/migrations/20230915230734_tap_ravs.up.sql b/migrations/20230915230734_tap_ravs.up.sql index 9e4fc11c..9847d283 100644 --- a/migrations/20230915230734_tap_ravs.up.sql +++ b/migrations/20230915230734_tap_ravs.up.sql @@ -1,4 +1,4 @@ -CREATE TABLE IF NOT EXISTS scalar_tap_latest_ravs ( +CREATE TABLE IF NOT EXISTS scalar_tap_ravs ( allocation_id CHAR(40) NOT NULL, sender_address CHAR(40) NOT NULL, rav JSON NOT NULL, diff --git a/tap_agent/src/tap/account.rs b/tap_agent/src/tap/account.rs index 514756f3..dd9519d7 100644 --- a/tap_agent/src/tap/account.rs +++ b/tap_agent/src/tap/account.rs @@ -213,7 +213,7 @@ impl Account { SELECT rav -> 'message' ->> 'timestamp_ns' AS timestamp_ns FROM - scalar_tap_latest_ravs + scalar_tap_ravs WHERE allocation_id = $1 AND sender_address = $2 @@ -356,7 +356,7 @@ impl Account { // Mark the last RAV as last in the DB as a cue for the indexer-agent. let updated_rows = sqlx::query!( r#" - UPDATE scalar_tap_latest_ravs + UPDATE scalar_tap_ravs SET is_last = true WHERE allocation_id = $1 AND sender_address = $2 RETURNING * @@ -749,7 +749,7 @@ mod tests { let latest_rav = sqlx::query!( r#" SELECT rav - FROM scalar_tap_latest_ravs + FROM scalar_tap_ravs WHERE allocation_id = $1 AND sender_address = $2 "#, ALLOCATION_ID @@ -812,7 +812,7 @@ mod tests { let latest_rav = sqlx::query!( r#" SELECT rav - FROM scalar_tap_latest_ravs + FROM scalar_tap_ravs WHERE allocation_id = $1 AND sender_address = $2 "#, ALLOCATION_ID diff --git a/tap_agent/src/tap/rav_storage_adapter.rs b/tap_agent/src/tap/rav_storage_adapter.rs index ad8115fc..3db3312c 100644 --- a/tap_agent/src/tap/rav_storage_adapter.rs +++ b/tap_agent/src/tap/rav_storage_adapter.rs @@ -29,7 +29,7 @@ impl RAVStorageAdapterTrait for RAVStorageAdapter { async fn update_last_rav(&self, rav: SignedRAV) -> Result<(), Self::AdapterError> { let _fut = sqlx::query!( r#" - INSERT INTO scalar_tap_latest_ravs (allocation_id, sender_address, rav) + INSERT INTO scalar_tap_ravs (allocation_id, sender_address, rav) VALUES ($1, $2, $3) ON CONFLICT (allocation_id, sender_address) DO UPDATE SET rav = $3 @@ -55,7 +55,7 @@ impl RAVStorageAdapterTrait for RAVStorageAdapter { let latest_rav = sqlx::query!( r#" SELECT rav - FROM scalar_tap_latest_ravs + FROM scalar_tap_ravs WHERE allocation_id = $1 AND sender_address = $2 "#, self.allocation_id diff --git a/tap_agent/src/tap/test_utils.rs b/tap_agent/src/tap/test_utils.rs index e641f042..1df900e2 100644 --- a/tap_agent/src/tap/test_utils.rs +++ b/tap_agent/src/tap/test_utils.rs @@ -124,7 +124,7 @@ pub async fn store_receipt(pgpool: &PgPool, signed_receipt: SignedReceipt) -> Re pub async fn store_rav(pgpool: &PgPool, signed_rav: SignedRAV, sender: Address) -> Result<()> { sqlx::query!( r#" - INSERT INTO scalar_tap_latest_ravs ( + INSERT INTO scalar_tap_ravs ( allocation_id, sender_address, rav ) VALUES ($1, $2, $3) From 33782e97f3c1ffe5f29e0e867b55bb48a8d77efe Mon Sep 17 00:00:00 2001 From: Alexis Asseman Date: Thu, 2 Nov 2023 14:48:32 -0700 Subject: [PATCH 06/18] refactor: dashes in bin and crate names Instead of underscores. Signed-off-by: Alexis Asseman --- Cargo.lock | 2 +- Cargo.toml | 2 +- {tap_agent => tap-agent}/Cargo.toml | 4 ++-- {tap_agent => tap-agent}/src/agent.rs | 0 {tap_agent => tap-agent}/src/aggregator_endpoints.rs | 0 {tap_agent => tap-agent}/src/config.rs | 0 {tap_agent => tap-agent}/src/database.rs | 0 {tap_agent => tap-agent}/src/main.rs | 0 {tap_agent => tap-agent}/src/tap/account.rs | 0 {tap_agent => tap-agent}/src/tap/accounts_manager.rs | 0 {tap_agent => tap-agent}/src/tap/escrow_adapter.rs | 0 {tap_agent => tap-agent}/src/tap/mod.rs | 0 {tap_agent => tap-agent}/src/tap/rav_storage_adapter.rs | 0 {tap_agent => tap-agent}/src/tap/receipt_checks_adapter.rs | 0 {tap_agent => tap-agent}/src/tap/receipt_storage_adapter.rs | 0 {tap_agent => tap-agent}/src/tap/test_utils.rs | 0 16 files changed, 4 insertions(+), 4 deletions(-) rename {tap_agent => tap-agent}/Cargo.toml (95%) rename {tap_agent => tap-agent}/src/agent.rs (100%) rename {tap_agent => tap-agent}/src/aggregator_endpoints.rs (100%) rename {tap_agent => tap-agent}/src/config.rs (100%) rename {tap_agent => tap-agent}/src/database.rs (100%) rename {tap_agent => tap-agent}/src/main.rs (100%) rename {tap_agent => tap-agent}/src/tap/account.rs (100%) rename {tap_agent => tap-agent}/src/tap/accounts_manager.rs (100%) rename {tap_agent => tap-agent}/src/tap/escrow_adapter.rs (100%) rename {tap_agent => tap-agent}/src/tap/mod.rs (100%) rename {tap_agent => tap-agent}/src/tap/rav_storage_adapter.rs (100%) rename {tap_agent => tap-agent}/src/tap/receipt_checks_adapter.rs (100%) rename {tap_agent => tap-agent}/src/tap/receipt_storage_adapter.rs (100%) rename {tap_agent => tap-agent}/src/tap/test_utils.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index ae9ddb87..02efb8d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2953,7 +2953,7 @@ dependencies = [ ] [[package]] -name = "indexer_tap_agent" +name = "indexer-tap-agent" version = "0.1.0" dependencies = [ "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index 61b4060f..28f64562 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ members = [ "common", "service", - "tap_agent", + "tap-agent", ] resolver = "2" diff --git a/tap_agent/Cargo.toml b/tap-agent/Cargo.toml similarity index 95% rename from tap_agent/Cargo.toml rename to tap-agent/Cargo.toml index dcf63e78..64aef9af 100644 --- a/tap_agent/Cargo.toml +++ b/tap-agent/Cargo.toml @@ -1,11 +1,11 @@ [package] -name = "indexer_tap_agent" +name = "indexer-tap-agent" version = "0.1.0" edition = "2021" publish = false [[bin]] -name = "indexer_tap_agent" +name = "indexer-tap-agent" path = "src/main.rs" [dependencies] diff --git a/tap_agent/src/agent.rs b/tap-agent/src/agent.rs similarity index 100% rename from tap_agent/src/agent.rs rename to tap-agent/src/agent.rs diff --git a/tap_agent/src/aggregator_endpoints.rs b/tap-agent/src/aggregator_endpoints.rs similarity index 100% rename from tap_agent/src/aggregator_endpoints.rs rename to tap-agent/src/aggregator_endpoints.rs diff --git a/tap_agent/src/config.rs b/tap-agent/src/config.rs similarity index 100% rename from tap_agent/src/config.rs rename to tap-agent/src/config.rs diff --git a/tap_agent/src/database.rs b/tap-agent/src/database.rs similarity index 100% rename from tap_agent/src/database.rs rename to tap-agent/src/database.rs diff --git a/tap_agent/src/main.rs b/tap-agent/src/main.rs similarity index 100% rename from tap_agent/src/main.rs rename to tap-agent/src/main.rs diff --git a/tap_agent/src/tap/account.rs b/tap-agent/src/tap/account.rs similarity index 100% rename from tap_agent/src/tap/account.rs rename to tap-agent/src/tap/account.rs diff --git a/tap_agent/src/tap/accounts_manager.rs b/tap-agent/src/tap/accounts_manager.rs similarity index 100% rename from tap_agent/src/tap/accounts_manager.rs rename to tap-agent/src/tap/accounts_manager.rs diff --git a/tap_agent/src/tap/escrow_adapter.rs b/tap-agent/src/tap/escrow_adapter.rs similarity index 100% rename from tap_agent/src/tap/escrow_adapter.rs rename to tap-agent/src/tap/escrow_adapter.rs diff --git a/tap_agent/src/tap/mod.rs b/tap-agent/src/tap/mod.rs similarity index 100% rename from tap_agent/src/tap/mod.rs rename to tap-agent/src/tap/mod.rs diff --git a/tap_agent/src/tap/rav_storage_adapter.rs b/tap-agent/src/tap/rav_storage_adapter.rs similarity index 100% rename from tap_agent/src/tap/rav_storage_adapter.rs rename to tap-agent/src/tap/rav_storage_adapter.rs diff --git a/tap_agent/src/tap/receipt_checks_adapter.rs b/tap-agent/src/tap/receipt_checks_adapter.rs similarity index 100% rename from tap_agent/src/tap/receipt_checks_adapter.rs rename to tap-agent/src/tap/receipt_checks_adapter.rs diff --git a/tap_agent/src/tap/receipt_storage_adapter.rs b/tap-agent/src/tap/receipt_storage_adapter.rs similarity index 100% rename from tap_agent/src/tap/receipt_storage_adapter.rs rename to tap-agent/src/tap/receipt_storage_adapter.rs diff --git a/tap_agent/src/tap/test_utils.rs b/tap-agent/src/tap/test_utils.rs similarity index 100% rename from tap_agent/src/tap/test_utils.rs rename to tap-agent/src/tap/test_utils.rs From 4389f35a3caffe84c04806e9816d199e2d8003c5 Mon Sep 17 00:00:00 2001 From: Alexis Asseman Date: Thu, 2 Nov 2023 14:51:02 -0700 Subject: [PATCH 07/18] style: fix spaces in macro call Signed-off-by: Alexis Asseman --- tap-agent/src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tap-agent/src/main.rs b/tap-agent/src/main.rs index df4d3be2..68f34e88 100644 --- a/tap-agent/src/main.rs +++ b/tap-agent/src/main.rs @@ -31,8 +31,8 @@ async fn main() -> Result<()> { let mut signal_sigint = signal(SignalKind::interrupt())?; let mut signal_sigterm = signal(SignalKind::terminate())?; tokio::select! { - _ = signal_sigint.recv() => debug !("Received SIGINT."), - _ = signal_sigterm.recv() => debug !("Received SIGTERM."), + _ = signal_sigint.recv() => debug!("Received SIGINT."), + _ = signal_sigterm.recv() => debug!("Received SIGTERM."), } // If we're here, we've received a signal to exit. From a53110b464caf9a9542dc19d20d2c52975f9ada9 Mon Sep 17 00:00:00 2001 From: Alexis Asseman Date: Thu, 2 Nov 2023 15:18:11 -0700 Subject: [PATCH 08/18] refactor: rename Account to SenderAllocationRelationship Signed-off-by: Alexis Asseman --- tap-agent/src/agent.rs | 7 +- tap-agent/src/tap/mod.rs | 4 +- ...t.rs => sender_allocation_relationship.rs} | 199 +++++++++++++----- ...ender_allocation_relationships_manager.rs} | 141 +++++++------ 4 files changed, 232 insertions(+), 119 deletions(-) rename tap-agent/src/tap/{account.rs => sender_allocation_relationship.rs} (82%) rename tap-agent/src/tap/{accounts_manager.rs => sender_allocation_relationships_manager.rs} (72%) diff --git a/tap-agent/src/agent.rs b/tap-agent/src/agent.rs index cd950cdf..5059f211 100644 --- a/tap-agent/src/agent.rs +++ b/tap-agent/src/agent.rs @@ -8,7 +8,10 @@ use indexer_common::prelude::{ escrow_accounts, indexer_allocations, DeploymentDetails, SubgraphClient, }; -use crate::{aggregator_endpoints, config, database, tap::accounts_manager}; +use crate::{ + aggregator_endpoints, config, database, + tap::sender_allocation_relationships_manager::SenderAllocationRelationshipsManager, +}; pub async fn start_agent(config: &'static config::Cli) { let pgpool = database::connect(&config.postgres).await; @@ -76,7 +79,7 @@ pub async fn start_agent(config: &'static config::Cli) { verifying_contract: config.receipts.receipts_verifier_address, }; - let _accounts_manager = accounts_manager::AccountsManager::new( + let _sender_allocation_relationships_manager = SenderAllocationRelationshipsManager::new( config, pgpool, indexer_allocations, diff --git a/tap-agent/src/tap/mod.rs b/tap-agent/src/tap/mod.rs index d648d3da..07e01cb1 100644 --- a/tap-agent/src/tap/mod.rs +++ b/tap-agent/src/tap/mod.rs @@ -1,12 +1,12 @@ // Copyright 2023-, GraphOps and Semiotic Labs. // SPDX-License-Identifier: Apache-2.0 -mod account; -pub mod accounts_manager; mod escrow_adapter; mod rav_storage_adapter; mod receipt_checks_adapter; mod receipt_storage_adapter; +mod sender_allocation_relationship; +pub mod sender_allocation_relationships_manager; #[cfg(test)] pub mod test_utils; diff --git a/tap-agent/src/tap/account.rs b/tap-agent/src/tap/sender_allocation_relationship.rs similarity index 82% rename from tap-agent/src/tap/account.rs rename to tap-agent/src/tap/sender_allocation_relationship.rs index dd9519d7..ed1041df 100644 --- a/tap-agent/src/tap/account.rs +++ b/tap-agent/src/tap/sender_allocation_relationship.rs @@ -23,7 +23,7 @@ use tokio::{ }; use tracing::{error, warn}; -use super::accounts_manager::NewReceiptNotification; +use super::sender_allocation_relationships_manager::NewReceiptNotification; use crate::{ config::{self}, tap::{ @@ -64,20 +64,20 @@ struct Inner { config: &'static config::Cli, } -/// An Account is the relationship between the allocation and the sender in the context of a single -/// allocation. +/// A SenderAllocationRelationship is the relationship between the indexer and the sender in the +/// context of a single allocation. /// -/// Manages the lifecycle of Scalar TAP for the Account, including: +/// Manages the lifecycle of Scalar TAP for the SenderAllocationRelationship, including: /// - Monitoring new receipts and keeping track of the unaggregated fees. /// - Requesting RAVs from the sender's TAP aggregator once the unaggregated fees reach a certain /// threshold. -/// - Requesting the last RAV from the sender's TAP aggregator (on Account EOL) -pub struct Account { +/// - Requesting the last RAV from the sender's TAP aggregator (on SenderAllocationRelationship EOL) +pub struct SenderAllocationRelationship { inner: Arc, rav_requester_task: Arc>>>, } -impl Account { +impl SenderAllocationRelationship { #[allow(clippy::too_many_arguments)] pub fn new( config: &'static config::Cli, @@ -400,8 +400,9 @@ impl Account { } } -impl Drop for Account { - /// Trying to make sure the RAV requester task is dropped when the account is dropped. +impl Drop for SenderAllocationRelationship { + /// Trying to make sure the RAV requester task is dropped when the SenderAllocationRelationship + /// is dropped. fn drop(&mut self) { let rav_requester_task = self.rav_requester_task.clone(); @@ -434,11 +435,11 @@ mod tests { const DUMMY_URL: &str = "http://localhost:1234"; - async fn create_account( + async fn create_sender_allocation_relationship( pgpool: PgPool, sender_aggregator_endpoint: String, escrow_subgraph_endpoint: &str, - ) -> Account { + ) -> SenderAllocationRelationship { let config = Box::leak(Box::new(config::Cli { config: None, ethereum: config::Ethereum { @@ -465,7 +466,7 @@ mod tests { let escrow_adapter = EscrowAdapter::new(escrow_accounts_eventual.clone()); - Account::new( + SenderAllocationRelationship::new( config, pgpool.clone(), *ALLOCATION_ID, @@ -478,13 +479,16 @@ mod tests { ) } - /// Test that the account correctly updates the unaggregated fees from the database when there - /// is no RAV in the database. + /// Test that the sender_allocation_relatioship correctly updates the unaggregated fees from the + /// database when there is no RAV in the database. /// - /// The account should consider all receipts found for the allocation and sender. + /// The sender_allocation_relatioship should consider all receipts found for the allocation and + /// sender. #[sqlx::test(migrations = "../migrations")] async fn test_update_unaggregated_fees_no_rav(pgpool: PgPool) { - let account = create_account(pgpool.clone(), DUMMY_URL.to_string(), DUMMY_URL).await; + let sender_allocation_relatioship = + create_sender_allocation_relationship(pgpool.clone(), DUMMY_URL.to_string(), DUMMY_URL) + .await; // Add receipts to the database. for i in 1..10 { @@ -495,25 +499,39 @@ mod tests { .unwrap(); } - // Let the account update the unaggregated fees from the database. - account.update_unaggregated_fees().await.unwrap(); + // Let the sender_allocation_relatioship update the unaggregated fees from the database. + sender_allocation_relatioship + .update_unaggregated_fees() + .await + .unwrap(); // Check that the unaggregated fees are correct. - assert_eq!(account.inner.unaggregated_fees.lock().await.value, 45u128); + assert_eq!( + sender_allocation_relatioship + .inner + .unaggregated_fees + .lock() + .await + .value, + 45u128 + ); } - /// Test that the account correctly updates the unaggregated fees from the database when there - /// is a RAV in the database as well as receipts which timestamp are lesser and greater than - /// the RAV's timestamp. + /// Test that the sender_allocation_relatioship correctly updates the unaggregated fees from the + /// database when there is a RAV in the database as well as receipts which timestamp are lesser + /// and greater than the RAV's timestamp. /// - /// The account should only consider receipts with a timestamp greater than the RAV's timestamp. + /// The sender_allocation_relatioship should only consider receipts with a timestamp greater + /// than the RAV's timestamp. #[sqlx::test(migrations = "../migrations")] async fn test_update_unaggregated_fees_with_rav(pgpool: PgPool) { - let account = create_account(pgpool.clone(), DUMMY_URL.to_string(), DUMMY_URL).await; + let sender_allocation_relatioship = + create_sender_allocation_relationship(pgpool.clone(), DUMMY_URL.to_string(), DUMMY_URL) + .await; // Add the RAV to the database. - // This RAV has timestamp 4. The Account should only consider receipts with a timestamp - // greater than 4. + // This RAV has timestamp 4. The sender_allocation_relatioship should only consider receipts + // with a timestamp greater than 4. let signed_rav = create_rav(*ALLOCATION_ID, SENDER.0.clone(), 4, 10).await; store_rav(&pgpool, signed_rav, SENDER.1).await.unwrap(); @@ -526,18 +544,32 @@ mod tests { .unwrap(); } - // Let the account update the unaggregated fees from the database. - account.update_unaggregated_fees().await.unwrap(); + // Let the sender_allocation_relatioship update the unaggregated fees from the database. + sender_allocation_relatioship + .update_unaggregated_fees() + .await + .unwrap(); // Check that the unaggregated fees are correct. - assert_eq!(account.inner.unaggregated_fees.lock().await.value, 35u128); + assert_eq!( + sender_allocation_relatioship + .inner + .unaggregated_fees + .lock() + .await + .value, + 35u128 + ); } - /// Test that the account correctly ignores new receipt notifications with an ID lower than - /// the last receipt ID processed (be it from the DB or from a prior receipt notification). + /// Test that the sender_allocation_relatioship correctly ignores new receipt notifications with + /// an ID lower than the last receipt ID processed (be it from the DB or from a prior receipt + /// notification). #[sqlx::test(migrations = "../migrations")] async fn test_handle_new_receipt_notification(pgpool: PgPool) { - let account = create_account(pgpool.clone(), DUMMY_URL.to_string(), DUMMY_URL).await; + let sender_allocation_relatioship = + create_sender_allocation_relationship(pgpool.clone(), DUMMY_URL.to_string(), DUMMY_URL) + .await; // Add receipts to the database. let mut expected_unaggregated_fees = 0u128; @@ -550,11 +582,19 @@ mod tests { expected_unaggregated_fees += u128::from(i); } - account.update_unaggregated_fees().await.unwrap(); + sender_allocation_relatioship + .update_unaggregated_fees() + .await + .unwrap(); // Check that the unaggregated fees are correct. assert_eq!( - account.inner.unaggregated_fees.lock().await.value, + sender_allocation_relatioship + .inner + .unaggregated_fees + .lock() + .await + .value, expected_unaggregated_fees ); @@ -568,13 +608,18 @@ mod tests { timestamp_ns: 19, value: 19, }; - account + sender_allocation_relatioship .handle_new_receipt_notification(new_receipt_notification) .await; // Check that the unaggregated fees have *not* increased. assert_eq!( - account.inner.unaggregated_fees.lock().await.value, + sender_allocation_relatioship + .inner + .unaggregated_fees + .lock() + .await + .value, expected_unaggregated_fees ); @@ -586,14 +631,19 @@ mod tests { timestamp_ns: 20, value: 20, }; - account + sender_allocation_relatioship .handle_new_receipt_notification(new_receipt_notification) .await; expected_unaggregated_fees += 20; // Check that the unaggregated fees are correct. assert_eq!( - account.inner.unaggregated_fees.lock().await.value, + sender_allocation_relatioship + .inner + .unaggregated_fees + .lock() + .await + .value, expected_unaggregated_fees ); @@ -605,13 +655,18 @@ mod tests { timestamp_ns: 19, value: 19, }; - account + sender_allocation_relatioship .handle_new_receipt_notification(new_receipt_notification) .await; // Check that the unaggregated fees have *not* increased. assert_eq!( - account.inner.unaggregated_fees.lock().await.value, + sender_allocation_relatioship + .inner + .unaggregated_fees + .lock() + .await + .value, expected_unaggregated_fees ); } @@ -645,8 +700,8 @@ mod tests { ) .await; - // Create an account. - let account = create_account( + // Create a sender_allocation_relatioship. + let sender_allocation_relatioship = create_sender_allocation_relationship( pgpool.clone(), "http://".to_owned() + &aggregator_endpoint.to_string(), &mock_server.uri(), @@ -662,11 +717,16 @@ mod tests { .unwrap(); } - // Let the account update the unaggregated fees from the database. - account.update_unaggregated_fees().await.unwrap(); + // Let the sender_allocation_relatioship update the unaggregated fees from the database. + sender_allocation_relatioship + .update_unaggregated_fees() + .await + .unwrap(); // Trigger a RAV request manually. - Account::rav_requester_try(&account.inner).await.unwrap(); + SenderAllocationRelationship::rav_requester_try(&sender_allocation_relatioship.inner) + .await + .unwrap(); // Stop the TAP aggregator server. handle.stop().unwrap(); @@ -702,8 +762,8 @@ mod tests { ) .await; - // Create an account. - let account = create_account( + // Create a sender_allocation_relatioship. + let sender_allocation_relatioship = create_sender_allocation_relationship( pgpool.clone(), "http://".to_owned() + &aggregator_endpoint.to_string(), &mock_server.uri(), @@ -724,7 +784,7 @@ mod tests { store_receipt(&pgpool, receipt.signed_receipt()) .await .unwrap(); - account + sender_allocation_relatioship .handle_new_receipt_notification(NewReceiptNotification { allocation_id: *ALLOCATION_ID, sender_address: SENDER.1, @@ -741,7 +801,12 @@ mod tests { } // Wait for the RAV requester to finish. - while Account::rav_requester_task_is_running(&account.rav_requester_task.lock().await) { + while SenderAllocationRelationship::rav_requester_task_is_running( + &sender_allocation_relatioship + .rav_requester_task + .lock() + .await, + ) { tokio::time::sleep(std::time::Duration::from_millis(100)).await; } @@ -771,10 +836,23 @@ mod tests { assert!(latest_rav.message.value_aggregate >= trigger_value); // Check that the unaggregated fees value is reduced. - assert!(account.inner.unaggregated_fees.lock().await.value <= trigger_value); + assert!( + sender_allocation_relatioship + .inner + .unaggregated_fees + .lock() + .await + .value + <= trigger_value + ); // Reset the total value and trigger value. - total_value = account.inner.unaggregated_fees.lock().await.value; + total_value = sender_allocation_relatioship + .inner + .unaggregated_fees + .lock() + .await + .value; trigger_value = 0; // Add more receipts @@ -787,7 +865,7 @@ mod tests { .await .unwrap(); - account + sender_allocation_relatioship .handle_new_receipt_notification(NewReceiptNotification { allocation_id: *ALLOCATION_ID, sender_address: SENDER.1, @@ -804,7 +882,12 @@ mod tests { } // Wait for the RAV requester to finish. - while Account::rav_requester_task_is_running(&account.rav_requester_task.lock().await) { + while SenderAllocationRelationship::rav_requester_task_is_running( + &sender_allocation_relatioship + .rav_requester_task + .lock() + .await, + ) { tokio::time::sleep(std::time::Duration::from_millis(100)).await; } @@ -835,7 +918,15 @@ mod tests { assert!(latest_rav.message.value_aggregate >= trigger_value); // Check that the unaggregated fees value is reduced. - assert!(account.inner.unaggregated_fees.lock().await.value <= trigger_value); + assert!( + sender_allocation_relatioship + .inner + .unaggregated_fees + .lock() + .await + .value + <= trigger_value + ); // Stop the TAP aggregator server. handle.stop().unwrap(); diff --git a/tap-agent/src/tap/accounts_manager.rs b/tap-agent/src/tap/sender_allocation_relationships_manager.rs similarity index 72% rename from tap-agent/src/tap/accounts_manager.rs rename to tap-agent/src/tap/sender_allocation_relationships_manager.rs index b67a7078..ab191dc6 100644 --- a/tap-agent/src/tap/accounts_manager.rs +++ b/tap-agent/src/tap/sender_allocation_relationships_manager.rs @@ -15,8 +15,8 @@ use sqlx::{postgres::PgListener, PgPool}; use tokio::sync::RwLock; use tracing::{error, warn}; -use super::account::Account; use super::escrow_adapter::EscrowAdapter; +use super::sender_allocation_relationship::SenderAllocationRelationship; use crate::config; #[derive(Deserialize, Debug)] @@ -28,7 +28,7 @@ pub struct NewReceiptNotification { pub value: u128, } -pub struct AccountsManager { +pub struct SenderAllocationRelationshipsManager { _inner: Arc, new_receipts_watcher_handle: tokio::task::JoinHandle<()>, _eligible_allocations_senders_pipe: PipeHandle, @@ -38,8 +38,9 @@ pub struct AccountsManager { struct Inner { config: &'static config::Cli, pgpool: PgPool, - /// Map of (allocation_id, sender_address) to Account. - accounts: Arc>>, + /// Map of (allocation_id, sender_address) to SenderAllocationRelationship. + sender_allocation_relationships: + Arc>>, indexer_allocations: Eventual>, escrow_accounts: Eventual>, escrow_subgraph: &'static SubgraphClient, @@ -48,7 +49,7 @@ struct Inner { sender_aggregator_endpoints: HashMap, } -impl AccountsManager { +impl SenderAllocationRelationshipsManager { pub async fn new( config: &'static config::Cli, pgpool: PgPool, @@ -63,7 +64,7 @@ impl AccountsManager { let inner = Arc::new(Inner { config, pgpool, - accounts: Arc::new(RwLock::new(HashMap::new())), + sender_allocation_relationships: Arc::new(RwLock::new(HashMap::new())), indexer_allocations, escrow_accounts, escrow_subgraph, @@ -72,7 +73,7 @@ impl AccountsManager { sender_aggregator_endpoints, }); - Self::update_accounts( + Self::update_sender_allocation_relationships( &inner, inner .indexer_allocations @@ -86,11 +87,11 @@ impl AccountsManager { .expect("Should get escrow accounts from Eventual"), ) .await - .expect("Should be able to update accounts"); + .expect("Should be able to update sender_allocation_relationships"); - // Listen to pg_notify events. We start it before updating the unaggregated_fees for - // all accounts, so that we don't miss any receipts. PG will buffer the notifications - // until we start consuming them with `new_receipts_watcher`. + // Listen to pg_notify events. We start it before updating the unaggregated_fees for all + // SenderAllocationRelationship instances, so that we don't miss any receipts. PG will + // buffer the notifications until we start consuming them with `new_receipts_watcher`. let mut pglistener = PgListener::connect_with(&inner.pgpool.clone()) .await .unwrap(); @@ -102,11 +103,12 @@ impl AccountsManager { 'scalar_tap_receipt_notification'", ); - let mut accounts_write_lock = inner.accounts.write().await; + let mut sender_allocation_relationships_write_lock = + inner.sender_allocation_relationships.write().await; - // Create accounts for all outstanding receipts in the database, because they may - // be linked to allocations that are not eligible anymore, but still need to get - // aggregated. + // Create SenderAllocationRelationship instances for all outstanding receipts in the + // database, because they may be linked to allocations that are not eligible anymore, but + // still need to get aggregated. sqlx::query!( r#" SELECT DISTINCT allocation_id, sender_address @@ -123,11 +125,11 @@ impl AccountsManager { let sender = Address::from_str(&row.sender_address) .expect("sender_address should be a valid address"); - // Only create a account if it doesn't exist yet. + // Only create a SenderAllocationRelationship if it doesn't exist yet. if let std::collections::hash_map::Entry::Vacant(e) = - accounts_write_lock.entry((allocation_id, sender)) + sender_allocation_relationships_write_lock.entry((allocation_id, sender)) { - e.insert(Account::new( + e.insert(SenderAllocationRelationship::new( config, inner.pgpool.clone(), allocation_id, @@ -145,25 +147,25 @@ impl AccountsManager { } }); - // Update the unaggregated_fees for all accounts by pulling the receipts from the - // database. - for account in accounts_write_lock.values() { - account + // Update the unaggregated_fees for all SenderAllocationRelationship instances by pulling + // the receipts from the database. + for sender_allocation_relationship in sender_allocation_relationships_write_lock.values() { + sender_allocation_relationship .update_unaggregated_fees() .await .expect("should be able to update unaggregated_fees"); } - drop(accounts_write_lock); + drop(sender_allocation_relationships_write_lock); // Start the new_receipts_watcher task that will consume from the `pglistener` let new_receipts_watcher_handle = tokio::spawn(Self::new_receipts_watcher( pglistener, - inner.accounts.clone(), + inner.sender_allocation_relationships.clone(), )); // Start the eligible_allocations_senders_pipe that watches for changes in eligible senders - // and allocations and updates the accounts accordingly. + // and allocations and updates the SenderAllocationRelationship instances accordingly. let inner_clone = inner.clone(); let eligible_allocations_senders_pipe = eventuals::join(( inner.indexer_allocations.clone(), @@ -172,11 +174,18 @@ impl AccountsManager { .pipe_async(move |(indexer_allocations, escrow_accounts)| { let inner = inner_clone.clone(); async move { - Self::update_accounts(&inner, indexer_allocations, escrow_accounts) - .await - .unwrap_or_else(|e| { - error!("Error while updating accounts: {:?}", e); - }); + Self::update_sender_allocation_relationships( + &inner, + indexer_allocations, + escrow_accounts, + ) + .await + .unwrap_or_else(|e| { + error!( + "Error while updating sender_allocation_relationships: {:?}", + e + ); + }); } }); @@ -188,10 +197,12 @@ impl AccountsManager { } /// Continuously listens for new receipt notifications from Postgres and forwards them to the - /// corresponding account. + /// corresponding SenderAllocationRelationship. async fn new_receipts_watcher( mut pglistener: PgListener, - accounts: Arc>>, + sender_allocation_relationships: Arc< + RwLock>, + >, ) { loop { // TODO: recover from errors or shutdown the whole program? @@ -205,40 +216,45 @@ impl AccountsManager { NewReceiptNotification", ); - if let Some(account) = accounts.read().await.get(&( - new_receipt_notification.allocation_id, - new_receipt_notification.sender_address, - )) { - account + if let Some(sender_allocation_relationship) = + sender_allocation_relationships.read().await.get(&( + new_receipt_notification.allocation_id, + new_receipt_notification.sender_address, + )) + { + sender_allocation_relationship .handle_new_receipt_notification(new_receipt_notification) .await; } else { warn!( - "No account found for allocation_id {} and sender_address {} to process \ - new receipt notification. This should not happen.", + "No sender_allocation_relationship found for allocation_id {} and \ + sender_address {} to process new receipt notification. This should not \ + happen.", new_receipt_notification.allocation_id, new_receipt_notification.sender_address ); } } } - async fn update_accounts( + async fn update_sender_allocation_relationships( inner: &Inner, indexer_allocations: HashMap, escrow_accounts: HashMap, ) -> Result<()> { let eligible_allocations: Vec
= indexer_allocations.keys().copied().collect(); let senders: Vec
= escrow_accounts.keys().copied().collect(); - let mut accounts_write = inner.accounts.write().await; + let mut sender_allocation_relationships_write = + inner.sender_allocation_relationships.write().await; - // Create accounts for all currently eligible (allocation, sender) + // Create SenderAllocationRelationship instances for all currently eligible + // (allocation, sender) for allocation_id in &eligible_allocations { for sender in &senders { - // Only create an account if it doesn't exist yet. + // Only create a SenderAllocationRelationship if it doesn't exist yet. if let std::collections::hash_map::Entry::Vacant(e) = - accounts_write.entry((*allocation_id, *sender)) + sender_allocation_relationships_write.entry((*allocation_id, *sender)) { - e.insert(Account::new( + e.insert(SenderAllocationRelationship::new( inner.config, inner.pgpool.clone(), *allocation_id, @@ -259,21 +275,24 @@ impl AccountsManager { } } - // Trigger a last rav request for all accounts that correspond to ineligible - // (allocations, sender). - for ((allocation_id, sender), account) in accounts_write.iter() { + // Trigger a last rav request for all SenderAllocationRelationship instances that correspond + // to ineligible (allocations, sender). + for ((allocation_id, sender), sender_allocation_relatioship) in + sender_allocation_relationships_write.iter() + { if !eligible_allocations.contains(allocation_id) || !senders.contains(sender) { - account.start_last_rav_request().await + sender_allocation_relatioship.start_last_rav_request().await } } - // TODO: remove accounts that are finished. Ideally done in another async task? + // TODO: remove SenderAllocationRelationship instances that are finished. Ideally done in + // another async task? Ok(()) } } -impl Drop for AccountsManager { +impl Drop for SenderAllocationRelationshipsManager { fn drop(&mut self) { // Abort the notification watcher on drop. Otherwise it may panic because the PgPool could // get dropped before. (Observed in tests) @@ -295,14 +314,14 @@ mod tests { }; use crate::tap::{ - account::State, + sender_allocation_relationship::State, test_utils::{INDEXER, SENDER, TAP_EIP712_DOMAIN_SEPARATOR}, }; use super::*; #[sqlx::test(migrations = "../migrations")] - async fn test_account_creation_and_eol(pgpool: PgPool) { + async fn test_sender_allocation_relatioship_creation_and_eol(pgpool: PgPool) { let config = Box::leak(Box::new(config::Cli { config: None, ethereum: config::Ethereum { @@ -343,7 +362,7 @@ mod tests { DeploymentDetails::for_query_url(&mock_server.uri()).unwrap(), ))); - let accounts = AccountsManager::new( + let sender_allocation_relatioships = SenderAllocationRelationshipsManager::new( config, pgpool.clone(), indexer_allocations_eventual, @@ -388,13 +407,13 @@ mod tests { // Add an escrow account to the escrow_accounts Eventual. escrow_accounts_writer.write(HashMap::from([(SENDER.1, U256::from_str("1000").unwrap())])); - // Wait for the account to be created. + // Wait for the SenderAllocationRelationship to be created. tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; - // Check that the account was created. - assert!(accounts + // Check that the SenderAllocationRelationship was created. + assert!(sender_allocation_relatioships ._inner - .accounts + .sender_allocation_relationships .write() .await .contains_key(&(allocation_id, SENDER.1))); @@ -405,11 +424,11 @@ mod tests { // Wait a bit tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; - // Check that the account state is last_rav_pending + // Check that the SenderAllocationRelationship state is last_rav_pending assert_eq!( - accounts + sender_allocation_relatioships ._inner - .accounts + .sender_allocation_relationships .read() .await .get(&(allocation_id, SENDER.1)) From 8630749358a4b4bbb2b00980b1cdcf2982a5fa2d Mon Sep 17 00:00:00 2001 From: Alexis Asseman Date: Thu, 2 Nov 2023 16:11:41 -0700 Subject: [PATCH 09/18] refactor: simplify update_unaggregated_fees_static In particular, simplify handling of NULL results from the DB. Signed-off-by: Alexis Asseman --- .../src/tap/sender_allocation_relationship.rs | 34 ++++++------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/tap-agent/src/tap/sender_allocation_relationship.rs b/tap-agent/src/tap/sender_allocation_relationship.rs index ed1041df..a3a6c6d4 100644 --- a/tap-agent/src/tap/sender_allocation_relationship.rs +++ b/tap-agent/src/tap/sender_allocation_relationship.rs @@ -10,7 +10,7 @@ use ethereum_types::U256; use eventuals::Eventual; use indexer_common::prelude::SubgraphClient; use jsonrpsee::{core::client::ClientT, http_client::HttpClientBuilder, rpc_params}; -use sqlx::PgPool; +use sqlx::{types::BigDecimal, PgPool}; use tap_aggregator::jsonrpsee_helpers::JsonRpcResponse; use tap_core::{ eip_712_signed_message::EIP712SignedMessage, @@ -219,7 +219,6 @@ impl SenderAllocationRelationship { AND sender_address = $2 ) SELECT - COUNT(*), MAX(id), SUM(value) FROM @@ -251,28 +250,17 @@ impl SenderAllocationRelationship { let mut unaggregated_fees = inner.unaggregated_fees.lock().await; - // `COUNT(*)` will always return a value, so we don't need to check for `None`. - match res.count.unwrap() { - 0 => { - unaggregated_fees.last_id = 0; - unaggregated_fees.value = 0; - } - // If the count is non-zero, then `MAX(id)` and `SUM(value)` will be non-null. - // If they are null, then something is extremely wrong with the database. - _ => { - ensure!( - res.max.is_some(), - "MAX(id) is null but the receipt COUNT(*) is not zero" - ); - ensure!( - res.sum.is_some(), - "SUM(value) is null, but the receipt COUNT(*) is not zero" - ); + ensure!( + res.sum.is_none() == res.max.is_none(), + "Exactly one of SUM(value) and MAX(id) is null. This should not happen." + ); - unaggregated_fees.last_id = res.max.unwrap().try_into()?; - unaggregated_fees.value = res.sum.unwrap().to_string().parse::()?; - } - } + unaggregated_fees.last_id = res.max.unwrap_or(0).try_into()?; + unaggregated_fees.value = res + .sum + .unwrap_or(BigDecimal::from(0)) + .to_string() + .parse::()?; // TODO: check if we need to run a RAV request here. From 906c79bf1a752a66951de71cd560708460b8db75 Mon Sep 17 00:00:00 2001 From: Alexis Asseman Date: Thu, 9 Nov 2023 18:18:04 -0800 Subject: [PATCH 10/18] refactor: cleanup config Signed-off-by: Alexis Asseman --- tap-agent/src/config.rs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/tap-agent/src/config.rs b/tap-agent/src/config.rs index 56975691..67495553 100644 --- a/tap-agent/src/config.rs +++ b/tap-agent/src/config.rs @@ -45,13 +45,6 @@ pub struct Cli { #[derive(Clone, Debug, Args, Serialize, Deserialize, Default)] #[group(required = true, multiple = true)] pub struct Ethereum { - #[clap( - long, - value_name = "mnemonic", - env = "MNEMONIC", - help = "Mnemonic for the operator wallet" - )] - pub mnemonic: String, #[clap( long, value_name = "indexer-address", @@ -81,7 +74,7 @@ pub struct Receipts { } #[derive(Clone, Debug, Args, Serialize, Deserialize, Default)] -#[group(required = true, multiple = true)] +#[group(multiple = true)] pub struct IndexerInfrastructure { #[clap( long, @@ -162,7 +155,7 @@ pub struct Postgres { } #[derive(Clone, Debug, Args, Serialize, Deserialize, Default)] -#[group(required = true, multiple = true)] +#[group(multiple = true)] pub struct NetworkSubgraph { #[clap( long, From 692bf46eb6e1048d092b2a0436f9afd7e41aea29 Mon Sep 17 00:00:00 2001 From: Alexis Asseman Date: Fri, 10 Nov 2023 11:40:59 -0800 Subject: [PATCH 11/18] docs: importance of receipt's SERIAL id in DB Signed-off-by: Alexis Asseman --- migrations/20230912220523_tap_receipts.up.sql | 2 +- tap-agent/src/tap/sender_allocation_relationship.rs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/migrations/20230912220523_tap_receipts.up.sql b/migrations/20230912220523_tap_receipts.up.sql index c7cd9c22..14d0f6ed 100644 --- a/migrations/20230912220523_tap_receipts.up.sql +++ b/migrations/20230912220523_tap_receipts.up.sql @@ -1,5 +1,5 @@ CREATE TABLE IF NOT EXISTS scalar_tap_receipts ( - id BIGSERIAL PRIMARY KEY, + id BIGSERIAL PRIMARY KEY, -- id being SERIAL is important for the function of tap-agent allocation_id CHAR(40) NOT NULL, sender_address CHAR(40) NOT NULL, timestamp_ns NUMERIC(20) NOT NULL, diff --git a/tap-agent/src/tap/sender_allocation_relationship.rs b/tap-agent/src/tap/sender_allocation_relationship.rs index a3a6c6d4..4ac603a8 100644 --- a/tap-agent/src/tap/sender_allocation_relationship.rs +++ b/tap-agent/src/tap/sender_allocation_relationship.rs @@ -43,6 +43,9 @@ type TapManager = tap_core::tap_manager::Manager< #[derive(Default, Debug)] struct UnaggregatedFees { pub value: u128, + /// The ID of the last receipt value added to the unaggregated fees value. + /// This is used to make sure we don't process the same receipt twice. Relies on the fact that + /// the receipts IDs are SERIAL in the database. pub last_id: u64, } From 01336a19c05b523dcb6e636760eeac19d36c779f Mon Sep 17 00:00:00 2001 From: Alexis Asseman Date: Fri, 10 Nov 2023 12:51:59 -0800 Subject: [PATCH 12/18] feat: add timeout config for RAV request Signed-off-by: Alexis Asseman --- tap-agent/src/config.rs | 8 ++++++++ tap-agent/src/tap/sender_allocation_relationship.rs | 10 +++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/tap-agent/src/config.rs b/tap-agent/src/config.rs index 67495553..5afe90a7 100644 --- a/tap-agent/src/config.rs +++ b/tap-agent/src/config.rs @@ -231,6 +231,14 @@ pub struct Tap { default_value_t = 1_000_000_000 // 1 second )] pub rav_request_timestamp_buffer_ns: u64, + #[clap( + long, + value_name = "rav-request-timeout", + env = "RAV_REQUEST_TIMEOUT", + help = "Timeout (in seconds) for RAV requests.", + default_value_t = 5 + )] + pub rav_request_timeout_secs: u64, // TODO: Remove this whenever the the gateway registry is ready #[clap( diff --git a/tap-agent/src/tap/sender_allocation_relationship.rs b/tap-agent/src/tap/sender_allocation_relationship.rs index 4ac603a8..ffba02e0 100644 --- a/tap-agent/src/tap/sender_allocation_relationship.rs +++ b/tap-agent/src/tap/sender_allocation_relationship.rs @@ -1,7 +1,7 @@ // Copyright 2023-, GraphOps and Semiotic Labs. // SPDX-License-Identifier: Apache-2.0 -use std::{collections::HashMap, sync::Arc}; +use std::{collections::HashMap, sync::Arc, time::Duration}; use alloy_primitives::Address; use alloy_sol_types::Eip712Domain; @@ -295,9 +295,12 @@ impl SenderAllocationRelationship { .await?; // TODO: Request compression and response decompression. Also a fancy user agent? - let client = HttpClientBuilder::default().build(&inner.sender_aggregator_endpoint)?; + let client = HttpClientBuilder::default() + .request_timeout(Duration::from_secs( + inner.config.tap.rav_request_timeout_secs, + )) + .build(&inner.sender_aggregator_endpoint)?; - // TODO: Add a timeout. let response: JsonRpcResponse> = client .request( "aggregate_receipts", @@ -440,6 +443,7 @@ mod tests { tap: config::Tap { rav_request_trigger_value: 100, rav_request_timestamp_buffer_ns: 1000, + rav_request_timeout_secs: 5, ..Default::default() }, ..Default::default() From 499508297722a11375f25e4668d618671efc7018 Mon Sep 17 00:00:00 2001 From: Alexis Asseman Date: Fri, 10 Nov 2023 12:59:05 -0800 Subject: [PATCH 13/18] fix: state-based receipt notification rejection Signed-off-by: Alexis Asseman --- tap-agent/src/tap/sender_allocation_relationship.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tap-agent/src/tap/sender_allocation_relationship.rs b/tap-agent/src/tap/sender_allocation_relationship.rs index ffba02e0..127573e6 100644 --- a/tap-agent/src/tap/sender_allocation_relationship.rs +++ b/tap-agent/src/tap/sender_allocation_relationship.rs @@ -147,12 +147,12 @@ impl SenderAllocationRelationship { &self, new_receipt_notification: NewReceiptNotification, ) { - // If we're in the last rav pending state, we don't want to process any new receipts. - if self.state().await == State::LastRavPending { + // If we're in the last rav pending state or finished, we don't want to process any new + // receipts. + if self.state().await != State::Running { error!( - "Received a new receipt notification for allocation {} and sender {} while \ - the last RAV is pending. This should not have happened since this allocation \ - and/or sender is not eligible anymore.", + "Received a new receipt notification for now ineligible allocation {} and \ + sender {}.", self.inner.allocation_id, self.inner.sender ); return; From 446bde215532937d492fc063b1264afde4bda400 Mon Sep 17 00:00:00 2001 From: Alexis Asseman Date: Fri, 10 Nov 2023 13:07:03 -0800 Subject: [PATCH 14/18] refactor: cleanup Signed-off-by: Alexis Asseman --- tap-agent/src/tap/receipt_checks_adapter.rs | 2 +- tap-agent/src/tap/receipt_storage_adapter.rs | 4 ++-- tap-agent/src/tap/sender_allocation_relationship.rs | 1 - tap-agent/src/tap/sender_allocation_relationships_manager.rs | 1 - 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/tap-agent/src/tap/receipt_checks_adapter.rs b/tap-agent/src/tap/receipt_checks_adapter.rs index 1bd7026d..c4257ea1 100644 --- a/tap-agent/src/tap/receipt_checks_adapter.rs +++ b/tap-agent/src/tap/receipt_checks_adapter.rs @@ -72,7 +72,7 @@ impl ReceiptChecksAdapterTrait for ReceiptChecksAdapter { _receipt: &EIP712SignedMessage, _receipt_id: u64, ) -> Result { - panic!("Not implemented."); + unimplemented!(); } async fn is_valid_allocation_id( diff --git a/tap-agent/src/tap/receipt_storage_adapter.rs b/tap-agent/src/tap/receipt_storage_adapter.rs index 615aa54d..3586592c 100644 --- a/tap-agent/src/tap/receipt_storage_adapter.rs +++ b/tap-agent/src/tap/receipt_storage_adapter.rs @@ -91,7 +91,7 @@ impl ReceiptStorageAdapterTrait for ReceiptStorageAdapter { /// We don't need this method in TAP Agent. async fn store_receipt(&self, _receipt: ReceivedReceipt) -> Result { - panic!("Not implemented"); + unimplemented!(); } async fn retrieve_receipts_in_timestamp_range + Send>( @@ -131,7 +131,7 @@ impl ReceiptStorageAdapterTrait for ReceiptStorageAdapter { _receipt_id: u64, _receipt: ReceivedReceipt, ) -> Result<(), Self::AdapterError> { - panic!("Not implemented"); + unimplemented!(); } async fn remove_receipts_in_timestamp_range + Send>( diff --git a/tap-agent/src/tap/sender_allocation_relationship.rs b/tap-agent/src/tap/sender_allocation_relationship.rs index 127573e6..19d466ff 100644 --- a/tap-agent/src/tap/sender_allocation_relationship.rs +++ b/tap-agent/src/tap/sender_allocation_relationship.rs @@ -438,7 +438,6 @@ mod tests { config: None, ethereum: config::Ethereum { indexer_address: INDEXER.1, - ..Default::default() }, tap: config::Tap { rav_request_trigger_value: 100, diff --git a/tap-agent/src/tap/sender_allocation_relationships_manager.rs b/tap-agent/src/tap/sender_allocation_relationships_manager.rs index ab191dc6..7d401b59 100644 --- a/tap-agent/src/tap/sender_allocation_relationships_manager.rs +++ b/tap-agent/src/tap/sender_allocation_relationships_manager.rs @@ -326,7 +326,6 @@ mod tests { config: None, ethereum: config::Ethereum { indexer_address: INDEXER.1, - ..Default::default() }, tap: config::Tap { rav_request_trigger_value: 100, From 8e6460f1b86ba67c1846fde6d5ae3345dbcc047c Mon Sep 17 00:00:00 2001 From: Alexis Asseman Date: Mon, 13 Nov 2023 11:15:21 -0800 Subject: [PATCH 15/18] fix: duration conversions Signed-off-by: Alexis Asseman --- tap-agent/src/agent.rs | 4 ++-- tap-agent/src/config.rs | 10 +++++----- tap-agent/src/tap/receipt_checks_adapter.rs | 8 ++++---- tap-agent/src/tap/sender_allocation_relationship.rs | 4 ++-- .../src/tap/sender_allocation_relationships_manager.rs | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tap-agent/src/agent.rs b/tap-agent/src/agent.rs index 5059f211..4b54c932 100644 --- a/tap-agent/src/agent.rs +++ b/tap-agent/src/agent.rs @@ -40,7 +40,7 @@ pub async fn start_agent(config: &'static config::Cli) { network_subgraph, config.ethereum.indexer_address, 1, - Duration::from_secs(config.network_subgraph.allocation_syncing_interval), + Duration::from_millis(config.network_subgraph.allocation_syncing_interval_ms), ); let escrow_subgraph = Box::leak(Box::new(SubgraphClient::new( @@ -64,7 +64,7 @@ pub async fn start_agent(config: &'static config::Cli) { let escrow_accounts = escrow_accounts( escrow_subgraph, config.ethereum.indexer_address, - Duration::from_secs(config.escrow_subgraph.escrow_syncing_interval), + Duration::from_millis(config.escrow_subgraph.escrow_syncing_interval_ms), ); // TODO: replace with a proper implementation once the gateway registry contract is ready diff --git a/tap-agent/src/config.rs b/tap-agent/src/config.rs index 5afe90a7..e48bb47d 100644 --- a/tap-agent/src/config.rs +++ b/tap-agent/src/config.rs @@ -179,7 +179,7 @@ pub struct NetworkSubgraph { default_value_t = 120_000, help = "Interval (in ms) for syncing indexer allocations from the network" )] - pub allocation_syncing_interval: u64, + pub allocation_syncing_interval_ms: u64, } #[derive(Clone, Debug, Args, Serialize, Deserialize, Default)] @@ -208,7 +208,7 @@ pub struct EscrowSubgraph { default_value_t = 120_000, help = "Interval (in ms) for syncing indexer escrow accounts from the escrow subgraph" )] - pub escrow_syncing_interval: u64, + pub escrow_syncing_interval_ms: u64, } #[derive(Clone, Debug, Args, Serialize, Deserialize, Default)] @@ -226,11 +226,11 @@ pub struct Tap { long, value_name = "rav-request-timestamp-buffer", env = "RAV_REQUEST_TIMESTAMP_BUFFER", - help = "Buffer (in ns) to add between the current time and the timestamp of the \ + help = "Buffer (in ms) to add between the current time and the timestamp of the \ last unaggregated fee when triggering a RAV request.", - default_value_t = 1_000_000_000 // 1 second + default_value_t = 1_000 // 1 second )] - pub rav_request_timestamp_buffer_ns: u64, + pub rav_request_timestamp_buffer_ms: u64, #[clap( long, value_name = "rav-request-timeout", diff --git a/tap-agent/src/tap/receipt_checks_adapter.rs b/tap-agent/src/tap/receipt_checks_adapter.rs index c4257ea1..0c0b0966 100644 --- a/tap-agent/src/tap/receipt_checks_adapter.rs +++ b/tap-agent/src/tap/receipt_checks_adapter.rs @@ -40,7 +40,7 @@ impl ReceiptChecksAdapter { sender_id, config.ethereum.indexer_address, escrow_subgraph, - config.escrow_subgraph.escrow_syncing_interval, + config.escrow_subgraph.escrow_syncing_interval_ms, ); Self { query_appraisals, @@ -137,7 +137,7 @@ impl ReceiptChecksAdapter { sender_address: Address, indexer_address: Address, escrow_subgraph: &'static SubgraphClient, - escrow_subgraph_polling_interval: u64, + escrow_subgraph_polling_interval_ms: u64, ) -> Eventual { #[derive(serde::Deserialize)] struct AllocationResponse { @@ -150,7 +150,7 @@ impl ReceiptChecksAdapter { transactions: Vec, } - timer(Duration::from_secs(escrow_subgraph_polling_interval)).map_with_retry( + timer(Duration::from_millis(escrow_subgraph_polling_interval_ms)).map_with_retry( move |_| async move { let response = escrow_subgraph .query::(&json!({ @@ -198,7 +198,7 @@ impl ReceiptChecksAdapter { "Failed to check the escrow redeem status for allocation {} and sender {}: {}", allocation_id, sender_address, error ); - sleep(Duration::from_secs(escrow_subgraph_polling_interval).div_f32(2.)) + sleep(Duration::from_millis(escrow_subgraph_polling_interval_ms).div_f32(2.)) }, ) } diff --git a/tap-agent/src/tap/sender_allocation_relationship.rs b/tap-agent/src/tap/sender_allocation_relationship.rs index 19d466ff..10551c7e 100644 --- a/tap-agent/src/tap/sender_allocation_relationship.rs +++ b/tap-agent/src/tap/sender_allocation_relationship.rs @@ -291,7 +291,7 @@ impl SenderAllocationRelationship { expected_rav, } = inner .tap_manager - .create_rav_request(inner.config.tap.rav_request_timestamp_buffer_ns) + .create_rav_request(inner.config.tap.rav_request_timestamp_buffer_ms * 1_000_000) .await?; // TODO: Request compression and response decompression. Also a fancy user agent? @@ -441,7 +441,7 @@ mod tests { }, tap: config::Tap { rav_request_trigger_value: 100, - rav_request_timestamp_buffer_ns: 1000, + rav_request_timestamp_buffer_ms: 1, rav_request_timeout_secs: 5, ..Default::default() }, diff --git a/tap-agent/src/tap/sender_allocation_relationships_manager.rs b/tap-agent/src/tap/sender_allocation_relationships_manager.rs index 7d401b59..f667dc56 100644 --- a/tap-agent/src/tap/sender_allocation_relationships_manager.rs +++ b/tap-agent/src/tap/sender_allocation_relationships_manager.rs @@ -329,7 +329,7 @@ mod tests { }, tap: config::Tap { rav_request_trigger_value: 100, - rav_request_timestamp_buffer_ns: 1000, + rav_request_timestamp_buffer_ms: 1, ..Default::default() }, ..Default::default() From 05cac7c316007bb6406169771fe44ab5b4b5e388 Mon Sep 17 00:00:00 2001 From: Alexis Asseman Date: Mon, 13 Nov 2023 13:01:24 -0800 Subject: [PATCH 16/18] feat: rav trigger config in GRT instead of wei Signed-off-by: Alexis Asseman --- Cargo.lock | 21 ++++++- tap-agent/Cargo.toml | 1 + tap-agent/src/config.rs | 59 ++++++++++++++++++- ...sender_allocation_relationships_manager.rs | 1 + 4 files changed, 76 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 02efb8d5..b3d21c41 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -821,6 +821,20 @@ dependencies = [ "num-traits", ] +[[package]] +name = "bigdecimal" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06619be423ea5bb86c95f087d5707942791a08a85530df0db2209a3ecfb8bc9" +dependencies = [ + "autocfg", + "libm", + "num-bigint", + "num-integer", + "num-traits", + "serde", +] + [[package]] name = "bit-set" version = "0.5.3" @@ -2960,6 +2974,7 @@ dependencies = [ "alloy-sol-types", "anyhow", "async-trait", + "bigdecimal 0.4.2", "clap", "confy", "dotenvy", @@ -5488,7 +5503,7 @@ checksum = "8d6753e460c998bbd4cd8c6f0ed9a64346fcca0723d6e75e52fdc351c5d2169d" dependencies = [ "ahash 0.8.3", "atoi", - "bigdecimal", + "bigdecimal 0.3.1", "byteorder", "bytes", "crc", @@ -5570,7 +5585,7 @@ checksum = "864b869fdf56263f4c95c45483191ea0af340f9f3e3e7b4d57a61c7c87a970db" dependencies = [ "atoi", "base64 0.21.4", - "bigdecimal", + "bigdecimal 0.3.1", "bitflags 2.4.1", "byteorder", "bytes", @@ -5615,7 +5630,7 @@ checksum = "eb7ae0e6a97fb3ba33b23ac2671a5ce6e3cabe003f451abd5a56e7951d975624" dependencies = [ "atoi", "base64 0.21.4", - "bigdecimal", + "bigdecimal 0.3.1", "bitflags 2.4.1", "byteorder", "crc", diff --git a/tap-agent/Cargo.toml b/tap-agent/Cargo.toml index 64aef9af..2eb3d4a1 100644 --- a/tap-agent/Cargo.toml +++ b/tap-agent/Cargo.toml @@ -13,6 +13,7 @@ alloy-primitives = "0.4.2" alloy-sol-types = "0.4.2" anyhow = "1.0.72" async-trait = "0.1.72" +bigdecimal = { version = "0.4.2", features = ["serde"] } clap = { version = "4.4.3", features = ["derive", "env"] } confy = "0.5.1" dotenvy = "0.15.7" diff --git a/tap-agent/src/config.rs b/tap-agent/src/config.rs index e48bb47d..b292ce0c 100644 --- a/tap-agent/src/config.rs +++ b/tap-agent/src/config.rs @@ -1,9 +1,10 @@ // Copyright 2023-, GraphOps and Semiotic Labs. // SPDX-License-Identifier: Apache-2.0 -use std::path::PathBuf; +use std::{path::PathBuf, str::FromStr}; use alloy_primitives::Address; +use bigdecimal::{BigDecimal, ToPrimitive}; use clap::{command, Args, Parser, ValueEnum}; use dotenvy::dotenv; use serde::{Deserialize, Serialize}; @@ -218,8 +219,9 @@ pub struct Tap { long, value_name = "rav-request-trigger-value", env = "RAV_REQUEST_TRIGGER_VALUE", - help = "Value of unaggregated fees that triggers a RAV request (in GRT wei).", - default_value_t = 10_000_000_000_000_000_000 // 10 GRT + help = "Value of unaggregated fees that triggers a RAV request (in GRT).", + default_value = "10", + value_parser(parse_grt_value_to_nonzero_u64) )] pub rav_request_trigger_value: u64, #[clap( @@ -266,6 +268,26 @@ fn init_tracing(format: String) -> Result<(), SetGlobalDefaultError> { } } +fn parse_grt_value_to_nonzero_u64(s: &str) -> Result { + let v = BigDecimal::from_str(s) + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?; + if v <= 0.into() { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "GRT value must be greater than 0".to_string(), + )); + } + // Convert to wei + let v = v * BigDecimal::from(10u64.pow(18)); + // Convert to u64 + v.to_u64().ok_or_else(|| { + std::io::Error::new( + std::io::ErrorKind::Other, + "GRT value cannot be represented as a u64 GRT wei value".to_string(), + ) + }) +} + impl Cli { /// Parse config arguments If environmental variable for config is set to a valid /// config file path, then parse from config Otherwise parse from command line @@ -319,3 +341,34 @@ pub enum LogLevel { Error, Fatal, } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_parse_grt_value_to_u64() { + assert_eq!( + parse_grt_value_to_nonzero_u64("1").unwrap(), + 1_000_000_000_000_000_000 + ); + assert_eq!( + parse_grt_value_to_nonzero_u64("1.1").unwrap(), + 1_100_000_000_000_000_000 + ); + assert_eq!( + parse_grt_value_to_nonzero_u64("1.000000000000000001").unwrap(), + 1_000_000_000_000_000_001 + ); + assert_eq!( + parse_grt_value_to_nonzero_u64("0.000000000000000001").unwrap(), + 1 + ); + assert!(parse_grt_value_to_nonzero_u64("0").is_err()); + assert!(parse_grt_value_to_nonzero_u64("-1").is_err()); + assert_eq!( + parse_grt_value_to_nonzero_u64("1.0000000000000000001").unwrap(), + 1_000_000_000_000_000_000 + ); + } +} diff --git a/tap-agent/src/tap/sender_allocation_relationships_manager.rs b/tap-agent/src/tap/sender_allocation_relationships_manager.rs index f667dc56..5b71cf4e 100644 --- a/tap-agent/src/tap/sender_allocation_relationships_manager.rs +++ b/tap-agent/src/tap/sender_allocation_relationships_manager.rs @@ -302,6 +302,7 @@ impl Drop for SenderAllocationRelationshipsManager { #[cfg(test)] mod tests { + use indexer_common::{ prelude::{AllocationStatus, SubgraphDeployment}, subgraph_client::DeploymentDetails, From 4f0ea80e4e9d8cdb3b4dfaec00b4b8fb719148ff Mon Sep 17 00:00:00 2001 From: Alexis Asseman Date: Mon, 13 Nov 2023 14:28:45 -0800 Subject: [PATCH 17/18] refactor: rename "gateway" to "sender" Signed-off-by: Alexis Asseman --- tap-agent/src/tap/escrow_adapter.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tap-agent/src/tap/escrow_adapter.rs b/tap-agent/src/tap/escrow_adapter.rs index 5a0a62ab..b5957145 100644 --- a/tap-agent/src/tap/escrow_adapter.rs +++ b/tap-agent/src/tap/escrow_adapter.rs @@ -55,7 +55,7 @@ impl EscrowAdapterTrait for EscrowAdapter { .get(&sender) .ok_or(AdapterError::AdapterError { error: format!( - "Gateway {} not found in escrow balances map, could not get available escrow.", + "Sender {} not found in escrow balances map, could not get available escrow.", sender ) .to_string(), @@ -63,7 +63,7 @@ impl EscrowAdapterTrait for EscrowAdapter { .to_owned(); let balance: u128 = balance.try_into().map_err(|_| AdapterError::AdapterError { error: format!( - "Gateway {} escrow balance is too large to fit in u128, \ + "Sender {} escrow balance is too large to fit in u128, \ could not get available escrow.", sender ) @@ -87,7 +87,7 @@ impl EscrowAdapterTrait for EscrowAdapter { if current_available_escrow < value { return Err(AdapterError::AdapterError { error: format!( - "Gateway {} does not have enough escrow to subtract {} from {}.", + "Sender {} does not have enough escrow to subtract {} from {}.", sender, value, *fees ) .to_string(), From 1178c6386768c98b7efb8463c024c57286ef7d1b Mon Sep 17 00:00:00 2001 From: Alexis Asseman Date: Mon, 13 Nov 2023 17:17:50 -0800 Subject: [PATCH 18/18] refactor: rename `is_last` col to `final` in `scalar_tap_ravs` table Signed-off-by: Alexis Asseman --- ...e3ffe3109b63b14aa746eacdb81288f919c16.json | 35 ------------------- ...9c73af715e64ea63132d4768731b222ad672b.json | 29 +++++++++++++++ ...0c2c494f3ba0774097652df984248869c20d.json} | 6 ++-- migrations/20230915230734_tap_ravs.up.sql | 2 +- .../src/tap/sender_allocation_relationship.rs | 2 +- 5 files changed, 34 insertions(+), 40 deletions(-) delete mode 100644 .sqlx/query-46a50e4bbfb5f7c57bb42fcfa17e3ffe3109b63b14aa746eacdb81288f919c16.json create mode 100644 .sqlx/query-6d2f5eecfd846d8f1e2db87e1a79c73af715e64ea63132d4768731b222ad672b.json rename .sqlx/{query-d47a86f9686f9c72d2d5047d4c315adf6a3900d89553bdec541201fd3e282d83.json => query-7a48651e528b87c6a618534806e70c2c494f3ba0774097652df984248869c20d.json} (71%) diff --git a/.sqlx/query-46a50e4bbfb5f7c57bb42fcfa17e3ffe3109b63b14aa746eacdb81288f919c16.json b/.sqlx/query-46a50e4bbfb5f7c57bb42fcfa17e3ffe3109b63b14aa746eacdb81288f919c16.json deleted file mode 100644 index a166ca8d..00000000 --- a/.sqlx/query-46a50e4bbfb5f7c57bb42fcfa17e3ffe3109b63b14aa746eacdb81288f919c16.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n WITH rav AS (\n SELECT \n rav -> 'message' ->> 'timestamp_ns' AS timestamp_ns \n FROM \n scalar_tap_ravs \n WHERE \n allocation_id = $1 \n AND sender_address = $2\n ) \n SELECT \n COUNT(*), \n MAX(id), \n SUM(value) \n FROM \n scalar_tap_receipts \n WHERE \n allocation_id = $1 \n AND sender_address = $2 \n AND CASE WHEN (\n SELECT \n timestamp_ns :: NUMERIC \n FROM \n rav\n ) IS NOT NULL THEN timestamp_ns > (\n SELECT \n timestamp_ns :: NUMERIC \n FROM \n rav\n ) ELSE TRUE END\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "count", - "type_info": "Int8" - }, - { - "ordinal": 1, - "name": "max", - "type_info": "Int8" - }, - { - "ordinal": 2, - "name": "sum", - "type_info": "Numeric" - } - ], - "parameters": { - "Left": [ - "Bpchar", - "Bpchar" - ] - }, - "nullable": [ - null, - null, - null - ] - }, - "hash": "46a50e4bbfb5f7c57bb42fcfa17e3ffe3109b63b14aa746eacdb81288f919c16" -} diff --git a/.sqlx/query-6d2f5eecfd846d8f1e2db87e1a79c73af715e64ea63132d4768731b222ad672b.json b/.sqlx/query-6d2f5eecfd846d8f1e2db87e1a79c73af715e64ea63132d4768731b222ad672b.json new file mode 100644 index 00000000..e878a43f --- /dev/null +++ b/.sqlx/query-6d2f5eecfd846d8f1e2db87e1a79c73af715e64ea63132d4768731b222ad672b.json @@ -0,0 +1,29 @@ +{ + "db_name": "PostgreSQL", + "query": "\n WITH rav AS (\n SELECT \n rav -> 'message' ->> 'timestamp_ns' AS timestamp_ns \n FROM \n scalar_tap_ravs \n WHERE \n allocation_id = $1 \n AND sender_address = $2\n ) \n SELECT \n MAX(id), \n SUM(value) \n FROM \n scalar_tap_receipts \n WHERE \n allocation_id = $1 \n AND sender_address = $2 \n AND CASE WHEN (\n SELECT \n timestamp_ns :: NUMERIC \n FROM \n rav\n ) IS NOT NULL THEN timestamp_ns > (\n SELECT \n timestamp_ns :: NUMERIC \n FROM \n rav\n ) ELSE TRUE END\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "max", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "sum", + "type_info": "Numeric" + } + ], + "parameters": { + "Left": [ + "Bpchar", + "Bpchar" + ] + }, + "nullable": [ + null, + null + ] + }, + "hash": "6d2f5eecfd846d8f1e2db87e1a79c73af715e64ea63132d4768731b222ad672b" +} diff --git a/.sqlx/query-d47a86f9686f9c72d2d5047d4c315adf6a3900d89553bdec541201fd3e282d83.json b/.sqlx/query-7a48651e528b87c6a618534806e70c2c494f3ba0774097652df984248869c20d.json similarity index 71% rename from .sqlx/query-d47a86f9686f9c72d2d5047d4c315adf6a3900d89553bdec541201fd3e282d83.json rename to .sqlx/query-7a48651e528b87c6a618534806e70c2c494f3ba0774097652df984248869c20d.json index a63ac2ab..3bfe8040 100644 --- a/.sqlx/query-d47a86f9686f9c72d2d5047d4c315adf6a3900d89553bdec541201fd3e282d83.json +++ b/.sqlx/query-7a48651e528b87c6a618534806e70c2c494f3ba0774097652df984248869c20d.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n UPDATE scalar_tap_ravs\n SET is_last = true\n WHERE allocation_id = $1 AND sender_address = $2\n RETURNING *\n ", + "query": "\n UPDATE scalar_tap_ravs\n SET final = true\n WHERE allocation_id = $1 AND sender_address = $2\n RETURNING *\n ", "describe": { "columns": [ { @@ -20,7 +20,7 @@ }, { "ordinal": 3, - "name": "is_last", + "name": "final", "type_info": "Bool" } ], @@ -37,5 +37,5 @@ false ] }, - "hash": "d47a86f9686f9c72d2d5047d4c315adf6a3900d89553bdec541201fd3e282d83" + "hash": "7a48651e528b87c6a618534806e70c2c494f3ba0774097652df984248869c20d" } diff --git a/migrations/20230915230734_tap_ravs.up.sql b/migrations/20230915230734_tap_ravs.up.sql index 9847d283..61ee9e86 100644 --- a/migrations/20230915230734_tap_ravs.up.sql +++ b/migrations/20230915230734_tap_ravs.up.sql @@ -2,6 +2,6 @@ CREATE TABLE IF NOT EXISTS scalar_tap_ravs ( allocation_id CHAR(40) NOT NULL, sender_address CHAR(40) NOT NULL, rav JSON NOT NULL, - is_last BOOLEAN DEFAULT FALSE NOT NULL, + final BOOLEAN DEFAULT FALSE NOT NULL, PRIMARY KEY (allocation_id, sender_address) ); diff --git a/tap-agent/src/tap/sender_allocation_relationship.rs b/tap-agent/src/tap/sender_allocation_relationship.rs index 10551c7e..ad940aa5 100644 --- a/tap-agent/src/tap/sender_allocation_relationship.rs +++ b/tap-agent/src/tap/sender_allocation_relationship.rs @@ -351,7 +351,7 @@ impl SenderAllocationRelationship { let updated_rows = sqlx::query!( r#" UPDATE scalar_tap_ravs - SET is_last = true + SET final = true WHERE allocation_id = $1 AND sender_address = $2 RETURNING * "#,