feat: implement indexer service skeleton

This commit is contained in:
Daniil Polyakov 2026-01-24 04:05:59 +03:00
parent f4c81f5e78
commit 2288a725d2
44 changed files with 1774 additions and 24 deletions

547
Cargo.lock generated
View File

@ -1177,6 +1177,12 @@ dependencies = [
"shlex",
]
[[package]]
name = "cesu8"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
[[package]]
name = "cexpr"
version = "0.6.0"
@ -1310,6 +1316,16 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
[[package]]
name = "combine"
version = "4.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
dependencies = [
"bytes",
"memchr",
]
[[package]]
name = "common"
version = "0.1.0"
@ -1399,6 +1415,16 @@ dependencies = [
"libc",
]
[[package]]
name = "core-foundation"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.7"
@ -1412,7 +1438,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf"
dependencies = [
"bitflags 1.3.2",
"core-foundation",
"core-foundation 0.9.4",
"libc",
]
@ -2151,6 +2177,10 @@ name = "futures-timer"
version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24"
dependencies = [
"gloo-timers",
"send_wrapper",
]
[[package]]
name = "futures-util"
@ -2235,6 +2265,52 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
[[package]]
name = "gloo-net"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06f627b1a58ca3d42b45d6104bf1e1a03799df472df00988b6ba21accc10580"
dependencies = [
"futures-channel",
"futures-core",
"futures-sink",
"gloo-utils",
"http 1.4.0",
"js-sys",
"pin-project",
"serde",
"serde_json",
"thiserror 1.0.69",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
]
[[package]]
name = "gloo-timers"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c"
dependencies = [
"futures-channel",
"futures-core",
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "gloo-utils"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa"
dependencies = [
"js-sys",
"serde",
"serde_json",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "governor"
version = "0.6.3"
@ -2507,6 +2583,7 @@ dependencies = [
"http 1.4.0",
"hyper",
"hyper-util",
"log",
"rustls",
"rustls-pki-types",
"tokio",
@ -2695,6 +2772,45 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ee796ad498c8d9a1d68e477df8f754ed784ef875de1414ebdaf169f70a6a784"
[[package]]
name = "indexer_service"
version = "0.1.0"
dependencies = [
"anyhow",
"async-trait",
"clap",
"env_logger",
"indexer_service_protocol",
"indexer_service_rpc",
"jsonrpsee",
"log",
"tokio",
"tokio-util",
]
[[package]]
name = "indexer_service_protocol"
version = "0.1.0"
dependencies = [
"base64",
"borsh",
"common",
"nssa",
"nssa_core",
"schemars 1.2.0",
"serde",
]
[[package]]
name = "indexer_service_rpc"
version = "0.1.0"
dependencies = [
"indexer_service_protocol",
"jsonrpsee",
"schemars 1.2.0",
"serde_json",
]
[[package]]
name = "indexmap"
version = "1.9.3"
@ -2855,6 +2971,28 @@ dependencies = [
"zeroize",
]
[[package]]
name = "jni"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
dependencies = [
"cesu8",
"cfg-if",
"combine",
"jni-sys",
"log",
"thiserror 1.0.69",
"walkdir",
"windows-sys 0.45.0",
]
[[package]]
name = "jni-sys"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
[[package]]
name = "jobserver"
version = "0.1.34"
@ -2875,6 +3013,178 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "jsonrpsee"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f3f48dc3e6b8bd21e15436c1ddd0bc22a6a54e8ec46fedd6adf3425f396ec6a"
dependencies = [
"jsonrpsee-client-transport",
"jsonrpsee-core",
"jsonrpsee-http-client",
"jsonrpsee-proc-macros",
"jsonrpsee-server",
"jsonrpsee-types",
"jsonrpsee-wasm-client",
"jsonrpsee-ws-client",
"tokio",
"tracing",
]
[[package]]
name = "jsonrpsee-client-transport"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf36eb27f8e13fa93dcb50ccb44c417e25b818cfa1a481b5470cd07b19c60b98"
dependencies = [
"base64",
"futures-channel",
"futures-util",
"gloo-net",
"http 1.4.0",
"jsonrpsee-core",
"pin-project",
"rustls",
"rustls-pki-types",
"rustls-platform-verifier",
"soketto",
"thiserror 2.0.17",
"tokio",
"tokio-rustls",
"tokio-util",
"tracing",
"url",
]
[[package]]
name = "jsonrpsee-core"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "316c96719901f05d1137f19ba598b5fe9c9bc39f4335f67f6be8613921946480"
dependencies = [
"async-trait",
"bytes",
"futures-timer",
"futures-util",
"http 1.4.0",
"http-body",
"http-body-util",
"jsonrpsee-types",
"parking_lot",
"pin-project",
"rand 0.9.2",
"rustc-hash",
"serde",
"serde_json",
"thiserror 2.0.17",
"tokio",
"tokio-stream",
"tower 0.5.2",
"tracing",
"wasm-bindgen-futures",
]
[[package]]
name = "jsonrpsee-http-client"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "790bedefcec85321e007ff3af84b4e417540d5c87b3c9779b9e247d1bcc3dab8"
dependencies = [
"base64",
"http-body",
"hyper",
"hyper-rustls",
"hyper-util",
"jsonrpsee-core",
"jsonrpsee-types",
"rustls",
"rustls-platform-verifier",
"serde",
"serde_json",
"thiserror 2.0.17",
"tokio",
"tower 0.5.2",
"url",
]
[[package]]
name = "jsonrpsee-proc-macros"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2da3f8ab5ce1bb124b6d082e62dffe997578ceaf0aeb9f3174a214589dc00f07"
dependencies = [
"heck",
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 2.0.111",
]
[[package]]
name = "jsonrpsee-server"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c51b7c290bb68ce3af2d029648148403863b982f138484a73f02a9dd52dbd7f"
dependencies = [
"futures-util",
"http 1.4.0",
"http-body",
"http-body-util",
"hyper",
"hyper-util",
"jsonrpsee-core",
"jsonrpsee-types",
"pin-project",
"route-recognizer",
"serde",
"serde_json",
"soketto",
"thiserror 2.0.17",
"tokio",
"tokio-stream",
"tokio-util",
"tower 0.5.2",
"tracing",
]
[[package]]
name = "jsonrpsee-types"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc88ff4688e43cc3fa9883a8a95c6fa27aa2e76c96e610b737b6554d650d7fd5"
dependencies = [
"http 1.4.0",
"serde",
"serde_json",
"thiserror 2.0.17",
]
[[package]]
name = "jsonrpsee-wasm-client"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7902885de4779f711a95d82c8da2d7e5f9f3a7c7cfa44d51c067fd1c29d72a3c"
dependencies = [
"jsonrpsee-client-transport",
"jsonrpsee-core",
"jsonrpsee-types",
"tower 0.5.2",
]
[[package]]
name = "jsonrpsee-ws-client"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b6fceceeb05301cc4c065ab3bd2fa990d41ff4eb44e4ca1b30fa99c057c3e79"
dependencies = [
"http 1.4.0",
"jsonrpsee-client-transport",
"jsonrpsee-core",
"jsonrpsee-types",
"tower 0.5.2",
"url",
]
[[package]]
name = "k256"
version = "0.13.4"
@ -3717,10 +4027,10 @@ dependencies = [
"libc",
"log",
"openssl",
"openssl-probe",
"openssl-probe 0.1.6",
"openssl-sys",
"schannel",
"security-framework",
"security-framework 2.11.1",
"security-framework-sys",
"tempfile",
]
@ -3967,6 +4277,12 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
[[package]]
name = "openssl-probe"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"
[[package]]
name = "openssl-sys"
version = "0.9.111"
@ -4324,7 +4640,7 @@ dependencies = [
"quinn-udp",
"rustc-hash",
"rustls",
"socket2 0.5.10",
"socket2 0.6.1",
"thiserror 2.0.17",
"tokio",
"tracing",
@ -4829,6 +5145,12 @@ dependencies = [
"librocksdb-sys",
]
[[package]]
name = "route-recognizer"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746"
[[package]]
name = "rpds"
version = "1.2.0"
@ -4944,6 +5266,7 @@ version = "0.23.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f"
dependencies = [
"log",
"once_cell",
"ring",
"rustls-pki-types",
@ -4952,6 +5275,18 @@ dependencies = [
"zeroize",
]
[[package]]
name = "rustls-native-certs"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63"
dependencies = [
"openssl-probe 0.2.1",
"rustls-pki-types",
"schannel",
"security-framework 3.5.1",
]
[[package]]
name = "rustls-pki-types"
version = "1.13.2"
@ -4962,6 +5297,33 @@ dependencies = [
"zeroize",
]
[[package]]
name = "rustls-platform-verifier"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19787cda76408ec5404443dc8b31795c87cd8fec49762dc75fa727740d34acc1"
dependencies = [
"core-foundation 0.10.1",
"core-foundation-sys",
"jni",
"log",
"once_cell",
"rustls",
"rustls-native-certs",
"rustls-platform-verifier-android",
"rustls-webpki",
"security-framework 3.5.1",
"security-framework-sys",
"webpki-root-certs 0.26.11",
"windows-sys 0.52.0",
]
[[package]]
name = "rustls-platform-verifier-android"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
[[package]]
name = "rustls-webpki"
version = "0.103.8"
@ -5004,6 +5366,15 @@ dependencies = [
"yaml-rust2",
]
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "schannel"
version = "0.1.28"
@ -5027,16 +5398,29 @@ dependencies = [
[[package]]
name = "schemars"
version = "1.1.0"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9558e172d4e8533736ba97870c4b2cd63f84b382a3d6eb063da41b91cce17289"
checksum = "54e910108742c57a770f492731f99be216a52fadd361b06c8fb59d74ccc267d2"
dependencies = [
"dyn-clone",
"ref-cast",
"schemars_derive",
"serde",
"serde_json",
]
[[package]]
name = "schemars_derive"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4908ad288c5035a8eb12cfdf0d49270def0a268ee162b75eeee0f85d155a7c45"
dependencies = [
"proc-macro2",
"quote",
"serde_derive_internals",
"syn 2.0.111",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
@ -5085,7 +5469,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
dependencies = [
"bitflags 2.10.0",
"core-foundation",
"core-foundation 0.9.4",
"core-foundation-sys",
"libc",
"security-framework-sys",
]
[[package]]
name = "security-framework"
version = "3.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef"
dependencies = [
"bitflags 2.10.0",
"core-foundation 0.10.1",
"core-foundation-sys",
"libc",
"security-framework-sys",
@ -5111,6 +5508,12 @@ dependencies = [
"serde_core",
]
[[package]]
name = "send_wrapper"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0"
[[package]]
name = "sequencer_core"
version = "0.1.0"
@ -5216,6 +5619,17 @@ dependencies = [
"syn 2.0.111",
]
[[package]]
name = "serde_derive_internals"
version = "0.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.111",
]
[[package]]
name = "serde_json"
version = "1.0.145"
@ -5273,7 +5687,7 @@ dependencies = [
"indexmap 1.9.3",
"indexmap 2.12.1",
"schemars 0.9.0",
"schemars 1.1.0",
"schemars 1.2.0",
"serde_core",
"serde_json",
"serde_with_macros",
@ -5401,6 +5815,22 @@ dependencies = [
"windows-sys 0.60.2",
]
[[package]]
name = "soketto"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e859df029d160cb88608f5d7df7fb4753fd20fdfb4de5644f3d8b8440841721"
dependencies = [
"base64",
"bytes",
"futures",
"http 1.4.0",
"httparse",
"log",
"rand 0.8.5",
"sha1",
]
[[package]]
name = "spin"
version = "0.9.8"
@ -5540,7 +5970,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
dependencies = [
"bitflags 1.3.2",
"core-foundation",
"core-foundation 0.9.4",
"system-configuration-sys",
]
@ -5791,12 +6221,13 @@ dependencies = [
[[package]]
name = "tokio-util"
version = "0.7.17"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594"
checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098"
dependencies = [
"bytes",
"futures-core",
"futures-io",
"futures-sink",
"pin-project-lite",
"tokio",
@ -6125,6 +6556,16 @@ version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "walkdir"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
dependencies = [
"same-file",
"winapi-util",
]
[[package]]
name = "wallet"
version = "0.1.0"
@ -6270,6 +6711,24 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "webpki-root-certs"
version = "0.26.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e"
dependencies = [
"webpki-root-certs 1.0.5",
]
[[package]]
name = "webpki-root-certs"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36a29fc0408b113f68cf32637857ab740edfafdf460c326cd2afaa2d84cc05dc"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "webpki-roots"
version = "1.0.4"
@ -6380,6 +6839,15 @@ dependencies = [
"windows-link",
]
[[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",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
@ -6407,6 +6875,21 @@ dependencies = [
"windows-link",
]
[[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",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
@ -6440,6 +6923,12 @@ dependencies = [
"windows_x86_64_msvc 0.53.1",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
@ -6452,6 +6941,12 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
@ -6464,6 +6959,12 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
@ -6488,6 +6989,12 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
[[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.52.6"
@ -6500,6 +7007,12 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
@ -6512,6 +7025,12 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
@ -6524,6 +7043,12 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"

View File

@ -2,16 +2,19 @@
resolver = "3"
members = [
"integration_tests",
"sequencer_runner",
"storage",
"key_protocol",
"sequencer_rpc",
"mempool",
"wallet",
"sequencer_core",
"common",
"nssa",
"nssa/core",
"sequencer_core",
"sequencer_rpc",
"sequencer_runner",
"indexer_service",
"indexer_service/protocol",
"indexer_service/rpc",
"program_methods",
"program_methods/guest",
"test_program_methods",
@ -32,6 +35,9 @@ key_protocol = { path = "key_protocol" }
sequencer_core = { path = "sequencer_core" }
sequencer_rpc = { path = "sequencer_rpc" }
sequencer_runner = { path = "sequencer_runner" }
indexer_service = { path = "indexer_service" }
indexer_service_protocol = { path = "indexer_service/protocol" }
indexer_service_rpc = { path = "indexer_service/rpc" }
wallet = { path = "wallet" }
test_program_methods = { path = "test_program_methods" }
bedrock_client = { path = "bedrock_client" }
@ -42,6 +48,7 @@ tokio = { version = "1.28.2", features = [
"sync",
"fs",
] }
tokio-util = "0.7.18"
risc0-zkvm = { version = "3.0.3", features = ['std'] }
risc0-build = "3.0.3"
anyhow = "1.0.98"
@ -52,6 +59,7 @@ serde = { version = "1.0.60", default-features = false, features = ["derive"] }
serde_json = "1.0.81"
actix = "0.13.0"
actix-cors = "0.6.1"
jsonrpsee = "0.26.0"
futures = "0.3"
actix-rt = "*"
lazy_static = "1.5.0"
@ -78,6 +86,7 @@ borsh = "1.5.7"
base58 = "0.2.0"
itertools = "0.14.0"
url = "2.5.4"
schemars = "1.2.0"
logos-blockchain-common-http-client = { git = "https://github.com/logos-blockchain/logos-blockchain.git" }
logos-blockchain-key-management-system-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git" }

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,17 @@
[package]
name = "indexer_service"
version = "0.1.0"
edition = "2024"
[dependencies]
indexer_service_protocol.workspace = true
indexer_service_rpc = { workspace = true, features = ["server"] }
clap = { workspace = true, features = ["derive"] }
anyhow.workspace = true
tokio.workspace = true
tokio-util.workspace = true
env_logger.workspace = true
log.workspace = true
jsonrpsee.workspace = true
async-trait = "0.1.89"

View File

@ -0,0 +1,64 @@
# Chef stage - uses pre-built cargo-chef image
FROM lukemathwalker/cargo-chef:latest-rust-1.91.1-slim-trixie AS chef
# Install build dependencies
RUN apt-get update && apt-get install -y \
pkg-config \
libssl-dev \
libclang-dev \
clang \
curl \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /indexer_service
# Planner stage - generates dependency recipe
FROM chef AS planner
COPY . .
RUN cargo chef prepare --bin indexer_service --recipe-path recipe.json
# Builder stage - builds dependencies and application
FROM chef AS builder
COPY --from=planner /indexer_service/recipe.json recipe.json
# Build dependencies only (this layer will be cached)
RUN cargo chef cook --bin indexer_service --release --recipe-path recipe.json
# Copy source code
COPY . .
# Build the actual application
RUN cargo build --release --bin indexer_service
# Strip debug symbols to reduce binary size
RUN strip /indexer_service/target/release/indexer_service
# Runtime stage - minimal image
FROM debian:trixie-slim
# Create non-root user for security
RUN useradd -m -u 1000 -s /bin/bash indexer_service_user
# Copy binary from builder
COPY --from=builder --chown=indexer_service_user:indexer_service_user /indexer_service/target/release/indexer_service /usr/local/bin/indexer_service
# Expose default port
EXPOSE 8779
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl http://localhost:8779 \
-H "Content-Type: application/json" \
-d "{ \
\"jsonrpc\": \"2.0\", \
\"method\": \"get_schema\", \
\"params\": {}, \
\"id\": 1 \
}" || exit 1
# Run the application
ENV RUST_LOG=info
USER indexer_service_user
WORKDIR /indexer_service
CMD ["indexer_service"]

View File

@ -0,0 +1,9 @@
services:
indexer_service:
image: lssa/indexer_service
build:
context: ..
dockerfile: indexer_service/Dockerfile
container_name: indexer_service
ports:
- "8779:8779"

View File

@ -0,0 +1,18 @@
[package]
name = "indexer_service_protocol"
version = "0.1.0"
edition = "2024"
[dependencies]
nssa_core = { workspace = true, optional = true, features = ["host"] }
nssa = { workspace = true, optional = true }
common = { workspace = true, optional = true }
serde = { workspace = true, features = ["derive"] }
schemars.workspace = true
base64.workspace = true
borsh = { workspace = true, optional = true }
[features]
# Enable conversion to/from NSSA core types
convert = ["dep:nssa_core", "dep:nssa", "dep:common", "dep:borsh"]

View File

@ -0,0 +1,648 @@
//! Conversions between indexer_service_protocol types and nssa/nssa_core types
use crate::*;
// ============================================================================
// Account-related conversions
// ============================================================================
impl From<nssa_core::account::AccountId> for AccountId {
fn from(value: nssa_core::account::AccountId) -> Self {
Self {
value: value.into_value(),
}
}
}
impl From<AccountId> for nssa_core::account::AccountId {
fn from(value: AccountId) -> Self {
let AccountId { value } = value;
nssa_core::account::AccountId::new(value)
}
}
impl From<nssa_core::account::Account> for Account {
fn from(value: nssa_core::account::Account) -> Self {
let nssa_core::account::Account {
program_owner,
balance,
data,
nonce,
} = value;
Self {
program_owner,
balance,
data: data.into(),
nonce,
}
}
}
impl TryFrom<Account> for nssa_core::account::Account {
type Error = nssa_core::account::data::DataTooBigError;
fn try_from(value: Account) -> Result<Self, Self::Error> {
let Account {
program_owner,
balance,
data,
nonce,
} = value;
Ok(nssa_core::account::Account {
program_owner,
balance,
data: data.try_into()?,
nonce,
})
}
}
impl From<nssa_core::account::Data> for Data {
fn from(value: nssa_core::account::Data) -> Self {
Self(value.into_inner())
}
}
impl TryFrom<Data> for nssa_core::account::Data {
type Error = nssa_core::account::data::DataTooBigError;
fn try_from(value: Data) -> Result<Self, Self::Error> {
nssa_core::account::Data::try_from(value.0)
}
}
// ============================================================================
// Commitment and Nullifier conversions
// ============================================================================
impl From<nssa_core::Commitment> for Commitment {
fn from(value: nssa_core::Commitment) -> Self {
Self(value.to_byte_array())
}
}
impl From<Commitment> for nssa_core::Commitment {
fn from(value: Commitment) -> Self {
nssa_core::Commitment::from_byte_array(value.0)
}
}
impl From<nssa_core::Nullifier> for Nullifier {
fn from(value: nssa_core::Nullifier) -> Self {
Self(value.to_byte_array())
}
}
impl From<Nullifier> for nssa_core::Nullifier {
fn from(value: Nullifier) -> Self {
nssa_core::Nullifier::from_byte_array(value.0)
}
}
impl From<nssa_core::CommitmentSetDigest> for CommitmentSetDigest {
fn from(value: nssa_core::CommitmentSetDigest) -> Self {
Self(value)
}
}
impl From<CommitmentSetDigest> for nssa_core::CommitmentSetDigest {
fn from(value: CommitmentSetDigest) -> Self {
value.0
}
}
// ============================================================================
// Encryption-related conversions
// ============================================================================
impl From<nssa_core::encryption::Ciphertext> for Ciphertext {
fn from(value: nssa_core::encryption::Ciphertext) -> Self {
Self(value.into_inner())
}
}
impl From<Ciphertext> for nssa_core::encryption::Ciphertext {
fn from(value: Ciphertext) -> Self {
nssa_core::encryption::Ciphertext::from_inner(value.0)
}
}
impl From<nssa_core::encryption::EphemeralPublicKey> for EphemeralPublicKey {
fn from(value: nssa_core::encryption::EphemeralPublicKey) -> Self {
Self(value.0)
}
}
impl From<EphemeralPublicKey> for nssa_core::encryption::EphemeralPublicKey {
fn from(value: EphemeralPublicKey) -> Self {
nssa_core::encryption::shared_key_derivation::Secp256k1Point(value.0)
}
}
// ============================================================================
// Signature and PublicKey conversions
// ============================================================================
impl From<nssa::Signature> for Signature {
fn from(value: nssa::Signature) -> Self {
let nssa::Signature { value } = value;
Self(value)
}
}
impl From<Signature> for nssa::Signature {
fn from(value: Signature) -> Self {
let Signature(sig_value) = value;
nssa::Signature { value: sig_value }
}
}
impl From<nssa::PublicKey> for PublicKey {
fn from(value: nssa::PublicKey) -> Self {
Self(*value.value())
}
}
impl TryFrom<PublicKey> for nssa::PublicKey {
type Error = nssa::error::NssaError;
fn try_from(value: PublicKey) -> Result<Self, Self::Error> {
nssa::PublicKey::try_new(value.0)
}
}
// ============================================================================
// Proof conversions
// ============================================================================
impl From<nssa::privacy_preserving_transaction::circuit::Proof> for Proof {
fn from(value: nssa::privacy_preserving_transaction::circuit::Proof) -> Self {
Self(value.into_inner())
}
}
impl From<Proof> for nssa::privacy_preserving_transaction::circuit::Proof {
fn from(value: Proof) -> Self {
nssa::privacy_preserving_transaction::circuit::Proof::from_inner(value.0)
}
}
// ============================================================================
// EncryptedAccountData conversions
// ============================================================================
impl From<nssa::privacy_preserving_transaction::message::EncryptedAccountData>
for EncryptedAccountData
{
fn from(value: nssa::privacy_preserving_transaction::message::EncryptedAccountData) -> Self {
Self {
ciphertext: value.ciphertext.into(),
epk: value.epk.into(),
view_tag: value.view_tag,
}
}
}
impl From<EncryptedAccountData>
for nssa::privacy_preserving_transaction::message::EncryptedAccountData
{
fn from(value: EncryptedAccountData) -> Self {
Self {
ciphertext: value.ciphertext.into(),
epk: value.epk.into(),
view_tag: value.view_tag,
}
}
}
// ============================================================================
// Transaction Message conversions
// ============================================================================
impl From<nssa::public_transaction::Message> for PublicMessage {
fn from(value: nssa::public_transaction::Message) -> Self {
let nssa::public_transaction::Message {
program_id,
account_ids,
nonces,
instruction_data,
} = value;
Self {
program_id,
account_ids: account_ids.into_iter().map(Into::into).collect(),
nonces,
instruction_data,
}
}
}
impl From<PublicMessage> for nssa::public_transaction::Message {
fn from(value: PublicMessage) -> Self {
let PublicMessage {
program_id,
account_ids,
nonces,
instruction_data,
} = value;
Self::new_preserialized(
program_id,
account_ids.into_iter().map(Into::into).collect(),
nonces,
instruction_data,
)
}
}
impl From<nssa::privacy_preserving_transaction::message::Message> for PrivacyPreservingMessage {
fn from(value: nssa::privacy_preserving_transaction::message::Message) -> Self {
let nssa::privacy_preserving_transaction::message::Message {
public_account_ids,
nonces,
public_post_states,
encrypted_private_post_states,
new_commitments,
new_nullifiers,
} = value;
Self {
public_account_ids: public_account_ids.into_iter().map(Into::into).collect(),
nonces,
public_post_states: public_post_states.into_iter().map(Into::into).collect(),
encrypted_private_post_states: encrypted_private_post_states
.into_iter()
.map(Into::into)
.collect(),
new_commitments: new_commitments.into_iter().map(Into::into).collect(),
new_nullifiers: new_nullifiers
.into_iter()
.map(|(n, d)| (n.into(), d.into()))
.collect(),
}
}
}
impl TryFrom<PrivacyPreservingMessage> for nssa::privacy_preserving_transaction::message::Message {
type Error = nssa_core::account::data::DataTooBigError;
fn try_from(value: PrivacyPreservingMessage) -> Result<Self, Self::Error> {
let PrivacyPreservingMessage {
public_account_ids,
nonces,
public_post_states,
encrypted_private_post_states,
new_commitments,
new_nullifiers,
} = value;
Ok(Self {
public_account_ids: public_account_ids.into_iter().map(Into::into).collect(),
nonces,
public_post_states: public_post_states
.into_iter()
.map(TryInto::try_into)
.collect::<Result<Vec<_>, _>>()?,
encrypted_private_post_states: encrypted_private_post_states
.into_iter()
.map(Into::into)
.collect(),
new_commitments: new_commitments.into_iter().map(Into::into).collect(),
new_nullifiers: new_nullifiers
.into_iter()
.map(|(n, d)| (n.into(), d.into()))
.collect(),
})
}
}
impl From<nssa::program_deployment_transaction::Message> for ProgramDeploymentMessage {
fn from(value: nssa::program_deployment_transaction::Message) -> Self {
Self {
bytecode: value.into_bytecode(),
}
}
}
impl From<ProgramDeploymentMessage> for nssa::program_deployment_transaction::Message {
fn from(value: ProgramDeploymentMessage) -> Self {
let ProgramDeploymentMessage { bytecode } = value;
Self::new(bytecode)
}
}
// ============================================================================
// WitnessSet conversions
// ============================================================================
impl TryFrom<nssa::public_transaction::WitnessSet> for WitnessSet {
type Error = ();
fn try_from(_value: nssa::public_transaction::WitnessSet) -> Result<Self, Self::Error> {
// Public transaction witness sets don't have proofs, so we can't convert them directly
Err(())
}
}
impl From<nssa::privacy_preserving_transaction::witness_set::WitnessSet> for WitnessSet {
fn from(value: nssa::privacy_preserving_transaction::witness_set::WitnessSet) -> Self {
let (sigs_and_pks, proof) = value.into_raw_parts();
Self {
signatures_and_public_keys: sigs_and_pks
.into_iter()
.map(|(sig, pk)| (sig.into(), pk.into()))
.collect(),
proof: proof.into(),
}
}
}
impl TryFrom<WitnessSet> for nssa::privacy_preserving_transaction::witness_set::WitnessSet {
type Error = nssa::error::NssaError;
fn try_from(value: WitnessSet) -> Result<Self, Self::Error> {
let WitnessSet {
signatures_and_public_keys,
proof,
} = value;
let signatures_and_public_keys = signatures_and_public_keys
.into_iter()
.map(|(sig, pk)| Ok((sig.into(), pk.try_into()?)))
.collect::<Result<Vec<_>, Self::Error>>()?;
Ok(Self::from_raw_parts(
signatures_and_public_keys,
proof.into(),
))
}
}
// ============================================================================
// Transaction conversions
// ============================================================================
impl From<nssa::PublicTransaction> for PublicTransaction {
fn from(value: nssa::PublicTransaction) -> Self {
Self {
message: value.message().clone().into(),
witness_set: WitnessSet {
signatures_and_public_keys: value
.witness_set()
.signatures_and_public_keys()
.iter()
.map(|(sig, pk)| (sig.clone().into(), pk.clone().into()))
.collect(),
proof: Proof(vec![]), // Public transactions don't have proofs
},
}
}
}
impl TryFrom<PublicTransaction> for nssa::PublicTransaction {
type Error = nssa::error::NssaError;
fn try_from(value: PublicTransaction) -> Result<Self, Self::Error> {
let PublicTransaction {
message,
witness_set,
} = value;
let WitnessSet {
signatures_and_public_keys,
proof: _,
} = witness_set;
Ok(Self::new(
message.into(),
nssa::public_transaction::WitnessSet::from_raw_parts(
signatures_and_public_keys
.into_iter()
.map(|(sig, pk)| Ok((sig.into(), pk.try_into()?)))
.collect::<Result<Vec<_>, Self::Error>>()?,
),
))
}
}
impl From<nssa::PrivacyPreservingTransaction> for PrivacyPreservingTransaction {
fn from(value: nssa::PrivacyPreservingTransaction) -> Self {
Self {
message: value.message().clone().into(),
witness_set: value.witness_set().clone().into(),
}
}
}
impl TryFrom<PrivacyPreservingTransaction> for nssa::PrivacyPreservingTransaction {
type Error = nssa::error::NssaError;
fn try_from(value: PrivacyPreservingTransaction) -> Result<Self, Self::Error> {
let PrivacyPreservingTransaction {
message,
witness_set,
} = value;
Ok(Self::new(
message.try_into().map_err(|_| {
nssa::error::NssaError::InvalidInput("Data too big error".to_string())
})?,
witness_set.try_into()?,
))
}
}
impl From<nssa::ProgramDeploymentTransaction> for ProgramDeploymentTransaction {
fn from(value: nssa::ProgramDeploymentTransaction) -> Self {
Self {
message: value.into_message().into(),
}
}
}
impl From<ProgramDeploymentTransaction> for nssa::ProgramDeploymentTransaction {
fn from(value: ProgramDeploymentTransaction) -> Self {
let ProgramDeploymentTransaction { message } = value;
Self::new(message.into())
}
}
impl From<common::transaction::NSSATransaction> for Transaction {
fn from(value: common::transaction::NSSATransaction) -> Self {
match value {
common::transaction::NSSATransaction::Public(tx) => Transaction::Public(tx.into()),
common::transaction::NSSATransaction::PrivacyPreserving(tx) => {
Transaction::PrivacyPreserving(tx.into())
}
common::transaction::NSSATransaction::ProgramDeployment(tx) => {
Transaction::ProgramDeployment(tx.into())
}
}
}
}
impl TryFrom<Transaction> for common::transaction::NSSATransaction {
type Error = nssa::error::NssaError;
fn try_from(value: Transaction) -> Result<Self, Self::Error> {
match value {
Transaction::Public(tx) => {
Ok(common::transaction::NSSATransaction::Public(tx.try_into()?))
}
Transaction::PrivacyPreserving(tx) => Ok(
common::transaction::NSSATransaction::PrivacyPreserving(tx.try_into()?),
),
Transaction::ProgramDeployment(tx) => Ok(
common::transaction::NSSATransaction::ProgramDeployment(tx.into()),
),
}
}
}
// ============================================================================
// Block conversions
// ============================================================================
impl From<common::block::BlockHeader> for BlockHeader {
fn from(value: common::block::BlockHeader) -> Self {
let common::block::BlockHeader {
block_id,
prev_block_hash,
hash,
timestamp,
signature,
} = value;
Self {
block_id,
prev_block_hash: Hash(prev_block_hash),
hash: Hash(hash),
timestamp,
signature: signature.into(),
}
}
}
impl TryFrom<BlockHeader> for common::block::BlockHeader {
type Error = nssa::error::NssaError;
fn try_from(value: BlockHeader) -> Result<Self, Self::Error> {
let BlockHeader {
block_id,
prev_block_hash,
hash,
timestamp,
signature,
} = value;
Ok(Self {
block_id,
prev_block_hash: prev_block_hash.0,
hash: hash.0,
timestamp,
signature: signature.into(),
})
}
}
impl TryFrom<common::block::BlockBody> for BlockBody {
type Error = std::io::Error;
fn try_from(value: common::block::BlockBody) -> Result<Self, Self::Error> {
// Note: EncodedTransaction doesn't have a direct conversion to NSSATransaction
// This conversion will decode and re-encode the transactions
use borsh::BorshDeserialize as _;
let common::block::BlockBody { transactions } = value;
let transactions = transactions
.into_iter()
.map(|encoded_tx| match encoded_tx.tx_kind {
common::transaction::TxKind::Public => {
nssa::PublicTransaction::try_from_slice(&encoded_tx.encoded_transaction_data)
.map(|tx| Transaction::Public(tx.into()))
}
common::transaction::TxKind::PrivacyPreserving => {
nssa::PrivacyPreservingTransaction::try_from_slice(
&encoded_tx.encoded_transaction_data,
)
.map(|tx| Transaction::PrivacyPreserving(tx.into()))
}
common::transaction::TxKind::ProgramDeployment => {
nssa::ProgramDeploymentTransaction::try_from_slice(
&encoded_tx.encoded_transaction_data,
)
.map(|tx| Transaction::ProgramDeployment(tx.into()))
}
})
.collect::<Result<Vec<_>, _>>()?;
Ok(Self { transactions })
}
}
impl TryFrom<BlockBody> for common::block::BlockBody {
type Error = nssa::error::NssaError;
fn try_from(value: BlockBody) -> Result<Self, Self::Error> {
let BlockBody { transactions } = value;
let transactions = transactions
.into_iter()
.map(|tx| {
let nssa_tx: common::transaction::NSSATransaction = tx.try_into()?;
Ok::<_, nssa::error::NssaError>(nssa_tx.into())
})
.collect::<Result<Vec<_>, _>>()?;
Ok(Self { transactions })
}
}
impl TryFrom<common::block::Block> for Block {
type Error = std::io::Error;
fn try_from(value: common::block::Block) -> Result<Self, Self::Error> {
let common::block::Block {
header,
body,
bedrock_status,
} = value;
Ok(Self {
header: header.into(),
body: body.try_into()?,
bedrock_status: bedrock_status.into(),
})
}
}
impl TryFrom<Block> for common::block::Block {
type Error = nssa::error::NssaError;
fn try_from(value: Block) -> Result<Self, Self::Error> {
let Block {
header,
body,
bedrock_status,
} = value;
Ok(Self {
header: header.try_into()?,
body: body.try_into()?,
bedrock_status: bedrock_status.into(),
})
}
}
impl From<common::block::BedrockStatus> for BedrockStatus {
fn from(value: common::block::BedrockStatus) -> Self {
match value {
common::block::BedrockStatus::Pending => Self::Pending,
common::block::BedrockStatus::Safe => Self::Safe,
common::block::BedrockStatus::Finalized => Self::Finalized,
}
}
}
impl From<BedrockStatus> for common::block::BedrockStatus {
fn from(value: BedrockStatus) -> Self {
match value {
BedrockStatus::Pending => Self::Pending,
BedrockStatus::Safe => Self::Safe,
BedrockStatus::Finalized => Self::Finalized,
}
}
}

View File

@ -0,0 +1,230 @@
//! This crate defines the protocol types used by the indexer service.
//!
//! Currently it mostly mimics types from `nssa_core`, but it's important to have a separate crate
//! to define a stable interface for the indexer service RPCs which evolves in its own way.
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[cfg(feature = "convert")]
mod convert;
pub type Nonce = u128;
pub type ProgramId = [u32; 8];
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
pub struct AccountId {
#[serde(with = "base64::arr")]
#[schemars(with = "String", description = "base64-encoded account ID")]
pub value: [u8; 32],
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
pub struct Account {
pub program_owner: ProgramId,
pub balance: u128,
pub data: Data,
pub nonce: Nonce,
}
pub type BlockId = u64;
pub type TimeStamp = u64;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
pub struct Block {
pub header: BlockHeader,
pub body: BlockBody,
pub bedrock_status: BedrockStatus,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
pub struct BlockHeader {
pub block_id: BlockId,
pub prev_block_hash: Hash,
pub hash: Hash,
pub timestamp: TimeStamp,
pub signature: Signature,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
pub struct Signature(
#[serde(with = "base64::arr")]
#[schemars(with = "String", description = "base64-encoded signature")]
pub [u8; 64],
);
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
pub struct BlockBody {
pub transactions: Vec<Transaction>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
pub enum Transaction {
Public(PublicTransaction),
PrivacyPreserving(PrivacyPreservingTransaction),
ProgramDeployment(ProgramDeploymentTransaction),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
pub struct PublicTransaction {
pub message: PublicMessage,
pub witness_set: WitnessSet,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
pub struct PrivacyPreservingTransaction {
pub message: PrivacyPreservingMessage,
pub witness_set: WitnessSet,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
pub struct PublicMessage {
pub program_id: ProgramId,
pub account_ids: Vec<AccountId>,
pub nonces: Vec<Nonce>,
pub instruction_data: InstructionData,
}
pub type InstructionData = Vec<u32>;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
pub struct PrivacyPreservingMessage {
pub public_account_ids: Vec<AccountId>,
pub nonces: Vec<Nonce>,
pub public_post_states: Vec<Account>,
pub encrypted_private_post_states: Vec<EncryptedAccountData>,
pub new_commitments: Vec<Commitment>,
pub new_nullifiers: Vec<(Nullifier, CommitmentSetDigest)>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
pub struct WitnessSet {
pub signatures_and_public_keys: Vec<(Signature, PublicKey)>,
pub proof: Proof,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
pub struct Proof(
#[serde(with = "base64")]
#[schemars(with = "String", description = "base64-encoded proof")]
pub Vec<u8>,
);
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
pub struct EncryptedAccountData {
pub ciphertext: Ciphertext,
pub epk: EphemeralPublicKey,
pub view_tag: ViewTag,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
pub struct ProgramDeploymentTransaction {
pub message: ProgramDeploymentMessage,
}
pub type ViewTag = u8;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
pub struct Ciphertext(
#[serde(with = "base64")]
#[schemars(with = "String", description = "base64-encoded ciphertext")]
pub Vec<u8>,
);
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
pub struct PublicKey(
#[serde(with = "base64::arr")]
#[schemars(with = "String", description = "base64-encoded public key")]
pub [u8; 32],
);
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
pub struct EphemeralPublicKey(
#[serde(with = "base64")]
#[schemars(with = "String", description = "base64-encoded ephemeral public key")]
pub Vec<u8>,
);
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
pub struct Commitment(
#[serde(with = "base64::arr")]
#[schemars(with = "String", description = "base64-encoded commitment")]
pub [u8; 32],
);
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
pub struct Nullifier(
#[serde(with = "base64::arr")]
#[schemars(with = "String", description = "base64-encoded nullifier")]
pub [u8; 32],
);
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
pub struct CommitmentSetDigest(
#[serde(with = "base64::arr")]
#[schemars(with = "String", description = "base64-encoded commitment set digest")]
pub [u8; 32],
);
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
pub struct ProgramDeploymentMessage {
#[serde(with = "base64")]
#[schemars(with = "String", description = "base64-encoded program bytecode")]
pub bytecode: Vec<u8>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
pub struct Data(
#[serde(with = "base64")]
#[schemars(with = "String", description = "base64-encoded account data")]
pub Vec<u8>,
);
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
pub struct Hash(
#[serde(with = "base64::arr")]
#[schemars(with = "String", description = "base64-encoded hash")]
pub [u8; 32],
);
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
pub enum BedrockStatus {
Pending,
Safe,
Finalized,
}
mod base64 {
use base64::prelude::{BASE64_STANDARD, Engine as _};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub mod arr {
use super::*;
pub fn serialize<S: Serializer>(v: &[u8], s: S) -> Result<S::Ok, S::Error> {
super::serialize(v, s)
}
pub fn deserialize<'de, const N: usize, D: Deserializer<'de>>(
d: D,
) -> Result<[u8; N], D::Error> {
let vec = super::deserialize(d)?;
vec.try_into().map_err(|_| {
serde::de::Error::custom(format!("Invalid length, expected {N} bytes"))
})
}
}
pub fn serialize<S: Serializer>(v: &[u8], s: S) -> Result<S::Ok, S::Error> {
let base64 = BASE64_STANDARD.encode(v);
String::serialize(&base64, s)
}
pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<u8>, D::Error> {
let base64 = String::deserialize(d)?;
BASE64_STANDARD
.decode(base64.as_bytes())
.map_err(serde::de::Error::custom)
}
}

View File

@ -0,0 +1,15 @@
[package]
name = "indexer_service_rpc"
version = "0.1.0"
edition = "2024"
[dependencies]
indexer_service_protocol = { workspace = true }
jsonrpsee = { workspace = true, features = ["macros"] }
serde_json.workspace = true
schemars.workspace = true
[features]
client = ["jsonrpsee/client"]
server = ["jsonrpsee/server"]

View File

@ -0,0 +1,40 @@
use indexer_service_protocol::{Account, AccountId, Block, BlockId, Hash, Transaction};
use jsonrpsee::{core::SubscriptionResult, proc_macros::rpc, types::ErrorObjectOwned};
#[cfg(all(not(feature = "server"), not(feature = "client")))]
compile_error!("At least one of `server` or `client` features must be enabled.");
#[cfg_attr(feature = "server", rpc(server))]
#[cfg_attr(feature = "client", rpc(client))]
pub trait Rpc {
#[method(name = "get_schema")]
fn get_schema(&self) -> Result<serde_json::Value, ErrorObjectOwned> {
// TODO: Canonical solution would be to provide `describe` method returning OpenRPC spec,
// But for now it's painful to implement, although can be done if really needed.
// Currently we can wait until we can auto-generated it: https://github.com/paritytech/jsonrpsee/issues/737
// and just return JSON schema.
// Block schema contains all other types used in the protocol, so it's sufficient to return
// its schema.
let block_schema = schemars::schema_for!(Block);
Ok(serde_json::to_value(block_schema).expect("Schema serialization should not fail"))
}
#[subscription(name = "subscribeToBlocks", item = Vec<Block>)]
async fn subscribe_to_blocks(&self, from: BlockId) -> SubscriptionResult;
#[method(name = "getBlockById")]
async fn get_block_by_id(&self, block_id: BlockId) -> Result<Block, ErrorObjectOwned>;
#[method(name = "getBlockByHash")]
async fn get_block_by_hash(&self, block_hash: Hash) -> Result<Block, ErrorObjectOwned>;
#[method(name = "getLastBlockId")]
async fn get_last_block_id(&self) -> Result<BlockId, ErrorObjectOwned>;
#[method(name = "getAccount")]
async fn get_account(&self, account_id: AccountId) -> Result<Account, ErrorObjectOwned>;
#[method(name = "getTransaction")]
async fn get_transaction(&self, tx_hash: Hash) -> Result<Transaction, ErrorObjectOwned>;
}

View File

@ -0,0 +1 @@
pub mod service;

View File

@ -0,0 +1,72 @@
use std::net::SocketAddr;
use anyhow::{Context as _, Result};
use clap::Parser;
use indexer_service_rpc::RpcServer as _;
use jsonrpsee::server::Server;
use log::{error, info};
use tokio_util::sync::CancellationToken;
#[derive(Debug, Parser)]
#[clap(version)]
struct Args {
#[clap(short, long, default_value = "8779")]
port: u16,
}
#[tokio::main]
async fn main() -> Result<()> {
env_logger::init();
let args = Args::parse();
let cancellation_token = listen_for_shutdown_signal();
let handle = run_server(args.port).await?;
let handle_clone = handle.clone();
tokio::select! {
_ = cancellation_token.cancelled() => {
info!("Shutting down server...");
}
_ = handle_clone.stopped() => {
error!("Server stopped unexpectedly");
}
}
info!("Server shutdown complete");
Ok(())
}
async fn run_server(port: u16) -> Result<jsonrpsee::server::ServerHandle> {
let server = Server::builder()
.build(SocketAddr::from(([0, 0, 0, 0], port)))
.await
.context("Failed to build RPC server")?;
let addr = server
.local_addr()
.context("Failed to get local address of RPC server")?;
info!("Starting Indexer Service RPC server on {addr}");
let handle = server.start(indexer_service::service::IndexerService.into_rpc());
Ok(handle)
}
fn listen_for_shutdown_signal() -> CancellationToken {
let cancellation_token = CancellationToken::new();
let cancellation_token_clone = cancellation_token.clone();
tokio::spawn(async move {
if let Err(err) = tokio::signal::ctrl_c().await {
error!("Failed to listen for Ctrl-C signal: {err}");
return;
}
info!("Received Ctrl-C signal");
cancellation_token_clone.cancel();
});
cancellation_token
}

View File

@ -0,0 +1,36 @@
use indexer_service_protocol::{Account, AccountId, Block, BlockId, Hash, Transaction};
use jsonrpsee::{core::SubscriptionResult, types::ErrorObjectOwned};
pub struct IndexerService;
// `async_trait` is required by `jsonrpsee`
#[async_trait::async_trait]
impl indexer_service_rpc::RpcServer for IndexerService {
async fn subscribe_to_blocks(
&self,
_subscription_sink: jsonrpsee::PendingSubscriptionSink,
_from: BlockId,
) -> SubscriptionResult {
todo!()
}
async fn get_block_by_id(&self, _block_id: BlockId) -> Result<Block, ErrorObjectOwned> {
todo!()
}
async fn get_block_by_hash(&self, _block_hash: Hash) -> Result<Block, ErrorObjectOwned> {
todo!()
}
async fn get_last_block_id(&self) -> Result<BlockId, ErrorObjectOwned> {
todo!()
}
async fn get_account(&self, _account_id: AccountId) -> Result<Account, ErrorObjectOwned> {
todo!()
}
async fn get_transaction(&self, _tx_hash: Hash) -> Result<Transaction, ErrorObjectOwned> {
todo!()
}
}

View File

@ -68,6 +68,10 @@ impl AccountId {
pub fn value(&self) -> &[u8; 32] {
&self.value
}
pub fn into_value(self) -> [u8; 32] {
self.value
}
}
impl AsRef<[u8]> for AccountId {

View File

@ -69,6 +69,11 @@ impl Commitment {
self.0
}
#[cfg(feature = "host")]
pub fn from_byte_array(bytes: [u8; 32]) -> Self {
Self(bytes)
}
#[cfg(feature = "host")]
pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result<Self, NssaCoreError> {
let mut bytes = [0u8; 32];
@ -89,6 +94,11 @@ impl Nullifier {
self.0
}
#[cfg(feature = "host")]
pub fn from_byte_array(bytes: [u8; 32]) -> Self {
Self(bytes)
}
pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result<Self, NssaCoreError> {
let mut bytes = [0u8; 32];
cursor.read_exact(&mut bytes)?;
@ -106,6 +116,16 @@ impl Ciphertext {
bytes
}
#[cfg(feature = "host")]
pub fn into_inner(self) -> Vec<u8> {
self.0
}
#[cfg(feature = "host")]
pub fn from_inner(inner: Vec<u8>) -> Self {
Self(inner)
}
#[cfg(feature = "host")]
pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result<Self, NssaCoreError> {
let mut u32_bytes = [0; 4];

View File

@ -20,6 +20,16 @@ use crate::{
#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
pub struct Proof(pub(crate) Vec<u8>);
impl Proof {
pub fn into_inner(self) -> Vec<u8> {
self.0
}
pub fn from_inner(inner: Vec<u8>) -> Self {
Self(inner)
}
}
#[derive(Clone)]
pub struct ProgramWithDependencies {
pub program: Program,

View File

@ -45,12 +45,12 @@ impl EncryptedAccountData {
#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
pub struct Message {
pub(crate) public_account_ids: Vec<AccountId>,
pub(crate) nonces: Vec<Nonce>,
pub(crate) public_post_states: Vec<Account>,
pub public_account_ids: Vec<AccountId>,
pub nonces: Vec<Nonce>,
pub public_post_states: Vec<Account>,
pub encrypted_private_post_states: Vec<EncryptedAccountData>,
pub new_commitments: Vec<Commitment>,
pub(crate) new_nullifiers: Vec<(Nullifier, CommitmentSetDigest)>,
pub new_nullifiers: Vec<(Nullifier, CommitmentSetDigest)>,
}
impl Message {

View File

@ -16,7 +16,7 @@ use crate::{
#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
pub struct PrivacyPreservingTransaction {
pub message: Message,
witness_set: WitnessSet,
pub witness_set: WitnessSet,
}
impl PrivacyPreservingTransaction {

View File

@ -46,4 +46,18 @@ impl WitnessSet {
pub fn proof(&self) -> &Proof {
&self.proof
}
pub fn into_raw_parts(self) -> (Vec<(Signature, PublicKey)>, Proof) {
(self.signatures_and_public_keys, self.proof)
}
pub fn from_raw_parts(
signatures_and_public_keys: Vec<(Signature, PublicKey)>,
proof: Proof,
) -> Self {
Self {
signatures_and_public_keys,
proof,
}
}
}

View File

@ -9,4 +9,8 @@ impl Message {
pub fn new(bytecode: Vec<u8>) -> Self {
Self { bytecode }
}
pub fn into_bytecode(self) -> Vec<u8> {
self.bytecode
}
}

View File

@ -14,6 +14,10 @@ impl ProgramDeploymentTransaction {
Self { message }
}
pub fn into_message(self) -> Message {
self.message
}
pub(crate) fn validate_and_produce_public_state_diff(
&self,
state: &V02State,

View File

@ -9,10 +9,10 @@ use crate::{AccountId, error::NssaError, program::Program};
#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
pub struct Message {
pub(crate) program_id: ProgramId,
pub(crate) account_ids: Vec<AccountId>,
pub(crate) nonces: Vec<Nonce>,
pub(crate) instruction_data: InstructionData,
pub program_id: ProgramId,
pub account_ids: Vec<AccountId>,
pub nonces: Vec<Nonce>,
pub instruction_data: InstructionData,
}
impl Message {

View File

@ -37,6 +37,16 @@ impl WitnessSet {
pub fn signatures_and_public_keys(&self) -> &[(Signature, PublicKey)] {
&self.signatures_and_public_keys
}
pub fn into_raw_parts(self) -> Vec<(Signature, PublicKey)> {
self.signatures_and_public_keys
}
pub fn from_raw_parts(signatures_and_public_keys: Vec<(Signature, PublicKey)>) -> Self {
Self {
signatures_and_public_keys,
}
}
}
#[cfg(test)]

View File

@ -8,7 +8,7 @@ use rand::{RngCore, rngs::OsRng};
#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
pub struct Signature {
value: [u8; 64],
pub value: [u8; 64],
}
impl Signature {