diff --git a/Cargo.lock b/Cargo.lock index 7692e3cd..41ad5e96 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,9 +25,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy" -version = "1.0.25" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "992a9d0732a0e0e1a34d61a6553ad28f761c9049bb46572d3916f172348d2cb7" +checksum = "2e2a5d689ccd182f1d138a61f081841b905034e0089f5278f6c200f2bcdab00a" dependencies = [ "alloy-consensus", "alloy-contract", @@ -51,9 +51,9 @@ dependencies = [ [[package]] name = "alloy-chains" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2672194d5865f00b03e5c7844d2c6f172a842e5c3bc49d7f9b871c42c95ae65" +checksum = "ef8ff73a143281cb77c32006b04af9c047a6b8fe5860e85a88ad325328965355" dependencies = [ "alloy-primitives", "num_enum", @@ -62,9 +62,9 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "1.0.25" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35f021a55afd68ff2364ccfddaa364fc9a38a72200cdc74fcfb8dc3231d38f2c" +checksum = "d213580c17d239ae83c0d897ac3315db7cda83d2d4936a9823cc3517552f2e24" dependencies = [ "alloy-eips", "alloy-primitives", @@ -87,9 +87,9 @@ dependencies = [ [[package]] name = "alloy-consensus-any" -version = "1.0.25" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a0ecca7a71b1f88e63d19e2d9397ce56949d3dd3484fd73c73d0077dc5c93d4" +checksum = "81443e3b8dccfeac7cd511aced15928c97ff253f4177acbb97de97178e543f6c" dependencies = [ "alloy-consensus", "alloy-eips", @@ -101,9 +101,9 @@ dependencies = [ [[package]] name = "alloy-contract" -version = "1.0.25" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd26132cbfa6e5f191a01f7b9725eaa0680a953be1fd005d588b0e9422c16456" +checksum = "de217ab604f1bcfa2e3b0aff86d50812d5931d47522f9f0a949cc263ec2d108e" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -189,9 +189,9 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "1.0.25" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7473a19f02b25f8e1e8c69d35f02c07245694d11bd91bfe00e9190ac106b3838" +checksum = "2a15b4b0f6bab47aae017d52bb5a739bda381553c09fb9918b7172721ef5f5de" dependencies = [ "alloy-eip2124", "alloy-eip2930", @@ -204,14 +204,16 @@ dependencies = [ "derive_more", "either", "serde", + "serde_with", "sha2", + "thiserror 2.0.17", ] [[package]] name = "alloy-genesis" -version = "1.0.25" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b2c29f25098bfa4cd3d9ec7806e1506716931e188c7c0843284123831c2cf1" +checksum = "33ba1cbc25a07e0142e8875fcbe80e1fdb02be8160ae186b90f4b9a69a72ed2b" dependencies = [ "alloy-eips", "alloy-primitives", @@ -235,9 +237,9 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "1.0.25" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a4d1f49fdf9780b60e52c20ffcc1e352d8d27885cc8890620eb584978265dd9" +checksum = "f8882ec8e4542cfd02aadc6dccbe90caa73038f60016d936734eb6ced53d2167" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -250,9 +252,9 @@ dependencies = [ [[package]] name = "alloy-network" -version = "1.0.25" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2991c432e149babfd996194f8f558f85d7326ac4cf52c55732d32078ff0282d4" +checksum = "51d6d87d588bda509881a7a66ae77c86514bd1193ac30fbff0e0f24db95eb5a5" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -276,9 +278,9 @@ dependencies = [ [[package]] name = "alloy-network-primitives" -version = "1.0.25" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d540d962ddbc3e95153bafe56ccefeb16dfbffa52c5f7bdd66cd29ec8f52259" +checksum = "5b14fa9ba5774e0b30ae6a04176d998211d516c8af69c9c530af7c6c42a8c508" dependencies = [ "alloy-consensus", "alloy-eips", @@ -316,9 +318,9 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "1.0.25" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e96d8084a1cf96be2df6219ac407275ac20c1136fa01f911535eb489aa006e8" +checksum = "475a5141313c3665b75d818be97d5fa3eb5e0abb7e832e9767edd94746db28e3" dependencies = [ "alloy-chains", "alloy-consensus", @@ -377,9 +379,9 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "1.0.25" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194ff51cd1d2e65c66b98425e0ca7eb559ca1a579725834c986d84faf8e224c0" +checksum = "25289674cd8c58fcca2568b5350423cb0dd7bca8c596c5e2869bfe4c5c57ed14" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -400,9 +402,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types" -version = "1.0.25" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d4fe522f6fc749c8afce721bdc8f73b715d317c3c02fcb9b51f7a143e4401dd" +checksum = "39676beaa50db545cf15447fc94ec5513b64e85a48357a0625b9a04aef08a910" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -412,9 +414,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-any" -version = "1.0.25" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "124b742619519d5932e586631f11050028b29c30e3e195f2bb04228c886253d6" +checksum = "01bac57c987c93773787619e20f89167db74d460a2d1d40f591d94fb7c22c379" dependencies = [ "alloy-consensus-any", "alloy-rpc-types-eth", @@ -423,9 +425,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "1.0.25" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "781d4d5020bea8f020e164f5593101c2e2f790d66d04a0727839d03bc4411ed7" +checksum = "1cd1e1b4dcdf13eaa96343e5c0dafc2d2e8ce5d20b90347169d46a1df0dec210" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -444,9 +446,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "1.0.25" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30be84f45d4f687b00efaba1e6290cbf53ccc8f6b8fbb54e4c2f9d2a0474ce95" +checksum = "f1b3b1078b8775077525bc9fe9f6577e815ceaecd6c412a4f3b4d8aa2836e8f6" dependencies = [ "alloy-primitives", "serde", @@ -455,9 +457,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "1.0.25" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa8c24b883fe56395db64afcd665fca32dcdef670a59e5338de6892c2e38d7e9" +checksum = "10ab1b8d4649bf7d0db8ab04e31658a6cc20364d920795484d886c35bed3bab4" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -472,15 +474,16 @@ dependencies = [ [[package]] name = "alloy-signer-aws" -version = "1.0.25" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b806737bea3c5091982b8571f36d0ee324f0dcbaef7fedf6bbffbb63f04c5653" +checksum = "a46118173eb381b2911202a83dc4f39267027b0fe7d3533449f5e4ebc0eadcab" dependencies = [ "alloy-consensus", "alloy-network", "alloy-primitives", "alloy-signer", "async-trait", + "aws-config", "aws-sdk-kms", "k256", "spki", @@ -490,9 +493,9 @@ dependencies = [ [[package]] name = "alloy-signer-gcp" -version = "1.0.25" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ebe6b2f97da5e3033be4d2936ba01f3ea82b0573814cb14bf4778db3fde42f5" +checksum = "b8a6aae8a120c191cc5b333bafa9877b3d5ccb2174ea25b6c2c08df28ca9d64b" dependencies = [ "alloy-consensus", "alloy-network", @@ -508,9 +511,9 @@ dependencies = [ [[package]] name = "alloy-signer-ledger" -version = "1.0.25" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d1c7a2c6d8d6532235b65fb40a298fe55df73311c212d368d48fb8ed9b03ce" +checksum = "feb0444055415b5d97c84b0f5d7a611849e68b63caa574aa1737035310dc1dba" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -528,9 +531,9 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "1.0.25" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05724615fd2ec3417f5cd07cab908300cbb3aae5badc1b805ca70c555b26775f" +checksum = "7bdeec36c8d9823102b571b3eab8b323e053dc19c12da14a9687bd474129bf2a" dependencies = [ "alloy-consensus", "alloy-network", @@ -617,9 +620,9 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "1.0.25" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20b7f8b6c540b55e858f958d3a92223494cf83c4fb43ff9b26491edbeb3a3b71" +checksum = "dce5129146a76ca6139a19832c75ad408857a56bcd18cd2c684183b8eacd78d8" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -641,9 +644,9 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "1.0.25" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "260e9584dfd7998760d7dfe1856c6f8f346462b9e7837287d7eddfb3922ef275" +checksum = "e2379d998f46d422ec8ef2b61603bc28cda931e5e267aea1ebe71f62da61d101" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -672,12 +675,12 @@ dependencies = [ [[package]] name = "alloy-tx-macros" -version = "1.0.25" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72e29436068f836727d4e7c819ae6bf6f9c9e19a32e96fc23e814709a277f23a" +checksum = "3b5becb9c269a7d05a2f28d549f86df5a5dbc923e2667eff84fdecac8cda534c" dependencies = [ "alloy-primitives", - "darling", + "darling 0.21.3", "proc-macro2", "quote", "syn 2.0.106", @@ -851,15 +854,13 @@ checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" [[package]] name = "async-compression" -version = "0.4.28" +version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6448dfb3960f0b038e88c781ead1e7eb7929dfc3a71a1336ec9086c00f6d1e75" +checksum = "977eb15ea9efd848bb8a4a1a2500347ed7f0bf794edf0dc3ddcf439f43d36b23" dependencies = [ "compression-codecs", "compression-core", - "flate2", "futures-core", - "memchr", "pin-project-lite", "tokio", ] @@ -920,11 +921,36 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "aws-config" +version = "1.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bc1b40fb26027769f16960d2f4a6bc20c4bb755d403e552c8c1a73af433c246" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-sdk-sts", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http 1.3.1", + "time", + "tokio", + "tracing", + "url", +] + [[package]] name = "aws-credential-types" -version = "1.2.5" +version = "1.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1541072f81945fa1251f8795ef6c92c4282d74d59f88498ae7d4bf00f0ebdad9" +checksum = "d025db5d9f52cbc413b167136afb3d8aeea708c0d8884783cf6253be5e22f6f2" dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", @@ -958,9 +984,9 @@ dependencies = [ [[package]] name = "aws-sdk-kms" -version = "1.84.0" +version = "1.86.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98037a2a0745914d2f0fee41acb6cf88a76f0ed31dd75753b4dc318aa5a4da39" +checksum = "15e7ef7189e532a6d7654befd668b535d31f261c61342397da47ccfa3fb0505a" dependencies = [ "aws-credential-types", "aws-runtime", @@ -978,6 +1004,29 @@ dependencies = [ "tracing", ] +[[package]] +name = "aws-sdk-sts" +version = "1.85.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "410309ad0df4606bc721aff0d89c3407682845453247213a0ccc5ff8801ee107" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-query", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "fastrand", + "http 0.2.12", + "regex-lite", + "tracing", +] + [[package]] name = "aws-sigv4" version = "1.3.4" @@ -1033,9 +1082,9 @@ dependencies = [ [[package]] name = "aws-smithy-json" -version = "0.61.4" +version = "0.61.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a16e040799d29c17412943bdbf488fd75db04112d0c0d4b9290bacf5ae0014b9" +checksum = "eaa31b350998e703e9826b2104dd6f63be0508666e1aba88137af060e8944047" dependencies = [ "aws-smithy-types", ] @@ -1049,11 +1098,21 @@ dependencies = [ "aws-smithy-runtime-api", ] +[[package]] +name = "aws-smithy-query" +version = "0.60.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fbd61ceb3fe8a1cb7352e42689cec5335833cd9f94103a61e98f9bb61c64bb" +dependencies = [ + "aws-smithy-types", + "urlencoding", +] + [[package]] name = "aws-smithy-runtime" -version = "1.9.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3d57c8b53a72d15c8e190475743acf34e4996685e346a3448dd54ef696fc6e0" +checksum = "d3946acbe1ead1301ba6862e712c7903ca9bb230bdf1fbd1b5ac54158ef2ab1f" dependencies = [ "aws-smithy-async", "aws-smithy-http", @@ -1112,6 +1171,15 @@ dependencies = [ "time", ] +[[package]] +name = "aws-smithy-xml" +version = "0.60.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db87b96cb1b16c024980f133968d52882ca0daaee3a086c6decc500f6c99728" +dependencies = [ + "xmlparser", +] + [[package]] name = "aws-types" version = "1.3.8" @@ -1243,9 +1311,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.3" +version = "2.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d" +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" [[package]] name = "bitvec" @@ -1352,10 +1420,11 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.34" +version = "1.2.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42bc4aea80032b7bf409b0bc7ccad88853858911b7713a8062fdc0623867bedc" +checksum = "590f9024a68a8c40351881787f1934dc11afd69090f5edb6831464694d836ea3" dependencies = [ + "find-msvc-tools", "shlex", ] @@ -1422,22 +1491,20 @@ dependencies = [ [[package]] name = "compression-codecs" -version = "0.4.28" +version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46cc6539bf1c592cff488b9f253b30bc0ec50d15407c2cf45e27bd8f308d5905" +checksum = "485abf41ac0c8047c07c87c72c8fb3eb5197f6e9d7ded615dfd1a00ae00a0f64" dependencies = [ "compression-core", "flate2", - "futures-core", "memchr", - "pin-project-lite", ] [[package]] name = "compression-core" -version = "0.4.28" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2957e823c15bde7ecf1e8b64e537aa03a6be5fda0e2334e99887669e75b12e01" +checksum = "e47641d3deaf41fb1538ac1f54735925e275eaf3bf4d55c81b137fba797e5cbb" [[package]] name = "const-hex" @@ -1610,7 +1677,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a707ceda8652f6c7624f2be725652e9524c815bf3b9d55a0b2320be2303f9c11" dependencies = [ - "darling", + "darling 0.20.11", "proc-macro2", "quote", "syn 2.0.106", @@ -1623,8 +1690,18 @@ version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.20.11", + "darling_macro 0.20.11", +] + +[[package]] +name = "darling" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +dependencies = [ + "darling_core 0.21.3", + "darling_macro 0.21.3", ] [[package]] @@ -1641,13 +1718,39 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "darling_core" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "serde", + "strsim", + "syn 2.0.106", +] + [[package]] name = "darling_macro" version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ - "darling_core", + "darling_core 0.20.11", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "darling_macro" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +dependencies = [ + "darling_core 0.21.3", "quote", "syn 2.0.106", ] @@ -1685,9 +1788,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.4.0" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc" dependencies = [ "powerfmt", "serde", @@ -1911,6 +2014,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "find-msvc-tools" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e178e4fba8a2726903f6ba98a6d221e76f9c12c650d5dc0e6afdc50677b49650" + [[package]] name = "firestorm" version = "0.5.1" @@ -2151,7 +2260,7 @@ dependencies = [ "cfg-if", "libc", "r-efi", - "wasi 0.14.2+wasi-0.2.4", + "wasi 0.14.3+wasi-0.2.4", ] [[package]] @@ -2167,6 +2276,7 @@ dependencies = [ "anyhow", "assert_matches", "axum", + "base64", "custom_debug", "faster-hex", "futures", @@ -2954,9 +3064,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.27" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "loom" @@ -3212,7 +3322,7 @@ version = "0.10.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "cfg-if", "foreign-types", "libc", @@ -3450,9 +3560,9 @@ checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "potential_utf" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" dependencies = [ "zerovec", ] @@ -3545,7 +3655,7 @@ checksum = "6fcdab19deb5195a31cf7726a210015ff1496ba1464fd42cb4f537b8b01b471f" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.9.3", + "bitflags 2.9.4", "lazy_static", "num-traits", "rand 0.9.2", @@ -3758,7 +3868,7 @@ version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", ] [[package]] @@ -3971,7 +4081,7 @@ version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "errno", "libc", "linux-raw-sys", @@ -4160,7 +4270,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -4173,7 +4283,7 @@ version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80fb1d92c5028aa318b4b8bd7302a5bfcf48be96a37fc6fc790f806b0004ee0c" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -4297,7 +4407,7 @@ version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de90945e6565ce0d9a25098082ed4ee4002e047cb59892c318d66821e14bb30f" dependencies = [ - "darling", + "darling 0.20.11", "proc-macro2", "quote", "syn 2.0.106", @@ -4601,7 +4711,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -4779,12 +4889,11 @@ dependencies = [ [[package]] name = "time" -version = "0.3.41" +version = "0.3.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +checksum = "83bde6f1ec10e72d583d91623c939f623002284ef622b87de38cfd546cbf2031" dependencies = [ "deranged", - "itoa", "num-conv", "powerfmt", "serde", @@ -4794,15 +4903,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" [[package]] name = "time-macros" -version = "0.2.22" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" dependencies = [ "num-conv", "time-core", @@ -5001,7 +5110,7 @@ version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "bytes", "futures-util", "http 1.3.1", @@ -5198,6 +5307,12 @@ dependencies = [ "serde", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "utf8_iter" version = "1.0.4" @@ -5206,9 +5321,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.18.0" +version = "1.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f33196643e165781c20a5ead5582283a7dacbb87855d867fbc2df3f81eddc1be" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" dependencies = [ "getrandom 0.3.3", "js-sys", @@ -5265,11 +5380,11 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" -version = "0.14.2+wasi-0.2.4" +version = "0.14.3+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +checksum = "6a51ae83037bdd272a9e28ce236db8c07016dd0d50c27038b3f407533c030c95" dependencies = [ - "wit-bindgen-rt", + "wit-bindgen", ] [[package]] @@ -5358,9 +5473,9 @@ dependencies = [ [[package]] name = "wasmtimer" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8d49b5d6c64e8558d9b1b065014426f35c18de636895d24893dbbd329743446" +checksum = "1c598d6b99ea013e35844697fc4670d08339d5cda15588f193c6beedd12f644b" dependencies = [ "futures", "js-sys", @@ -5765,13 +5880,10 @@ dependencies = [ ] [[package]] -name = "wit-bindgen-rt" -version = "0.39.0" +name = "wit-bindgen" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags 2.9.3", -] +checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814" [[package]] name = "writeable" @@ -5788,6 +5900,12 @@ dependencies = [ "tap", ] +[[package]] +name = "xmlparser" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" + [[package]] name = "yoke" version = "0.8.0" diff --git a/Cargo.toml b/Cargo.toml index 69470b40..daf50300 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ axum = { version = "0.8.1", default-features = false, features = [ "tokio", "http1", ] } +base64 = "0.22.1" custom_debug = "0.6.1" faster-hex = "0.10.0" futures = "0.3" @@ -46,8 +47,8 @@ serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0.116", features = ["raw_value"] } serde_with = "3.8.1" snmalloc-rs = "0.3" -tap_graph = "0.3.3" -thegraph-core = { version = "0.15", features = [ +tap_graph = { version = "0.3.4", features = ["v2"] } +thegraph-core = { version = "0.15.1", features = [ "alloy-contract", "alloy-signer-local", "attestation", diff --git a/README.md b/README.md index 01f13dcc..a7e1bf28 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ client query. Indexer fees are clamped to a maximum of the gateway's budget. For an overview of TAP see https://github.com/semiotic-ai/timeline-aggregation-protocol. -The gateway acts as a TAP sender, where each indexer request is sent with a TAP receipt. The gateway +This is a horizon-ready gateway that generates TAP v2 receipts exclusively. The gateway acts as a TAP sender, where each indexer request is sent with a TAP v2 receipt. The gateway operator is expected to run 2 additional services: - [tap-aggregator](https://github.com/semiotic-ai/timeline-aggregation-protocol/tree/main/tap_aggregator): @@ -113,7 +113,7 @@ operator is expected to run 2 additional services: The gateway operator is also expected to manage at least 2 wallets: - sender: requires ETH for transaction gas and GRT to allocate into TAP escrow balances for paying indexers -- authorized signer: used by the gateway and tap-aggregator to sign receipts and RAVs +- authorized signer: used by the gateway and tap-aggregator to sign v2 receipts and RAVs ## operational notes diff --git a/src/client_query.rs b/src/client_query.rs index 638b0eac..393ecb24 100644 --- a/src/client_query.rs +++ b/src/client_query.rs @@ -285,7 +285,12 @@ async fn run_indexer_queries( let min_fee = *(min_fee.0 * grt_per_usd * one_grt) / selections.len() as f64; let indexer_fee = selection.fee.as_f64() * budget as f64; let fee = indexer_fee.max(min_fee) as u128; - let receipt = match ctx.receipt_signer.create_receipt(largest_allocation, fee) { + let receipt = match ctx.receipt_signer.create_receipt( + largest_allocation, + *indexer, + fee, + selection.data.largest_allocation_is_legacy, + ) { Ok(receipt) => receipt, Err(err) => { tracing::error!(?indexer, %deployment, error=?err, "failed to create receipt"); @@ -468,6 +473,7 @@ struct CandidateMetadata { #[debug(with = std::fmt::Display::fmt)] url: Url, largest_allocation: AllocationId, + largest_allocation_is_legacy: bool, } /// Given a list of indexings, build a list of candidates that are within the required block range @@ -576,6 +582,7 @@ fn build_candidates_list( deployment, url: indexing.indexer.url.clone(), largest_allocation: indexing.largest_allocation, + largest_allocation_is_legacy: indexing.largest_allocation_is_legacy, }, perf: perf.response, fee: Normalized::new(indexing.fee as f64 / budget as f64).unwrap_or(Normalized::ONE), @@ -686,7 +693,12 @@ pub async fn handle_indexer_query( let fee = *(ctx.budgeter.query_fees_target.0 * grt_per_usd * one_grt) as u128; let allocation = indexing.largest_allocation; - let receipt = match ctx.receipt_signer.create_receipt(allocation, fee) { + let receipt = match ctx.receipt_signer.create_receipt( + allocation, + *indexer, + fee, + indexing.largest_allocation_is_legacy, + ) { Ok(receipt) => receipt, Err(err) => { return Err(Error::Internal(anyhow!("failed to create receipt: {err}"))); diff --git a/src/config.rs b/src/config.rs index 4aaefe8e..f9244072 100644 --- a/src/config.rs +++ b/src/config.rs @@ -46,7 +46,7 @@ pub struct Config { /// Minimum indexer-service version that will receive queries #[serde_as(as = "DisplayFromStr")] pub min_indexer_version: Version, - /// Indexers used to query the network subgraph + /// Trusted indexers that can serve the network subgraph for free pub trusted_indexers: Vec, /// Maximum acceptable lag (in seconds) for network subgraph responses (default: 120) #[serde(default = "default_network_subgraph_max_lag_seconds")] @@ -61,6 +61,8 @@ pub struct Config { #[serde(deserialize_with = "deserialize_not_nan_f64")] pub query_fees_target: NotNan, pub receipts: Receipts, + /// Address for the Subgraph Service + pub subgraph_service: Address, } /// Default network subgraph max lag threshold (120 seconds) @@ -184,10 +186,14 @@ impl From for rdkafka::config::ClientConfig { pub struct Receipts { /// TAP verifier contract chain pub chain_id: U256, + /// TAP payer address + pub payer: Address, /// TAP signer key pub signer: B256, - /// TAP verifier contract address + /// TAP verifier contract address (v2) pub verifier: Address, + /// Legacy TAP verifier contract address (v1) + pub legacy_verifier: Address, } /// Load the configuration from a JSON file. diff --git a/src/indexer_client.rs b/src/indexer_client.rs index c0669042..104ae2dc 100644 --- a/src/indexer_client.rs +++ b/src/indexer_client.rs @@ -47,7 +47,7 @@ impl IndexerClient { query: &str, ) -> Result { let (auth_key, auth_value) = match auth { - IndexerAuth::Paid(receipt, _, _) => ("Tap-Receipt", receipt.serialize()), + IndexerAuth::Paid(receipt, _, _) => ("tap-receipt", receipt.serialize()), IndexerAuth::Free(token) => (AUTHORIZATION.as_str(), format!("Bearer {token}")), }; diff --git a/src/main.rs b/src/main.rs index 24ac8cb6..21716633 100644 --- a/src/main.rs +++ b/src/main.rs @@ -143,9 +143,12 @@ async fn main() { network.wait_until_ready().await; let receipt_signer: &'static ReceiptSigner = Box::leak(Box::new(ReceiptSigner::new( + conf.receipts.payer, receipt_signer, conf.receipts.chain_id, conf.receipts.verifier, + conf.receipts.legacy_verifier, + conf.subgraph_service, ))); let auth_service = init_auth_service( diff --git a/src/network/indexer_processing.rs b/src/network/indexer_processing.rs index 700e3d6f..a2a13b7c 100644 --- a/src/network/indexer_processing.rs +++ b/src/network/indexer_processing.rs @@ -54,6 +54,8 @@ pub struct IndexerInfo { pub struct IndexingRawInfo { /// The largest allocation. pub largest_allocation: AllocationId, + /// Whether the largest allocation is a legacy allocation + pub largest_allocation_is_legacy: bool, } /// Internal representation of the fetched indexer's indexing information. @@ -65,6 +67,9 @@ pub struct IndexingInfo { /// The largest allocation. pub largest_allocation: AllocationId, + /// Whether the largest allocation is a legacy allocation + pub largest_allocation_is_legacy: bool, + /// The indexing progress information /// /// See [`IndexingProgress`] for more information. @@ -78,6 +83,7 @@ impl From for IndexingInfo<(), ()> { fn from(raw: IndexingRawInfo) -> Self { Self { largest_allocation: raw.largest_allocation, + largest_allocation_is_legacy: raw.largest_allocation_is_legacy, progress: (), fee: (), } @@ -93,6 +99,7 @@ impl IndexingInfo<(), ()> { ) -> IndexingInfo { IndexingInfo { largest_allocation: self.largest_allocation, + largest_allocation_is_legacy: self.largest_allocation_is_legacy, progress, fee: self.fee, } @@ -105,6 +112,7 @@ impl IndexingInfo { fn with_fee(self, fee: u128) -> IndexingInfo { IndexingInfo { largest_allocation: self.largest_allocation, + largest_allocation_is_legacy: self.largest_allocation_is_legacy, progress: self.progress, fee, } diff --git a/src/network/pre_processing.rs b/src/network/pre_processing.rs index dc7b64a3..12023a72 100644 --- a/src/network/pre_processing.rs +++ b/src/network/pre_processing.rs @@ -18,7 +18,7 @@ pub fn into_internal_indexers_raw_info<'a>( ) -> HashMap { let mut indexer_indexing_largest_allocation: HashMap< (IndexerId, DeploymentId), - (AllocationId, u128), + (AllocationId, u128, bool), > = HashMap::new(); data.flat_map(|subgraph| { @@ -53,22 +53,30 @@ pub fn into_internal_indexers_raw_info<'a>( }; // Update the indexer's indexings largest allocations table - let indexing_largest_allocation = match indexer_indexing_largest_allocation - .entry((indexer_id, deployment_id)) - { - Entry::Vacant(entry) => { - entry.insert((allocation.id, allocation.allocated_tokens)); - allocation.id - } - Entry::Occupied(entry) => { - let (largest_allocation_address, largest_allocation_amount) = entry.into_mut(); - if allocation.allocated_tokens > *largest_allocation_amount { - *largest_allocation_address = allocation.id; - *largest_allocation_amount = allocation.allocated_tokens; + let (indexing_largest_allocation, indexing_largest_allocation_is_legacy) = + match indexer_indexing_largest_allocation.entry((indexer_id, deployment_id)) { + Entry::Vacant(entry) => { + entry.insert(( + allocation.id, + allocation.allocated_tokens, + allocation.is_legacy, + )); + (allocation.id, allocation.is_legacy) } - *largest_allocation_address - } - }; + Entry::Occupied(entry) => { + let ( + largest_allocation_address, + largest_allocation_amount, + largest_allocation_is_legacy, + ) = entry.into_mut(); + if allocation.allocated_tokens > *largest_allocation_amount { + *largest_allocation_address = allocation.id; + *largest_allocation_amount = allocation.allocated_tokens; + *largest_allocation_is_legacy = allocation.is_legacy; + } + (*largest_allocation_address, *largest_allocation_is_legacy) + } + }; // Update the indexer's indexings info let indexing = indexer @@ -76,9 +84,11 @@ pub fn into_internal_indexers_raw_info<'a>( .entry(deployment_id) .or_insert(IndexingRawInfo { largest_allocation: allocation.id, + largest_allocation_is_legacy: allocation.is_legacy, }); indexing.largest_allocation = indexing_largest_allocation; + indexing.largest_allocation_is_legacy = indexing_largest_allocation_is_legacy; } acc diff --git a/src/network/snapshot.rs b/src/network/snapshot.rs index 3ee43ec8..48a4b38e 100644 --- a/src/network/snapshot.rs +++ b/src/network/snapshot.rs @@ -36,6 +36,8 @@ pub struct Indexing { /// This is, among all allocations associated with the indexer and deployment, the address /// with the largest amount of allocated tokens. pub largest_allocation: AllocationId, + /// Whether the largest allocation is a legacy allocation + pub largest_allocation_is_legacy: bool, /// The indexer pub indexer: Arc, /// The indexing progress. @@ -365,6 +367,7 @@ fn construct_indexings_table_row( let indexing = Indexing { id: indexing_id, largest_allocation: indexing_largest_allocation_addr, + largest_allocation_is_legacy: indexing_info.largest_allocation_is_legacy, indexer: Arc::clone(indexer), progress: IndexingProgress { latest_block: indexing_progress.latest_block, diff --git a/src/network/subgraph_client.rs b/src/network/subgraph_client.rs index 87b44899..7117f7c5 100644 --- a/src/network/subgraph_client.rs +++ b/src/network/subgraph_client.rs @@ -77,6 +77,7 @@ pub mod types { pub id: AllocationId, #[serde_as(as = "serde_with::DisplayFromStr")] pub allocated_tokens: u128, + pub is_legacy: bool, pub indexer: Indexer, } @@ -94,7 +95,7 @@ pub mod types { #[serde_as] #[derive(Clone, CustomDebug, Deserialize)] pub struct TrustedIndexer { - /// network subgraph endpoint + /// Complete network subgraph endpoint URL (e.g., http://indexer:7601/subgraphs/id/Qmc2Cb...) #[debug(with = std::fmt::Display::fmt)] #[serde_as(as = "serde_with::DisplayFromStr")] pub url: Url, @@ -180,6 +181,7 @@ impl Client { ) { id allocatedTokens + isLegacy indexer { id url diff --git a/src/receipts.rs b/src/receipts.rs index 63dc333a..480afd10 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -1,9 +1,10 @@ use std::time::SystemTime; -use rand::RngCore; -use tap_graph::{Receipt as TapReceipt, SignedReceipt}; +use base64::{Engine as _, prelude::BASE64_STANDARD}; +use prost::Message as _; +use rand::RngCore as _; use thegraph_core::{ - AllocationId, + AllocationId, CollectionId, alloy::{ dyn_abi::Eip712Domain, primitives::{Address, U256}, @@ -11,64 +12,201 @@ use thegraph_core::{ }, }; -pub struct Receipt(SignedReceipt); +#[derive(Debug, Clone)] +pub enum Receipt { + V1(tap_graph::SignedReceipt), + V2(tap_graph::v2::SignedReceipt), +} impl Receipt { pub fn value(&self) -> u128 { - self.0.message.value + match self { + Receipt::V1(receipt) => receipt.message.value, + Receipt::V2(receipt) => receipt.message.value, + } } pub fn allocation(&self) -> Address { - self.0.message.allocation_id + match self { + Receipt::V1(receipt) => receipt.message.allocation_id, + Receipt::V2(receipt) => { + // TAP v2 receipts use collection ids which are 32 bytes. + // For the Subgraph Service these are 20 byte allocation ids with zero padding. + CollectionId::from(receipt.message.collection_id).as_address() + } + } } pub fn serialize(&self) -> String { - serde_json::to_string(&self.0).unwrap() + match self { + Receipt::V1(receipt) => { + // V1 receipts use JSON serialization + serde_json::to_string(receipt).expect("failed to serialize v1 receipt") + } + Receipt::V2(receipt) => self.serialize_v2(receipt), + } + } + + fn serialize_v2(&self, receipt: &tap_graph::v2::SignedReceipt) -> String { + #[derive(prost::Message)] + struct ReceiptMessage { + #[prost(bytes, tag = "1")] + collection_id: Vec, + #[prost(bytes, tag = "2")] + payer: Vec, + #[prost(bytes, tag = "3")] + data_service: Vec, + #[prost(bytes, tag = "4")] + service_provider: Vec, + #[prost(uint64, tag = "5")] + timestamp_ns: u64, + #[prost(uint64, tag = "6")] + nonce: u64, + #[prost(message, optional, tag = "7")] + value: Option, + } + #[derive(prost::Message)] + struct Uint128 { + #[prost(uint64, tag = "1")] + high: u64, + #[prost(uint64, tag = "2")] + low: u64, + } + #[derive(prost::Message)] + struct SignedReceipt { + #[prost(message, optional, tag = "1")] + message: Option, + #[prost(bytes, tag = "2")] + signature: Vec, + } + let receipt_message = ReceiptMessage { + collection_id: receipt.message.collection_id.to_vec(), + payer: receipt.message.payer.to_vec(), + data_service: receipt.message.data_service.to_vec(), + service_provider: receipt.message.service_provider.to_vec(), + timestamp_ns: receipt.message.timestamp_ns, + nonce: receipt.message.nonce, + value: Some(Uint128 { + high: (receipt.message.value >> 64) as u64, + low: receipt.message.value as u64, + }), + }; + let signature_bytes = receipt.signature.as_bytes().to_vec(); + + let signed_receipt = SignedReceipt { + message: Some(receipt_message), + signature: signature_bytes, + }; + + BASE64_STANDARD.encode(signed_receipt.encode_to_vec()) } } pub struct ReceiptSigner { + payer: Address, signer: PrivateKeySigner, - domain: Eip712Domain, + v2_domain: Eip712Domain, + v1_domain: Eip712Domain, + data_service: Address, } impl ReceiptSigner { - pub fn new(signer: PrivateKeySigner, chain_id: U256, verifying_contract: Address) -> Self { + pub fn new( + payer: Address, + signer: PrivateKeySigner, + chain_id: U256, + verifying_contract: Address, + legacy_verifying_contract: Address, + data_service: Address, + ) -> Self { + let v2_domain = Eip712Domain { + name: Some("GraphTallyCollector".into()), + version: Some("1".into()), + chain_id: Some(chain_id), + verifying_contract: Some(verifying_contract), + salt: None, + }; + let v1_domain = Eip712Domain { + name: Some("TAP".into()), + version: Some("1".into()), + chain_id: Some(chain_id), + verifying_contract: Some(legacy_verifying_contract), + salt: None, + }; Self { + payer, signer, - domain: Eip712Domain { - name: Some("TAP".into()), - version: Some("1".into()), - chain_id: Some(chain_id), - verifying_contract: Some(verifying_contract), - salt: None, - }, + v2_domain, + v1_domain, + data_service, } } - pub fn create_receipt(&self, allocation: AllocationId, fee: u128) -> anyhow::Result { - // Nonce generated with CSPRNG (ChaCha12), to avoid collision with receipts generated by - // other gateway processes. - // See https://docs.rs/rand/latest/rand/rngs/index.html#our-generators. + /// Create a v1 receipt for legacy allocations + pub fn create_v1_receipt( + &self, + allocation: AllocationId, + fee: u128, + ) -> anyhow::Result { let nonce = rand::rng().next_u64(); - let timestamp_ns = SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH) .unwrap() .as_nanos() .try_into() .map_err(|_| anyhow::anyhow!("failed to convert timestamp to ns"))?; - - let receipt = TapReceipt { + let receipt = tap_graph::Receipt { allocation_id: allocation.0.0.into(), timestamp_ns, nonce, value: fee, }; - let signed = SignedReceipt::new(&self.domain, receipt, &self.signer) - .map_err(|e| anyhow::anyhow!("failed to sign receipt: {:?}", e))?; + tap_graph::SignedReceipt::new(&self.v1_domain, receipt, &self.signer) + .map(Receipt::V1) + .map_err(|e| anyhow::anyhow!("failed to sign v1 receipt: {:?}", e)) + } + + /// Create a v2 receipt for new allocations + pub fn create_v2_receipt( + &self, + allocation: AllocationId, + indexer: Address, + fee: u128, + ) -> anyhow::Result { + let nonce = rand::rng().next_u64(); + let timestamp_ns = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_nanos() + .try_into() + .map_err(|_| anyhow::anyhow!("failed to convert timestamp to ns"))?; + let receipt = tap_graph::v2::Receipt { + collection_id: CollectionId::from(allocation).0.into(), + payer: self.payer, + data_service: self.data_service, + service_provider: indexer, + timestamp_ns, + nonce, + value: fee, + }; + tap_graph::v2::SignedReceipt::new(&self.v2_domain, receipt, &self.signer) + .map(Receipt::V2) + .map_err(|e| anyhow::anyhow!("failed to sign v2 receipt: {:?}", e)) + } - Ok(Receipt(signed)) + /// Create a receipt based on whether the allocation is legacy or not + pub fn create_receipt( + &self, + allocation: AllocationId, + indexer: Address, + fee: u128, + is_legacy: bool, + ) -> anyhow::Result { + if is_legacy { + self.create_v1_receipt(allocation, fee) + } else { + self.create_v2_receipt(allocation, indexer, fee) + } } } @@ -81,25 +219,73 @@ mod tests { use super::*; - #[test] - fn create_receipt() { - //* Given + fn create_test_signer() -> ReceiptSigner { let secret_key = PrivateKeySigner::from_slice(&[0xcd; 32]).expect("invalid secret key"); - let signer = ReceiptSigner::new( + ReceiptSigner::new( + address!("1111111111111111111111111111111111111111"), secret_key, 1.try_into().expect("invalid chain id"), address!("177b557b12f22bb17a9d73dcc994d978dd6f5f89"), - ); + address!("277b557b12f22bb17a9d73dcc994d978dd6f5f89"), // legacy verifier + address!("2222222222222222222222222222222222222222"), + ) + } + #[test] + fn create_v2_receipt() { + let signer = create_test_signer(); let allocation = allocation_id!("89b23fea4e46d40e8a4c6cca723e2a03fdd4bec2"); let fee = 1000; - //* When - let res = signer.create_receipt(allocation, fee); + let receipt = signer + .create_receipt( + allocation, + address!("3333333333333333333333333333333333333333"), + fee, + false, // not legacy + ) + .expect("failed to create v2 receipt"); + + assert_eq!(receipt.value(), fee); + assert_eq!(AllocationId::from(receipt.allocation()), allocation); + } + + #[test] + fn test_receipt_serialization() { + let signer = create_test_signer(); + let allocation = allocation_id!("89b23fea4e46d40e8a4c6cca723e2a03fdd4bec2"); + let fee = 1000; + + let receipt = signer + .create_receipt( + allocation, + address!("3333333333333333333333333333333333333333"), + fee, + false, // not legacy + ) + .expect("failed to create v2 receipt"); + + let serialized = receipt.serialize(); + assert!(!serialized.is_empty()); + assert_eq!(receipt.value(), fee); + } + + #[test] + fn create_v1_receipt() { + let signer = create_test_signer(); + let allocation = allocation_id!("89b23fea4e46d40e8a4c6cca723e2a03fdd4bec2"); + let fee = 1000; - //* Then - let receipt = res.expect("failed to create tap receipt"); + let receipt = signer + .create_receipt( + allocation, + address!("3333333333333333333333333333333333333333"), + fee, + true, // legacy + ) + .expect("failed to create v1 receipt"); assert_eq!(receipt.value(), fee); + assert_eq!(AllocationId::from(receipt.allocation()), allocation); } }