mirror of
https://github.com/logos-blockchain/logos-execution-zone.git
synced 2026-03-24 19:23:22 +00:00
Merge branch 'Pravdyvy/indexer-db-batching' into Pravdyvy/db-structural-updates
This commit is contained in:
commit
901d0701a8
@ -26,11 +26,20 @@ Thumbs.db
|
||||
ci_scripts/
|
||||
|
||||
# Documentation
|
||||
docs/
|
||||
*.md
|
||||
!README.md
|
||||
|
||||
# Configs (copy selectively if needed)
|
||||
# Non-build project files
|
||||
completions/
|
||||
configs/
|
||||
|
||||
# License
|
||||
Justfile
|
||||
clippy.toml
|
||||
rustfmt.toml
|
||||
flake.nix
|
||||
flake.lock
|
||||
LICENSE
|
||||
|
||||
# Docker compose files (not needed inside build)
|
||||
docker-compose*.yml
|
||||
**/docker-compose*.yml
|
||||
|
||||
@ -2,6 +2,9 @@ name: Publish Docker Images
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
@ -9,12 +12,12 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- name: sequencer_runner
|
||||
dockerfile: ./sequencer_runner/Dockerfile
|
||||
- name: sequencer_service
|
||||
dockerfile: ./sequencer/service/Dockerfile
|
||||
build_args: |
|
||||
STANDALONE=false
|
||||
- name: sequencer_runner-standalone
|
||||
dockerfile: ./sequencer_runner/Dockerfile
|
||||
- name: sequencer_service-standalone
|
||||
dockerfile: ./sequencer/service/Dockerfile
|
||||
build_args: |
|
||||
STANDALONE=true
|
||||
- name: indexer_service
|
||||
@ -42,6 +45,7 @@ jobs:
|
||||
with:
|
||||
images: ${{ secrets.DOCKER_REGISTRY }}/${{ github.repository }}/${{ matrix.name }}
|
||||
tags: |
|
||||
type=ref,event=tag
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -6,7 +6,7 @@ data/
|
||||
.idea/
|
||||
.vscode/
|
||||
rocksdb
|
||||
sequencer_runner/data/
|
||||
sequencer/service/data/
|
||||
storage.json
|
||||
result
|
||||
wallet-ffi/wallet_ffi.h
|
||||
|
||||
538
Cargo.lock
generated
538
Cargo.lock
generated
@ -2,229 +2,6 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "actix"
|
||||
version = "0.13.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de7fa236829ba0841304542f7614c42b80fca007455315c45c785ccfa873a85b"
|
||||
dependencies = [
|
||||
"actix-macros",
|
||||
"actix-rt",
|
||||
"actix_derive",
|
||||
"bitflags 2.11.0",
|
||||
"bytes",
|
||||
"crossbeam-channel",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
"log",
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"smallvec",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-codec"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a"
|
||||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-cors"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "daa239b93927be1ff123eebada5a3ff23e89f0124ccb8609234e5103d5a5ae6d"
|
||||
dependencies = [
|
||||
"actix-utils",
|
||||
"actix-web",
|
||||
"derive_more",
|
||||
"futures-util",
|
||||
"log",
|
||||
"once_cell",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-http"
|
||||
version = "3.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f860ee6746d0c5b682147b2f7f8ef036d4f92fe518251a3a35ffa3650eafdf0e"
|
||||
dependencies = [
|
||||
"actix-codec",
|
||||
"actix-rt",
|
||||
"actix-service",
|
||||
"actix-utils",
|
||||
"bitflags 2.11.0",
|
||||
"bytes",
|
||||
"bytestring",
|
||||
"derive_more",
|
||||
"encoding_rs",
|
||||
"foldhash",
|
||||
"futures-core",
|
||||
"http 0.2.12",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
"itoa",
|
||||
"language-tags",
|
||||
"mime",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"smallvec",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-macros"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-router"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14f8c75c51892f18d9c46150c5ac7beb81c95f78c8b83a634d49f4ca32551fe7"
|
||||
dependencies = [
|
||||
"bytestring",
|
||||
"cfg-if",
|
||||
"http 0.2.12",
|
||||
"regex-lite",
|
||||
"serde",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-rt"
|
||||
version = "2.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92589714878ca59a7626ea19734f0e07a6a875197eec751bb5d3f99e64998c63"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-server"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a65064ea4a457eaf07f2fba30b4c695bf43b721790e9530d26cb6f9019ff7502"
|
||||
dependencies = [
|
||||
"actix-rt",
|
||||
"actix-service",
|
||||
"actix-utils",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"mio",
|
||||
"socket2 0.5.10",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-service"
|
||||
version = "2.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e46f36bf0e5af44bdc4bdb36fbbd421aa98c79a9bce724e1edeb3894e10dc7f"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-utils"
|
||||
version = "3.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8"
|
||||
dependencies = [
|
||||
"local-waker",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-web"
|
||||
version = "4.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff87453bc3b56e9b2b23c1cc0b1be8797184accf51d2abe0f8a33ec275d316bf"
|
||||
dependencies = [
|
||||
"actix-codec",
|
||||
"actix-http",
|
||||
"actix-macros",
|
||||
"actix-router",
|
||||
"actix-rt",
|
||||
"actix-server",
|
||||
"actix-service",
|
||||
"actix-utils",
|
||||
"actix-web-codegen",
|
||||
"bytes",
|
||||
"bytestring",
|
||||
"cfg-if",
|
||||
"derive_more",
|
||||
"encoding_rs",
|
||||
"foldhash",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"impl-more",
|
||||
"itoa",
|
||||
"language-tags",
|
||||
"log",
|
||||
"mime",
|
||||
"once_cell",
|
||||
"pin-project-lite",
|
||||
"regex-lite",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"smallvec",
|
||||
"socket2 0.6.3",
|
||||
"time",
|
||||
"tracing",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-web-codegen"
|
||||
version = "4.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f591380e2e68490b5dfaf1dd1aa0ebe78d84ba7067078512b4ea6e4492d622b8"
|
||||
dependencies = [
|
||||
"actix-router",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix_derive"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6ac1e58cded18cb28ddc17143c4dea5345b3ad575e14f32f66e4054a56eb271"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "addchain"
|
||||
version = "0.2.1"
|
||||
@ -852,9 +629,9 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
||||
|
||||
[[package]]
|
||||
name = "astral-tokio-tar"
|
||||
version = "0.5.6"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec179a06c1769b1e42e1e2cbe74c7dcdb3d6383c838454d063eaac5bbb7ebbe5"
|
||||
checksum = "3c23f3af104b40a3430ccb90ed5f7bd877a8dc5c26fc92fde51a22b40890dcf9"
|
||||
dependencies = [
|
||||
"filetime",
|
||||
"futures-core",
|
||||
@ -1011,7 +788,7 @@ dependencies = [
|
||||
"axum-core 0.4.5",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http 1.4.0",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
@ -1045,7 +822,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"form_urlencoded",
|
||||
"futures-util",
|
||||
"http 1.4.0",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
@ -1080,7 +857,7 @@ dependencies = [
|
||||
"async-trait",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http 1.4.0",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"mime",
|
||||
@ -1099,7 +876,7 @@ checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"http 1.4.0",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"mime",
|
||||
@ -1313,7 +1090,7 @@ dependencies = [
|
||||
"futures-util",
|
||||
"hex",
|
||||
"home",
|
||||
"http 1.4.0",
|
||||
"http",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper-named-pipe",
|
||||
@ -1466,15 +1243,6 @@ dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytestring"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "113b4343b5f6617e7ad401ced8de3cc8b012e73a594347c307b90db3e9271289"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bzip2-sys"
|
||||
version = "0.1.13+1.0.8"
|
||||
@ -1732,20 +1500,15 @@ dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
"borsh",
|
||||
"bytesize",
|
||||
"hex",
|
||||
"log",
|
||||
"logos-blockchain-common-http-client",
|
||||
"nssa",
|
||||
"nssa_core",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_with",
|
||||
"sha2",
|
||||
"thiserror 2.0.18",
|
||||
"tokio-retry",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1877,15 +1640,6 @@ dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.11.0"
|
||||
@ -1992,15 +1746,6 @@ version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.6"
|
||||
@ -2194,7 +1939,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ab67060fc6b8ef687992d439ca0fa36e7ed17e9a0b16b25b601e8757df720de"
|
||||
dependencies = [
|
||||
"data-encoding",
|
||||
"syn 2.0.117",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2297,7 +2042,6 @@ version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb"
|
||||
dependencies = [
|
||||
"convert_case 0.10.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version",
|
||||
@ -3099,7 +2843,7 @@ dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"gloo-utils",
|
||||
"http 1.4.0",
|
||||
"http",
|
||||
"js-sys",
|
||||
"pin-project",
|
||||
"serde",
|
||||
@ -3163,7 +2907,7 @@ dependencies = [
|
||||
"fnv",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"http 1.4.0",
|
||||
"http",
|
||||
"indexmap 2.13.0",
|
||||
"slab",
|
||||
"tokio",
|
||||
@ -3318,17 +3062,6 @@ dependencies = [
|
||||
"utf8-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "1.4.0"
|
||||
@ -3346,7 +3079,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http 1.4.0",
|
||||
"http",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3357,7 +3090,7 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"http 1.4.0",
|
||||
"http",
|
||||
"http-body",
|
||||
"pin-project-lite",
|
||||
]
|
||||
@ -3432,7 +3165,7 @@ dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"h2",
|
||||
"http 1.4.0",
|
||||
"http",
|
||||
"http-body",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
@ -3465,7 +3198,7 @@ version = "0.27.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58"
|
||||
dependencies = [
|
||||
"http 1.4.0",
|
||||
"http",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"log",
|
||||
@ -3516,14 +3249,14 @@ dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"http 1.4.0",
|
||||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"ipnet",
|
||||
"libc",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"socket2 0.6.3",
|
||||
"socket2",
|
||||
"system-configuration",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
@ -3684,12 +3417,6 @@ dependencies = [
|
||||
"icu_properties",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "impl-more"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8a5a9a0ff0086c7a148acb942baaabeadf9504d10400b5a05645853729b9cd2"
|
||||
|
||||
[[package]]
|
||||
name = "include_bytes_aligned"
|
||||
version = "0.1.4"
|
||||
@ -3725,7 +3452,6 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
"async-trait",
|
||||
"clap",
|
||||
"env_logger",
|
||||
"futures",
|
||||
@ -3825,8 +3551,6 @@ name = "integration_tests"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
"borsh",
|
||||
"bytesize",
|
||||
"common",
|
||||
"env_logger",
|
||||
@ -3839,7 +3563,8 @@ dependencies = [
|
||||
"nssa",
|
||||
"nssa_core",
|
||||
"sequencer_core",
|
||||
"sequencer_runner",
|
||||
"sequencer_service",
|
||||
"sequencer_service_rpc",
|
||||
"serde_json",
|
||||
"tempfile",
|
||||
"testcontainers",
|
||||
@ -4048,7 +3773,7 @@ dependencies = [
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"gloo-net",
|
||||
"http 1.4.0",
|
||||
"http",
|
||||
"jsonrpsee-core",
|
||||
"pin-project",
|
||||
"rustls",
|
||||
@ -4073,7 +3798,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"futures-timer",
|
||||
"futures-util",
|
||||
"http 1.4.0",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"jsonrpsee-types",
|
||||
@ -4134,7 +3859,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c51b7c290bb68ce3af2d029648148403863b982f138484a73f02a9dd52dbd7f"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"http 1.4.0",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
@ -4160,7 +3885,7 @@ version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc88ff4688e43cc3fa9883a8a95c6fa27aa2e76c96e610b737b6554d650d7fd5"
|
||||
dependencies = [
|
||||
"http 1.4.0",
|
||||
"http",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.18",
|
||||
@ -4184,7 +3909,7 @@ version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b6fceceeb05301cc4c065ab3bd2fa990d41ff4eb44e4ca1b30fa99c057c3e79"
|
||||
dependencies = [
|
||||
"http 1.4.0",
|
||||
"http",
|
||||
"jsonrpsee-client-transport",
|
||||
"jsonrpsee-core",
|
||||
"jsonrpsee-types",
|
||||
@ -4238,12 +3963,6 @@ dependencies = [
|
||||
"thiserror 2.0.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "language-tags"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388"
|
||||
|
||||
[[package]]
|
||||
name = "lazy-regex"
|
||||
version = "3.6.0"
|
||||
@ -4620,12 +4339,6 @@ version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77"
|
||||
|
||||
[[package]]
|
||||
name = "local-waker"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.14"
|
||||
@ -5384,7 +5097,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
@ -5398,7 +5110,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"encoding_rs",
|
||||
"futures-util",
|
||||
"http 1.4.0",
|
||||
"http",
|
||||
"httparse",
|
||||
"memchr",
|
||||
"mime",
|
||||
@ -5545,6 +5257,7 @@ dependencies = [
|
||||
"risc0-zkvm",
|
||||
"secp256k1",
|
||||
"serde",
|
||||
"serde_with",
|
||||
"sha2",
|
||||
"test-case",
|
||||
"test_program_methods",
|
||||
@ -6148,8 +5861,10 @@ name = "program_deployment"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"common",
|
||||
"nssa",
|
||||
"nssa_core",
|
||||
"sequencer_service_rpc",
|
||||
"tokio",
|
||||
"wallet",
|
||||
]
|
||||
@ -6215,7 +5930,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.14.0",
|
||||
"itertools 0.10.5",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
@ -6228,7 +5943,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.14.0",
|
||||
"itertools 0.10.5",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
@ -6270,7 +5985,7 @@ dependencies = [
|
||||
"quinn-udp",
|
||||
"rustc-hash",
|
||||
"rustls",
|
||||
"socket2 0.6.3",
|
||||
"socket2",
|
||||
"thiserror 2.0.18",
|
||||
"tokio",
|
||||
"tracing",
|
||||
@ -6307,9 +6022,9 @@ dependencies = [
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"socket2 0.6.3",
|
||||
"socket2",
|
||||
"tracing",
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -6581,12 +6296,6 @@ dependencies = [
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-lite"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cab834c73d247e67f4fae452806d17d3c7501756d98c8808d7c9c7aa7d18f973"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.10"
|
||||
@ -6606,7 +6315,7 @@ dependencies = [
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2",
|
||||
"http 1.4.0",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
@ -7210,7 +6919,7 @@ dependencies = [
|
||||
"security-framework",
|
||||
"security-framework-sys",
|
||||
"webpki-root-certs 0.26.11",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -7221,9 +6930,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.103.9"
|
||||
version = "0.103.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53"
|
||||
checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
@ -7453,47 +7162,43 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sequencer_rpc"
|
||||
name = "sequencer_service"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"actix-cors",
|
||||
"actix-web",
|
||||
"anyhow",
|
||||
"base58",
|
||||
"base64 0.22.1",
|
||||
"bedrock_client",
|
||||
"borsh",
|
||||
"bytesize",
|
||||
"common",
|
||||
"futures",
|
||||
"hex",
|
||||
"itertools 0.14.0",
|
||||
"log",
|
||||
"mempool",
|
||||
"nssa",
|
||||
"sequencer_core",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tempfile",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sequencer_runner"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"actix",
|
||||
"actix-web",
|
||||
"anyhow",
|
||||
"clap",
|
||||
"common",
|
||||
"env_logger",
|
||||
"futures",
|
||||
"indexer_service_rpc",
|
||||
"jsonrpsee",
|
||||
"log",
|
||||
"mempool",
|
||||
"nssa",
|
||||
"sequencer_core",
|
||||
"sequencer_rpc",
|
||||
"sequencer_service_protocol",
|
||||
"sequencer_service_rpc",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sequencer_service_protocol"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"common",
|
||||
"nssa",
|
||||
"nssa_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sequencer_service_rpc"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"jsonrpsee",
|
||||
"sequencer_service_protocol",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -7689,7 +7394,7 @@ dependencies = [
|
||||
"const_format",
|
||||
"futures",
|
||||
"gloo-net",
|
||||
"http 1.4.0",
|
||||
"http",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"inventory",
|
||||
@ -7826,16 +7531,6 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.5.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.6.3"
|
||||
@ -7855,7 +7550,7 @@ dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"futures",
|
||||
"http 1.4.0",
|
||||
"http",
|
||||
"httparse",
|
||||
"log",
|
||||
"rand 0.8.5",
|
||||
@ -8147,9 +7842,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "testcontainers"
|
||||
version = "0.27.1"
|
||||
version = "0.27.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1c0624faaa317c56d6d19136580be889677259caf5c897941c6f446b4655068"
|
||||
checksum = "0bd36b06a2a6c0c3c81a83be1ab05fe86460d054d4d51bf513bc56b3e15bdc22"
|
||||
dependencies = [
|
||||
"astral-tokio-tar",
|
||||
"async-trait",
|
||||
@ -8161,7 +7856,7 @@ dependencies = [
|
||||
"etcetera",
|
||||
"ferroid",
|
||||
"futures",
|
||||
"http 1.4.0",
|
||||
"http",
|
||||
"itertools 0.14.0",
|
||||
"log",
|
||||
"memchr",
|
||||
@ -8321,7 +8016,7 @@ dependencies = [
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2 0.6.3",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
@ -8518,7 +8213,7 @@ dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"h2",
|
||||
"http 1.4.0",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
@ -8526,7 +8221,7 @@ dependencies = [
|
||||
"hyper-util",
|
||||
"percent-encoding",
|
||||
"pin-project",
|
||||
"socket2 0.6.3",
|
||||
"socket2",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
@ -8576,7 +8271,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"http 1.4.0",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"http-range-header",
|
||||
@ -8678,7 +8373,7 @@ checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"data-encoding",
|
||||
"http 1.4.0",
|
||||
"http",
|
||||
"httparse",
|
||||
"log",
|
||||
"rand 0.9.2",
|
||||
@ -8860,7 +8555,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d81f9efa9df032be5934a46a068815a10a042b494b6a58cb0a1a97bb5467ed6f"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"http 1.4.0",
|
||||
"http",
|
||||
"httparse",
|
||||
"log",
|
||||
]
|
||||
@ -8955,8 +8650,6 @@ dependencies = [
|
||||
"anyhow",
|
||||
"async-stream",
|
||||
"base58",
|
||||
"base64 0.22.1",
|
||||
"borsh",
|
||||
"clap",
|
||||
"common",
|
||||
"env_logger",
|
||||
@ -8972,9 +8665,11 @@ dependencies = [
|
||||
"nssa_core",
|
||||
"optfield",
|
||||
"rand 0.8.5",
|
||||
"sequencer_service_rpc",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"thiserror 2.0.18",
|
||||
"token_core",
|
||||
"tokio",
|
||||
"url",
|
||||
@ -8985,9 +8680,9 @@ name = "wallet-ffi"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cbindgen",
|
||||
"common",
|
||||
"nssa",
|
||||
"nssa_core",
|
||||
"sequencer_service_rpc",
|
||||
"tempfile",
|
||||
"tokio",
|
||||
"wallet",
|
||||
@ -9343,24 +9038,6 @@ dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||
dependencies = [
|
||||
"windows-targets 0.53.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
@ -9394,30 +9071,13 @@ dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.6",
|
||||
"windows_aarch64_msvc 0.52.6",
|
||||
"windows_i686_gnu 0.52.6",
|
||||
"windows_i686_gnullvm 0.52.6",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc 0.52.6",
|
||||
"windows_x86_64_gnu 0.52.6",
|
||||
"windows_x86_64_gnullvm 0.52.6",
|
||||
"windows_x86_64_msvc 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows_aarch64_gnullvm 0.53.1",
|
||||
"windows_aarch64_msvc 0.53.1",
|
||||
"windows_i686_gnu 0.53.1",
|
||||
"windows_i686_gnullvm 0.53.1",
|
||||
"windows_i686_msvc 0.53.1",
|
||||
"windows_x86_64_gnu 0.53.1",
|
||||
"windows_x86_64_gnullvm 0.53.1",
|
||||
"windows_x86_64_msvc 0.53.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.42.2"
|
||||
@ -9430,12 +9090,6 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
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"
|
||||
@ -9448,12 +9102,6 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
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"
|
||||
@ -9466,24 +9114,12 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
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"
|
||||
@ -9496,12 +9132,6 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
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"
|
||||
@ -9514,12 +9144,6 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
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"
|
||||
@ -9532,12 +9156,6 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
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"
|
||||
@ -9550,12 +9168,6 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.15"
|
||||
|
||||
14
Cargo.toml
14
Cargo.toml
@ -17,9 +17,10 @@ members = [
|
||||
"programs/amm",
|
||||
"programs/token/core",
|
||||
"programs/token",
|
||||
"sequencer_core",
|
||||
"sequencer_rpc",
|
||||
"sequencer_runner",
|
||||
"sequencer/core",
|
||||
"sequencer/service",
|
||||
"sequencer/service/protocol",
|
||||
"sequencer/service/rpc",
|
||||
"indexer/core",
|
||||
"indexer/service",
|
||||
"indexer/service/protocol",
|
||||
@ -42,9 +43,10 @@ common = { path = "common" }
|
||||
mempool = { path = "mempool" }
|
||||
storage = { path = "storage" }
|
||||
key_protocol = { path = "key_protocol" }
|
||||
sequencer_core = { path = "sequencer_core" }
|
||||
sequencer_rpc = { path = "sequencer_rpc" }
|
||||
sequencer_runner = { path = "sequencer_runner" }
|
||||
sequencer_core = { path = "sequencer/core" }
|
||||
sequencer_service_protocol = { path = "sequencer/service/protocol" }
|
||||
sequencer_service_rpc = { path = "sequencer/service/rpc" }
|
||||
sequencer_service = { path = "sequencer/service" }
|
||||
indexer_core = { path = "indexer/core" }
|
||||
indexer_service = { path = "indexer/service" }
|
||||
indexer_service_protocol = { path = "indexer/service/protocol" }
|
||||
|
||||
9
Justfile
9
Justfile
@ -30,10 +30,10 @@ run-bedrock:
|
||||
docker compose up
|
||||
|
||||
# Run Sequencer
|
||||
[working-directory: 'sequencer_runner']
|
||||
[working-directory: 'sequencer/service']
|
||||
run-sequencer:
|
||||
@echo "🧠 Running sequencer"
|
||||
RUST_LOG=info RISC0_DEV_MODE=1 cargo run --release -p sequencer_runner configs/debug
|
||||
RUST_LOG=info RISC0_DEV_MODE=1 cargo run --release -p sequencer_service configs/debug/sequencer_config.json
|
||||
|
||||
# Run Indexer
|
||||
[working-directory: 'indexer/service']
|
||||
@ -62,8 +62,9 @@ run-wallet +args:
|
||||
# Clean runtime data
|
||||
clean:
|
||||
@echo "🧹 Cleaning run artifacts"
|
||||
rm -rf sequencer_runner/bedrock_signing_key
|
||||
rm -rf sequencer_runner/rocksdb
|
||||
rm -rf sequencer/service/bedrock_signing_key
|
||||
rm -rf sequencer/service/rocksdb
|
||||
rm -rf indexer/service/rocksdb
|
||||
rm -rf wallet/configs/debug/storage.json
|
||||
rm -rf rocksdb
|
||||
cd bedrock && docker compose down -v
|
||||
|
||||
24
README.md
24
README.md
@ -12,7 +12,7 @@ Public accounts are stored on-chain as a visible map from IDs to account states,
|
||||
|
||||
### Programmability and selective privacy
|
||||
|
||||
LEZ aims to deliver full programmability in a hybrid public/private model, with the same flexibility and composability as public blockchains. Developers write and deploy programs in LEZ just as they would elsewhere. The protocol automatically supports executions that involve any combination of public and private accounts. From the program’s perspective, all accounts look the same, and privacy is enforced transparently. This lets developers focus on business logic while the system guarantees privacy and correctness.
|
||||
LEZ aims to deliver full programmability in a hybrid public/private model, with the same flexibility and composability as public blockchains. Developers write and deploy programs in LEZ without addressing privacy concerns. The protocol automatically supports executions that involve any combination of public and private accounts. From the program’s perspective, all accounts look the same, and privacy is enforced transparently. This lets developers focus on business logic while the system guarantees privacy and correctness.
|
||||
|
||||
To our knowledge, this design is unique to LEZ. Other privacy-focused programmable blockchains often require developers to explicitly handle private inputs inside their app logic. In LEZ, privacy is protocol-level: programs do not change, accounts are treated uniformly, and private execution works out of the box.
|
||||
|
||||
@ -73,6 +73,16 @@ This design keeps public transactions as fast as any RISC-V–based VM and makes
|
||||
---
|
||||
---
|
||||
|
||||
# Versioning
|
||||
|
||||
We release versions as git tags (e.g. `v0.1.0`). If no critical issues with version is found you can expect it to be immutable. All further features and fixes will be a part of the next tag. As the project is in active development we don't provide backward compatibility yet.
|
||||
For each tag we publish docker images of our services.
|
||||
If you depend on this project you can pin your rust dependency to a git tag like this:
|
||||
|
||||
```toml
|
||||
nssa_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.1.0" }
|
||||
```
|
||||
|
||||
# Install dependencies
|
||||
### Install build dependencies
|
||||
|
||||
@ -141,17 +151,17 @@ The sequencer and logos blockchain node can be run locally:
|
||||
- `./scripts/setup-logos-blockchain-circuits.sh`
|
||||
- `cargo build --all-features`
|
||||
- `./target/debug/logos-blockchain-node --deployment nodes/node/standalone-deployment-config.yaml nodes/node/standalone-node-config.yaml`
|
||||
|
||||
|
||||
- Alternatively (WARNING: This node is outdated) go to `logos-blockchain/lssa/` repo and run the node from docker:
|
||||
- `cd bedrock`
|
||||
- Change line 14 of `docker-compose.yml` from `"0:18080/tcp"` into `"8080:18080/tcp"`
|
||||
- `docker compose up`
|
||||
|
||||
|
||||
2. On another terminal go to the `logos-blockchain/lssa` repo and run indexer service:
|
||||
- `RUST_LOG=info cargo run -p indexer_service indexer/service/configs/indexer_config.json`
|
||||
|
||||
3. On another terminal go to the `logos-blockchain/lssa` repo and run the sequencer:
|
||||
- `RUST_LOG=info cargo run -p sequencer_runner sequencer_runner/configs/debug`
|
||||
- `RUST_LOG=info cargo run -p sequencer_service sequencer/service/configs/debug/sequencer_config.json`
|
||||
4. (To run the explorer): on another terminal go to `logos-blockchain/lssa/explorer_service` and run the following:
|
||||
- `cargo install cargo-leptos`
|
||||
- `cargo leptos build --release`
|
||||
@ -161,8 +171,8 @@ The sequencer and logos blockchain node can be run locally:
|
||||
|
||||
After stopping services above you need to remove 3 folders to start cleanly:
|
||||
1. In the `logos-blockchain/logos-blockchain` folder `state` (not needed in case of docker setup)
|
||||
2. In the `lssa` folder `sequencer_runner/rocksdb`
|
||||
3. In the `lssa` file `sequencer_runner/bedrock_signing_key`
|
||||
2. In the `lssa` folder `sequencer/service/rocksdb`
|
||||
3. In the `lssa` file `sequencer/service/bedrock_signing_key`
|
||||
4. In the `lssa` folder `indexer/service/rocksdb`
|
||||
|
||||
### Normal mode (`just` commands)
|
||||
@ -210,7 +220,7 @@ This will use a wallet binary built from this repo and not the one installed in
|
||||
### Standalone mode
|
||||
The sequencer can be run in standalone mode with:
|
||||
```bash
|
||||
RUST_LOG=info cargo run --features standalone -p sequencer_runner sequencer_runner/configs/debug
|
||||
RUST_LOG=info cargo run --features standalone -p sequencer_service sequencer/service/configs/debug
|
||||
```
|
||||
|
||||
## Running with Docker
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -13,16 +13,11 @@ nssa_core.workspace = true
|
||||
|
||||
anyhow.workspace = true
|
||||
thiserror.workspace = true
|
||||
serde_json.workspace = true
|
||||
serde.workspace = true
|
||||
serde_with.workspace = true
|
||||
reqwest.workspace = true
|
||||
base64.workspace = true
|
||||
sha2.workspace = true
|
||||
log.workspace = true
|
||||
hex.workspace = true
|
||||
borsh.workspace = true
|
||||
bytesize.workspace = true
|
||||
base64.workspace = true
|
||||
url.workspace = true
|
||||
logos-blockchain-common-http-client.workspace = true
|
||||
tokio-retry.workspace = true
|
||||
|
||||
@ -60,6 +60,18 @@ pub struct Block {
|
||||
pub bedrock_parent_id: MantleMsgId,
|
||||
}
|
||||
|
||||
impl Serialize for Block {
|
||||
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
crate::borsh_base64::serialize(self, serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Block {
|
||||
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
crate::borsh_base64::deserialize(deserializer)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
|
||||
pub struct HashableBlockData {
|
||||
pub block_id: BlockId,
|
||||
|
||||
25
common/src/borsh_base64.rs
Normal file
25
common/src/borsh_base64.rs
Normal file
@ -0,0 +1,25 @@
|
||||
//! This module provides utilities for serializing and deserializing data by combining Borsh and
|
||||
//! Base64 encodings.
|
||||
|
||||
use base64::{Engine as _, engine::general_purpose::STANDARD};
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub fn serialize<T: BorshSerialize, S: serde::Serializer>(
|
||||
value: &T,
|
||||
serializer: S,
|
||||
) -> Result<S::Ok, S::Error> {
|
||||
let borsh_encoded = borsh::to_vec(value).map_err(serde::ser::Error::custom)?;
|
||||
let base64_encoded = STANDARD.encode(&borsh_encoded);
|
||||
Serialize::serialize(&base64_encoded, serializer)
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, T: BorshDeserialize, D: serde::Deserializer<'de>>(
|
||||
deserializer: D,
|
||||
) -> Result<T, D::Error> {
|
||||
let base64_encoded = <String as Deserialize>::deserialize(deserializer)?;
|
||||
let borsh_encoded = STANDARD
|
||||
.decode(base64_encoded.as_bytes())
|
||||
.map_err(serde::de::Error::custom)?;
|
||||
borsh::from_slice(&borsh_encoded).map_err(serde::de::Error::custom)
|
||||
}
|
||||
@ -1,43 +0,0 @@
|
||||
use nssa::AccountId;
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::rpc_primitives::errors::RpcError;
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct SequencerRpcError {
|
||||
pub jsonrpc: String,
|
||||
pub error: RpcError,
|
||||
pub id: u64,
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum SequencerClientError {
|
||||
#[error("HTTP error")]
|
||||
HTTPError(#[from] reqwest::Error),
|
||||
#[error("Serde error")]
|
||||
SerdeError(#[from] serde_json::Error),
|
||||
#[error("Internal error: {0:?}")]
|
||||
InternalError(SequencerRpcError),
|
||||
}
|
||||
|
||||
impl From<SequencerRpcError> for SequencerClientError {
|
||||
fn from(value: SequencerRpcError) -> Self {
|
||||
Self::InternalError(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ExecutionFailureKind {
|
||||
#[error("Failed to get data from sequencer")]
|
||||
SequencerError(#[source] anyhow::Error),
|
||||
#[error("Inputs amounts does not match outputs")]
|
||||
AmountMismatchError,
|
||||
#[error("Accounts key not found")]
|
||||
KeyNotFoundError,
|
||||
#[error("Sequencer client error: {0:?}")]
|
||||
SequencerClientError(#[from] SequencerClientError),
|
||||
#[error("Can not pay for operation")]
|
||||
InsufficientFundsError,
|
||||
#[error("Account {0} data is invalid")]
|
||||
AccountDataError(AccountId),
|
||||
}
|
||||
@ -4,10 +4,8 @@ use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use serde_with::{DeserializeFromStr, SerializeDisplay};
|
||||
|
||||
pub mod block;
|
||||
mod borsh_base64;
|
||||
pub mod config;
|
||||
pub mod error;
|
||||
pub mod rpc_primitives;
|
||||
pub mod sequencer_client;
|
||||
pub mod transaction;
|
||||
|
||||
// Module for tests utility functions
|
||||
|
||||
@ -1,194 +0,0 @@
|
||||
use std::fmt;
|
||||
|
||||
use serde_json::{Value, to_value};
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
pub struct RpcParseError(pub String);
|
||||
|
||||
/// This struct may be returned from JSON RPC server in case of error.
|
||||
///
|
||||
/// It is expected that that this struct has impls From<_> all other RPC errors
|
||||
/// like [`RpcBlockError`](crate::types::blocks::RpcBlockError).
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct RpcError {
|
||||
#[serde(flatten)]
|
||||
pub error_struct: Option<RpcErrorKind>,
|
||||
/// Deprecated please use the `error_struct` instead.
|
||||
pub code: i64,
|
||||
/// Deprecated please use the `error_struct` instead.
|
||||
pub message: String,
|
||||
/// Deprecated please use the `error_struct` instead.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub data: Option<Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)]
|
||||
#[serde(tag = "name", content = "cause", rename_all = "SCREAMING_SNAKE_CASE")]
|
||||
pub enum RpcErrorKind {
|
||||
RequestValidationError(RpcRequestValidationErrorKind),
|
||||
HandlerError(Value),
|
||||
InternalError(Value),
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)]
|
||||
#[serde(tag = "name", content = "info", rename_all = "SCREAMING_SNAKE_CASE")]
|
||||
pub enum RpcRequestValidationErrorKind {
|
||||
MethodNotFound { method_name: String },
|
||||
ParseError { error_message: String },
|
||||
}
|
||||
|
||||
/// A general Server Error.
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Clone)]
|
||||
pub enum ServerError {
|
||||
Timeout,
|
||||
Closed,
|
||||
}
|
||||
|
||||
impl RpcError {
|
||||
/// A generic constructor.
|
||||
///
|
||||
/// Mostly for completeness, doesn't do anything but filling in the corresponding fields.
|
||||
#[must_use]
|
||||
pub const fn new(code: i64, message: String, data: Option<Value>) -> Self {
|
||||
Self {
|
||||
code,
|
||||
message,
|
||||
data,
|
||||
error_struct: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an Invalid Param error.
|
||||
pub fn invalid_params(data: impl serde::Serialize) -> Self {
|
||||
let value = match to_value(data) {
|
||||
Ok(value) => value,
|
||||
Err(err) => {
|
||||
return Self::server_error(Some(format!(
|
||||
"Failed to serialize invalid parameters error: {:?}",
|
||||
err.to_string()
|
||||
)));
|
||||
}
|
||||
};
|
||||
Self::new(-32_602, "Invalid params".to_owned(), Some(value))
|
||||
}
|
||||
|
||||
/// Create a server error.
|
||||
pub fn server_error<E: serde::Serialize>(e: Option<E>) -> Self {
|
||||
Self::new(
|
||||
-32_000,
|
||||
"Server error".to_owned(),
|
||||
e.map(|v| to_value(v).expect("Must be representable in JSON")),
|
||||
)
|
||||
}
|
||||
|
||||
/// Create a parse error.
|
||||
#[must_use]
|
||||
pub fn parse_error(e: String) -> Self {
|
||||
Self {
|
||||
code: -32_700,
|
||||
message: "Parse error".to_owned(),
|
||||
data: Some(Value::String(e.clone())),
|
||||
error_struct: Some(RpcErrorKind::RequestValidationError(
|
||||
RpcRequestValidationErrorKind::ParseError { error_message: e },
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn serialization_error(e: &str) -> Self {
|
||||
Self::new_internal_error(Some(Value::String(e.to_owned())), e)
|
||||
}
|
||||
|
||||
/// Helper method to define extract `INTERNAL_ERROR` in separate `RpcErrorKind`
|
||||
/// Returns `HANDLER_ERROR` if the error is not internal one.
|
||||
#[must_use]
|
||||
pub fn new_internal_or_handler_error(error_data: Option<Value>, error_struct: Value) -> Self {
|
||||
if error_struct["name"] == "INTERNAL_ERROR" {
|
||||
let error_message = match error_struct["info"].get("error_message") {
|
||||
Some(Value::String(error_message)) => error_message.as_str(),
|
||||
_ => "InternalError happened during serializing InternalError",
|
||||
};
|
||||
Self::new_internal_error(error_data, error_message)
|
||||
} else {
|
||||
Self::new_handler_error(error_data, error_struct)
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn new_internal_error(error_data: Option<Value>, info: &str) -> Self {
|
||||
Self {
|
||||
code: -32_000,
|
||||
message: "Server error".to_owned(),
|
||||
data: error_data,
|
||||
error_struct: Some(RpcErrorKind::InternalError(serde_json::json!({
|
||||
"name": "INTERNAL_ERROR",
|
||||
"info": serde_json::json!({"error_message": info})
|
||||
}))),
|
||||
}
|
||||
}
|
||||
|
||||
fn new_handler_error(error_data: Option<Value>, error_struct: Value) -> Self {
|
||||
Self {
|
||||
code: -32_000,
|
||||
message: "Server error".to_owned(),
|
||||
data: error_data,
|
||||
error_struct: Some(RpcErrorKind::HandlerError(error_struct)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a method not found error.
|
||||
#[must_use]
|
||||
pub fn method_not_found(method: String) -> Self {
|
||||
Self {
|
||||
code: -32_601,
|
||||
message: "Method not found".to_owned(),
|
||||
data: Some(Value::String(method.clone())),
|
||||
error_struct: Some(RpcErrorKind::RequestValidationError(
|
||||
RpcRequestValidationErrorKind::MethodNotFound {
|
||||
method_name: method,
|
||||
},
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for RpcError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{self:?}")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RpcParseError> for RpcError {
|
||||
fn from(parse_error: RpcParseError) -> Self {
|
||||
Self::parse_error(parse_error.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::convert::Infallible> for RpcError {
|
||||
fn from(_: std::convert::Infallible) -> Self {
|
||||
// SAFETY: Infallible error can never be constructed, so this code can never be reached.
|
||||
unsafe { core::hint::unreachable_unchecked() }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ServerError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Timeout => write!(f, "ServerError: Timeout"),
|
||||
Self::Closed => write!(f, "ServerError: Closed"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ServerError> for RpcError {
|
||||
fn from(e: ServerError) -> Self {
|
||||
let error_data = match to_value(&e) {
|
||||
Ok(value) => value,
|
||||
Err(_err) => {
|
||||
return Self::new_internal_error(None, "Failed to serialize ServerError");
|
||||
}
|
||||
};
|
||||
Self::new_internal_error(Some(error_data), e.to_string().as_str())
|
||||
}
|
||||
}
|
||||
@ -1,588 +0,0 @@
|
||||
// Copyright 2017 tokio-jsonrpc Developers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
|
||||
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
|
||||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
|
||||
//! JSON-RPC 2.0 messages.
|
||||
//!
|
||||
//! The main entrypoint here is the [Message](enum.Message.html). The others are just building
|
||||
//! blocks and you should generally work with `Message` instead.
|
||||
use std::fmt::{Formatter, Result as FmtResult};
|
||||
|
||||
use serde::{
|
||||
de::{Deserializer, Error, Unexpected, Visitor},
|
||||
ser::{SerializeStruct as _, Serializer},
|
||||
};
|
||||
use serde_json::{Result as JsonResult, Value};
|
||||
|
||||
use super::errors::RpcError;
|
||||
|
||||
pub type Parsed = Result<Message, Broken>;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
struct Version;
|
||||
|
||||
impl serde::Serialize for Version {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
serializer.serialize_str("2.0")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde::Deserialize<'de> for Version {
|
||||
#[expect(
|
||||
clippy::renamed_function_params,
|
||||
reason = "More readable than original serde parameter names"
|
||||
)]
|
||||
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
struct VersionVisitor;
|
||||
impl Visitor<'_> for VersionVisitor {
|
||||
type Value = Version;
|
||||
|
||||
fn expecting(&self, formatter: &mut Formatter<'_>) -> FmtResult {
|
||||
formatter.write_str("a version string")
|
||||
}
|
||||
|
||||
fn visit_str<E: Error>(self, value: &str) -> Result<Version, E> {
|
||||
match value {
|
||||
"2.0" => Ok(Version),
|
||||
_ => Err(E::invalid_value(Unexpected::Str(value), &"value 2.0")),
|
||||
}
|
||||
}
|
||||
}
|
||||
deserializer.deserialize_str(VersionVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
/// An RPC request.
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[expect(
|
||||
clippy::partial_pub_fields,
|
||||
reason = "We don't want to allow access to the version, but the others are public for ease of use"
|
||||
)]
|
||||
pub struct Request {
|
||||
jsonrpc: Version,
|
||||
pub method: String,
|
||||
#[serde(default, skip_serializing_if = "Value::is_null")]
|
||||
pub params: Value,
|
||||
pub id: Value,
|
||||
}
|
||||
|
||||
impl Request {
|
||||
#[must_use]
|
||||
pub fn from_payload_version_2_0(method: String, payload: serde_json::Value) -> Self {
|
||||
Self {
|
||||
jsonrpc: Version,
|
||||
method,
|
||||
params: payload,
|
||||
// ToDo: Correct checking of id
|
||||
id: 1.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Answer the request with a (positive) reply.
|
||||
///
|
||||
/// The ID is taken from the request.
|
||||
#[must_use]
|
||||
pub fn reply(&self, reply: Value) -> Message {
|
||||
Message::Response(Response {
|
||||
jsonrpc: Version,
|
||||
result: Ok(reply),
|
||||
id: self.id.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Answer the request with an error.
|
||||
#[must_use]
|
||||
pub fn error(&self, error: RpcError) -> Message {
|
||||
Message::Response(Response {
|
||||
jsonrpc: Version,
|
||||
result: Err(error),
|
||||
id: self.id.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A response to an RPC.
|
||||
///
|
||||
/// It is created by the methods on [Request](struct.Request.html).
|
||||
#[expect(
|
||||
clippy::partial_pub_fields,
|
||||
reason = "We don't want to allow access to the version, but the others are public for ease of use"
|
||||
)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Response {
|
||||
jsonrpc: Version,
|
||||
pub result: Result<Value, RpcError>,
|
||||
pub id: Value,
|
||||
}
|
||||
|
||||
impl serde::Serialize for Response {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let mut sub = serializer.serialize_struct("Response", 3)?;
|
||||
sub.serialize_field("jsonrpc", &self.jsonrpc)?;
|
||||
match &self.result {
|
||||
Ok(value) => sub.serialize_field("result", value),
|
||||
Err(err) => sub.serialize_field("error", err),
|
||||
}?;
|
||||
sub.serialize_field("id", &self.id)?;
|
||||
sub.end()
|
||||
}
|
||||
}
|
||||
|
||||
/// A helper trick for deserialization.
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct WireResponse {
|
||||
// It is actually used to eat and sanity check the deserialized text
|
||||
#[serde(rename = "jsonrpc")]
|
||||
_jsonrpc: Version,
|
||||
// Make sure we accept null as Some(Value::Null), instead of going to None
|
||||
#[serde(default, deserialize_with = "some_value")]
|
||||
result: Option<Value>,
|
||||
error: Option<RpcError>,
|
||||
id: Value,
|
||||
}
|
||||
|
||||
// Implementing deserialize is hard. We sidestep the difficulty by deserializing a similar
|
||||
// structure that directly corresponds to whatever is on the wire and then convert it to our more
|
||||
// convenient representation.
|
||||
impl<'de> serde::Deserialize<'de> for Response {
|
||||
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
let wr: WireResponse = serde::Deserialize::deserialize(deserializer)?;
|
||||
let result = match (wr.result, wr.error) {
|
||||
(Some(res), None) => Ok(res),
|
||||
(None, Some(err)) => Err(err),
|
||||
_ => {
|
||||
let err = D::Error::custom("Either 'error' or 'result' is expected, but not both");
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
Ok(Self {
|
||||
jsonrpc: Version,
|
||||
result,
|
||||
id: wr.id,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A notification (doesn't expect an answer).
|
||||
#[expect(
|
||||
clippy::partial_pub_fields,
|
||||
reason = "We don't want to allow access to the version, but the others are public for ease of use"
|
||||
)]
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Notification {
|
||||
jsonrpc: Version,
|
||||
pub method: String,
|
||||
#[serde(default, skip_serializing_if = "Value::is_null")]
|
||||
pub params: Value,
|
||||
}
|
||||
|
||||
/// One message of the JSON RPC protocol.
|
||||
///
|
||||
/// One message, directly mapped from the structures of the protocol. See the
|
||||
/// [specification](http://www.jsonrpc.org/specification) for more details.
|
||||
///
|
||||
/// Since the protocol allows one endpoint to be both client and server at the same time, the
|
||||
/// message can decode and encode both directions of the protocol.
|
||||
///
|
||||
/// The `Batch` variant is supposed to be created directly, without a constructor.
|
||||
///
|
||||
/// The `UnmatchedSub` variant is used when a request is an array and some of the subrequests
|
||||
/// aren't recognized as valid json rpc 2.0 messages. This is never returned as a top-level
|
||||
/// element, it is returned as `Err(Broken::Unmatched)`.
|
||||
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum Message {
|
||||
/// An RPC request.
|
||||
Request(Request),
|
||||
/// A response to a Request.
|
||||
Response(Response),
|
||||
/// A notification.
|
||||
Notification(Notification),
|
||||
/// A batch of more requests or responses.
|
||||
///
|
||||
/// The protocol allows bundling multiple requests, notifications or responses to a single
|
||||
/// message.
|
||||
///
|
||||
/// This variant has no direct constructor and is expected to be constructed manually.
|
||||
Batch(Vec<Self>),
|
||||
/// An unmatched sub entry in a `Batch`.
|
||||
///
|
||||
/// When there's a `Batch` and an element doesn't comform to the JSONRPC 2.0 format, that one
|
||||
/// is represented by this. This is never produced as a top-level value when parsing, the
|
||||
/// `Err(Broken::Unmatched)` is used instead. It is not possible to serialize.
|
||||
#[serde(skip_serializing)]
|
||||
UnmatchedSub(Value),
|
||||
}
|
||||
|
||||
impl Message {
|
||||
/// A constructor for a request.
|
||||
///
|
||||
/// The ID is auto-set to dontcare.
|
||||
#[must_use]
|
||||
pub fn request(method: String, params: Value) -> Self {
|
||||
let id = Value::from("dontcare");
|
||||
Self::Request(Request {
|
||||
jsonrpc: Version,
|
||||
method,
|
||||
params,
|
||||
id,
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a top-level error (without an ID).
|
||||
#[must_use]
|
||||
pub const fn error(error: RpcError) -> Self {
|
||||
Self::Response(Response {
|
||||
jsonrpc: Version,
|
||||
result: Err(error),
|
||||
id: Value::Null,
|
||||
})
|
||||
}
|
||||
|
||||
/// A constructor for a notification.
|
||||
#[must_use]
|
||||
pub const fn notification(method: String, params: Value) -> Self {
|
||||
Self::Notification(Notification {
|
||||
jsonrpc: Version,
|
||||
method,
|
||||
params,
|
||||
})
|
||||
}
|
||||
|
||||
/// A constructor for a response.
|
||||
#[must_use]
|
||||
pub const fn response(id: Value, result: Result<Value, RpcError>) -> Self {
|
||||
Self::Response(Response {
|
||||
jsonrpc: Version,
|
||||
result,
|
||||
id,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns id or Null if there is no id.
|
||||
#[must_use]
|
||||
pub fn id(&self) -> Value {
|
||||
match self {
|
||||
Self::Request(req) => req.id.clone(),
|
||||
Self::Response(response) => response.id.clone(),
|
||||
Self::Notification(_) | Self::Batch(_) | Self::UnmatchedSub(_) => Value::Null,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Message> for String {
|
||||
fn from(val: Message) -> Self {
|
||||
::serde_json::ser::to_string(&val).expect("message serialization to json should not fail")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Message> for Vec<u8> {
|
||||
fn from(val: Message) -> Self {
|
||||
::serde_json::ser::to_vec(&val)
|
||||
.expect("message serialization to json bytes should not fail")
|
||||
}
|
||||
}
|
||||
|
||||
/// A broken message.
|
||||
///
|
||||
/// Protocol-level errors.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum Broken {
|
||||
/// It was valid JSON, but doesn't match the form of a JSONRPC 2.0 message.
|
||||
Unmatched(Value),
|
||||
/// Invalid JSON.
|
||||
#[serde(skip_deserializing)]
|
||||
SyntaxError(String),
|
||||
}
|
||||
|
||||
impl Broken {
|
||||
/// Generate an appropriate error message.
|
||||
///
|
||||
/// The error message for these things are specified in the RFC, so this just creates an error
|
||||
/// with the right values.
|
||||
#[must_use]
|
||||
pub fn reply(&self) -> Message {
|
||||
match self {
|
||||
Self::Unmatched(_) => Message::error(RpcError::parse_error(
|
||||
"JSON RPC Request format was expected".to_owned(),
|
||||
)),
|
||||
Self::SyntaxError(e) => Message::error(RpcError::parse_error(e.clone())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A trick to easily deserialize and detect valid JSON, but invalid Message.
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum WireMessage {
|
||||
Message(Message),
|
||||
Broken(Broken),
|
||||
}
|
||||
|
||||
pub fn decoded_to_parsed(res: JsonResult<WireMessage>) -> Parsed {
|
||||
match res {
|
||||
Ok(WireMessage::Message(Message::UnmatchedSub(value))) => Err(Broken::Unmatched(value)),
|
||||
Ok(WireMessage::Message(m)) => Ok(m),
|
||||
Ok(WireMessage::Broken(b)) => Err(b),
|
||||
Err(e) => Err(Broken::SyntaxError(e.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
/// Read a [Message](enum.Message.html) from a slice.
|
||||
///
|
||||
/// Invalid JSON or JSONRPC messages are reported as [Broken](enum.Broken.html).
|
||||
pub fn from_slice(s: &[u8]) -> Parsed {
|
||||
decoded_to_parsed(::serde_json::de::from_slice(s))
|
||||
}
|
||||
|
||||
/// Read a [Message](enum.Message.html) from a string.
|
||||
///
|
||||
/// Invalid JSON or JSONRPC messages are reported as [Broken](enum.Broken.html).
|
||||
pub fn from_str(s: &str) -> Parsed {
|
||||
from_slice(s.as_bytes())
|
||||
}
|
||||
|
||||
/// Deserializer for `Option<Value>` that produces `Some(Value::Null)`.
|
||||
///
|
||||
/// The usual one produces None in that case. But we need to know the difference between
|
||||
/// `{x: null}` and `{}`.
|
||||
fn some_value<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Option<Value>, D::Error> {
|
||||
serde::Deserialize::deserialize(deserializer).map(Some)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serde_json::{Value, de::from_slice, json, ser::to_vec};
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Test serialization and deserialization of the Message.
|
||||
///
|
||||
/// We first deserialize it from a string. That way we check deserialization works.
|
||||
/// But since serialization doesn't have to produce the exact same result (order, spaces, …),
|
||||
/// we then serialize and deserialize the thing again and check it matches.
|
||||
#[test]
|
||||
fn message_serde() {
|
||||
// A helper for running one message test
|
||||
fn one(input: &str, expected: &Message) {
|
||||
let parsed: Message = from_str(input).unwrap();
|
||||
assert_eq!(*expected, parsed);
|
||||
let serialized = to_vec(&parsed).unwrap();
|
||||
let deserialized: Message = from_slice(&serialized).unwrap();
|
||||
assert_eq!(parsed, deserialized);
|
||||
}
|
||||
|
||||
// A request without parameters
|
||||
one(
|
||||
r#"{"jsonrpc": "2.0", "method": "call", "id": 1}"#,
|
||||
&Message::Request(Request {
|
||||
jsonrpc: Version,
|
||||
method: "call".to_owned(),
|
||||
params: Value::Null,
|
||||
id: json!(1),
|
||||
}),
|
||||
);
|
||||
// A request with parameters
|
||||
one(
|
||||
r#"{"jsonrpc": "2.0", "method": "call", "params": [1, 2, 3], "id": 2}"#,
|
||||
&Message::Request(Request {
|
||||
jsonrpc: Version,
|
||||
method: "call".to_owned(),
|
||||
params: json!([1, 2, 3]),
|
||||
id: json!(2),
|
||||
}),
|
||||
);
|
||||
// A notification (with parameters)
|
||||
one(
|
||||
r#"{"jsonrpc": "2.0", "method": "notif", "params": {"x": "y"}}"#,
|
||||
&Message::Notification(Notification {
|
||||
jsonrpc: Version,
|
||||
method: "notif".to_owned(),
|
||||
params: json!({"x": "y"}),
|
||||
}),
|
||||
);
|
||||
// A successful response
|
||||
one(
|
||||
r#"{"jsonrpc": "2.0", "result": 42, "id": 3}"#,
|
||||
&Message::Response(Response {
|
||||
jsonrpc: Version,
|
||||
result: Ok(json!(42)),
|
||||
id: json!(3),
|
||||
}),
|
||||
);
|
||||
// A successful response
|
||||
one(
|
||||
r#"{"jsonrpc": "2.0", "result": null, "id": 3}"#,
|
||||
&Message::Response(Response {
|
||||
jsonrpc: Version,
|
||||
result: Ok(Value::Null),
|
||||
id: json!(3),
|
||||
}),
|
||||
);
|
||||
// An error
|
||||
one(
|
||||
r#"{"jsonrpc": "2.0", "error": {"code": 42, "message": "Wrong!"}, "id": null}"#,
|
||||
&Message::Response(Response {
|
||||
jsonrpc: Version,
|
||||
result: Err(RpcError::new(42, "Wrong!".to_owned(), None)),
|
||||
id: Value::Null,
|
||||
}),
|
||||
);
|
||||
// A batch
|
||||
one(
|
||||
r#"[
|
||||
{"jsonrpc": "2.0", "method": "notif"},
|
||||
{"jsonrpc": "2.0", "method": "call", "id": 42}
|
||||
]"#,
|
||||
&Message::Batch(vec![
|
||||
Message::Notification(Notification {
|
||||
jsonrpc: Version,
|
||||
method: "notif".to_owned(),
|
||||
params: Value::Null,
|
||||
}),
|
||||
Message::Request(Request {
|
||||
jsonrpc: Version,
|
||||
method: "call".to_owned(),
|
||||
params: Value::Null,
|
||||
id: json!(42),
|
||||
}),
|
||||
]),
|
||||
);
|
||||
// Some handling of broken messages inside a batch
|
||||
let parsed = from_str(
|
||||
r#"[
|
||||
{"jsonrpc": "2.0", "method": "notif"},
|
||||
{"jsonrpc": "2.0", "method": "call", "id": 42},
|
||||
true
|
||||
]"#,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
Message::Batch(vec![
|
||||
Message::Notification(Notification {
|
||||
jsonrpc: Version,
|
||||
method: "notif".to_owned(),
|
||||
params: Value::Null,
|
||||
}),
|
||||
Message::Request(Request {
|
||||
jsonrpc: Version,
|
||||
method: "call".to_owned(),
|
||||
params: Value::Null,
|
||||
id: json!(42),
|
||||
}),
|
||||
Message::UnmatchedSub(Value::Bool(true)),
|
||||
]),
|
||||
parsed
|
||||
);
|
||||
to_vec(&Message::UnmatchedSub(Value::Null)).unwrap_err();
|
||||
}
|
||||
|
||||
/// A helper for the `broken` test.
|
||||
///
|
||||
/// Check that the given JSON string parses, but is not recognized as a valid RPC message.
|
||||
///
|
||||
/// Test things that are almost but not entirely JSONRPC are rejected.
|
||||
///
|
||||
/// The reject is done by returning it as Unmatched.
|
||||
#[test]
|
||||
fn broken() {
|
||||
// A helper with one test
|
||||
fn one(input: &str) {
|
||||
let msg = from_str(input);
|
||||
match msg {
|
||||
Err(Broken::Unmatched(_)) => (),
|
||||
_ => panic!("{input} recognized as an RPC message: {msg:?}!"),
|
||||
}
|
||||
}
|
||||
|
||||
// Missing the version
|
||||
one(r#"{"method": "notif"}"#);
|
||||
// Wrong version
|
||||
one(r#"{"jsonrpc": 2.0, "method": "notif"}"#);
|
||||
// A response with both result and error
|
||||
one(r#"{"jsonrpc": "2.0", "result": 42, "error": {"code": 42, "message": "!"}, "id": 1}"#);
|
||||
// A response without an id
|
||||
one(r#"{"jsonrpc": "2.0", "result": 42}"#);
|
||||
// An extra field
|
||||
one(r#"{"jsonrpc": "2.0", "method": "weird", "params": 42, "others": 43, "id": 2}"#);
|
||||
// Something completely different
|
||||
one(r#"{"x": [1, 2, 3]}"#);
|
||||
|
||||
match from_str("{]") {
|
||||
Err(Broken::SyntaxError(_)) => (),
|
||||
other => panic!("Something unexpected: {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Test some non-trivial aspects of the constructors.
|
||||
///
|
||||
/// This doesn't have a full coverage, because there's not much to actually test there.
|
||||
/// Most of it is related to the ids.
|
||||
#[test]
|
||||
#[ignore = "Not a full coverage test"]
|
||||
fn constructors() {
|
||||
let msg1 = Message::request("call".to_owned(), json!([1, 2, 3]));
|
||||
let msg2 = Message::request("call".to_owned(), json!([1, 2, 3]));
|
||||
// They differ, even when created with the same parameters
|
||||
assert_ne!(msg1, msg2);
|
||||
// And, specifically, they differ in the ID's
|
||||
let (req1, req2) = if let (Message::Request(req1), Message::Request(req2)) = (msg1, msg2) {
|
||||
assert_ne!(req1.id, req2.id);
|
||||
assert!(req1.id.is_string());
|
||||
assert!(req2.id.is_string());
|
||||
(req1, req2)
|
||||
} else {
|
||||
panic!("Non-request received");
|
||||
};
|
||||
let id1 = req1.id.clone();
|
||||
// When we answer a message, we get the same ID
|
||||
if let Message::Response(resp) = req1.reply(json!([1, 2, 3])) {
|
||||
assert_eq!(
|
||||
resp,
|
||||
Response {
|
||||
jsonrpc: Version,
|
||||
result: Ok(json!([1, 2, 3])),
|
||||
id: id1
|
||||
}
|
||||
);
|
||||
} else {
|
||||
panic!("Not a response");
|
||||
}
|
||||
let id2 = req2.id.clone();
|
||||
// The same with an error
|
||||
if let Message::Response(resp) = req2.error(RpcError::new(42, "Wrong!".to_owned(), None)) {
|
||||
assert_eq!(
|
||||
resp,
|
||||
Response {
|
||||
jsonrpc: Version,
|
||||
result: Err(RpcError::new(42, "Wrong!".to_owned(), None)),
|
||||
id: id2,
|
||||
}
|
||||
);
|
||||
} else {
|
||||
panic!("Not a response");
|
||||
}
|
||||
// When we have unmatched, we generate a top-level error with Null id.
|
||||
if let Message::Response(resp) =
|
||||
Message::error(RpcError::new(43, "Also wrong!".to_owned(), None))
|
||||
{
|
||||
assert_eq!(
|
||||
resp,
|
||||
Response {
|
||||
jsonrpc: Version,
|
||||
result: Err(RpcError::new(43, "Also wrong!".to_owned(), None)),
|
||||
id: Value::Null,
|
||||
}
|
||||
);
|
||||
} else {
|
||||
panic!("Not a response");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,57 +0,0 @@
|
||||
use bytesize::ByteSize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub mod errors;
|
||||
pub mod message;
|
||||
pub mod parser;
|
||||
pub mod requests;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct RpcLimitsConfig {
|
||||
/// Maximum byte size of the json payload.
|
||||
pub json_payload_max_size: ByteSize,
|
||||
}
|
||||
|
||||
impl Default for RpcLimitsConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
json_payload_max_size: ByteSize::mib(10),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct RpcConfig {
|
||||
pub addr: String,
|
||||
pub cors_allowed_origins: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub limits_config: RpcLimitsConfig,
|
||||
}
|
||||
|
||||
impl Default for RpcConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
addr: "0.0.0.0:3040".to_owned(),
|
||||
cors_allowed_origins: vec!["*".to_owned()],
|
||||
limits_config: RpcLimitsConfig::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RpcConfig {
|
||||
#[must_use]
|
||||
pub fn new(addr: &str) -> Self {
|
||||
Self {
|
||||
addr: addr.to_owned(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_port(port: u16) -> Self {
|
||||
Self {
|
||||
addr: format!("0.0.0.0:{port}"),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde_json::Value;
|
||||
|
||||
use super::errors::RpcParseError;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! parse_request {
|
||||
($request_name:ty) => {
|
||||
impl RpcRequest for $request_name {
|
||||
fn parse(value: Option<Value>) -> Result<Self, RpcParseError> {
|
||||
parse_params::<Self>(value)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub trait RpcRequest: Sized {
|
||||
fn parse(value: Option<Value>) -> Result<Self, RpcParseError>;
|
||||
}
|
||||
|
||||
pub fn parse_params<T: DeserializeOwned>(value: Option<Value>) -> Result<T, RpcParseError> {
|
||||
value.map_or_else(
|
||||
|| Err(RpcParseError("Require at least one parameter".to_owned())),
|
||||
|value| {
|
||||
serde_json::from_value(value)
|
||||
.map_err(|err| RpcParseError(format!("Failed parsing args: {err}")))
|
||||
},
|
||||
)
|
||||
}
|
||||
@ -1,219 +0,0 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use nssa::AccountId;
|
||||
use nssa_core::program::ProgramId;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
use super::{
|
||||
errors::RpcParseError,
|
||||
parser::{RpcRequest, parse_params},
|
||||
};
|
||||
use crate::{HashType, parse_request};
|
||||
|
||||
mod base64_deser {
|
||||
use base64::{Engine as _, engine::general_purpose};
|
||||
use serde::{self, Deserialize, Deserializer, Serializer, ser::SerializeSeq as _};
|
||||
|
||||
pub mod vec {
|
||||
use super::*;
|
||||
|
||||
pub fn serialize<S>(bytes_vec: &[Vec<u8>], serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut seq = serializer.serialize_seq(Some(bytes_vec.len()))?;
|
||||
for bytes in bytes_vec {
|
||||
let s = general_purpose::STANDARD.encode(bytes);
|
||||
seq.serialize_element(&s)?;
|
||||
}
|
||||
seq.end()
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<Vec<u8>>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let base64_strings: Vec<String> = Deserialize::deserialize(deserializer)?;
|
||||
base64_strings
|
||||
.into_iter()
|
||||
.map(|s| {
|
||||
general_purpose::STANDARD
|
||||
.decode(&s)
|
||||
.map_err(serde::de::Error::custom)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn serialize<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let base64_string = general_purpose::STANDARD.encode(bytes);
|
||||
serializer.serialize_str(&base64_string)
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let base64_string: String = Deserialize::deserialize(deserializer)?;
|
||||
general_purpose::STANDARD
|
||||
.decode(&base64_string)
|
||||
.map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct HelloRequest;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct RegisterAccountRequest {
|
||||
pub account_id: [u8; 32],
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct SendTxRequest {
|
||||
#[serde(with = "base64_deser")]
|
||||
pub transaction: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetBlockDataRequest {
|
||||
pub block_id: u64,
|
||||
}
|
||||
|
||||
/// Get a range of blocks from `start_block_id` to `end_block_id` (inclusive).
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetBlockRangeDataRequest {
|
||||
pub start_block_id: u64,
|
||||
pub end_block_id: u64,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetGenesisIdRequest;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetLastBlockRequest;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetInitialTestnetAccountsRequest;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetAccountBalanceRequest {
|
||||
pub account_id: AccountId,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetTransactionByHashRequest {
|
||||
pub hash: HashType,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetAccountsNoncesRequest {
|
||||
pub account_ids: Vec<AccountId>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetAccountRequest {
|
||||
pub account_id: AccountId,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetProofForCommitmentRequest {
|
||||
pub commitment: nssa_core::Commitment,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetProgramIdsRequest;
|
||||
|
||||
parse_request!(HelloRequest);
|
||||
parse_request!(RegisterAccountRequest);
|
||||
parse_request!(SendTxRequest);
|
||||
parse_request!(GetBlockDataRequest);
|
||||
parse_request!(GetBlockRangeDataRequest);
|
||||
parse_request!(GetGenesisIdRequest);
|
||||
parse_request!(GetLastBlockRequest);
|
||||
parse_request!(GetInitialTestnetAccountsRequest);
|
||||
parse_request!(GetAccountBalanceRequest);
|
||||
parse_request!(GetTransactionByHashRequest);
|
||||
parse_request!(GetAccountsNoncesRequest);
|
||||
parse_request!(GetProofForCommitmentRequest);
|
||||
parse_request!(GetAccountRequest);
|
||||
parse_request!(GetProgramIdsRequest);
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct HelloResponse {
|
||||
pub greeting: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct RegisterAccountResponse {
|
||||
pub status: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct SendTxResponse {
|
||||
pub status: String,
|
||||
pub tx_hash: HashType,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetBlockDataResponse {
|
||||
#[serde(with = "base64_deser")]
|
||||
pub block: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetBlockRangeDataResponse {
|
||||
#[serde(with = "base64_deser::vec")]
|
||||
pub blocks: Vec<Vec<u8>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetGenesisIdResponse {
|
||||
pub genesis_id: u64,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetLastBlockResponse {
|
||||
pub last_block: u64,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetAccountBalanceResponse {
|
||||
pub balance: u128,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetAccountsNoncesResponse {
|
||||
pub nonces: Vec<u128>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetTransactionByHashResponse {
|
||||
pub transaction: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetAccountResponse {
|
||||
pub account: nssa::Account,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetProofForCommitmentResponse {
|
||||
pub membership_proof: Option<nssa_core::MembershipProof>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetProgramIdsResponse {
|
||||
pub program_ids: HashMap<String, ProgramId>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct GetInitialTestnetAccountsResponse {
|
||||
/// Hex encoded account id.
|
||||
pub account_id: String,
|
||||
pub balance: u64,
|
||||
}
|
||||
@ -1,361 +0,0 @@
|
||||
use std::{collections::HashMap, ops::RangeInclusive};
|
||||
|
||||
use anyhow::Result;
|
||||
use nssa::AccountId;
|
||||
use nssa_core::program::ProgramId;
|
||||
use reqwest::Client;
|
||||
use serde::Deserialize;
|
||||
use serde_json::Value;
|
||||
use url::Url;
|
||||
|
||||
use super::rpc_primitives::requests::{
|
||||
GetAccountBalanceRequest, GetAccountBalanceResponse, GetBlockDataRequest, GetBlockDataResponse,
|
||||
GetGenesisIdRequest, GetGenesisIdResponse, GetInitialTestnetAccountsRequest,
|
||||
};
|
||||
use crate::{
|
||||
HashType,
|
||||
config::BasicAuth,
|
||||
error::{SequencerClientError, SequencerRpcError},
|
||||
rpc_primitives::{
|
||||
self,
|
||||
requests::{
|
||||
GetAccountRequest, GetAccountResponse, GetAccountsNoncesRequest,
|
||||
GetAccountsNoncesResponse, GetBlockRangeDataRequest, GetBlockRangeDataResponse,
|
||||
GetInitialTestnetAccountsResponse, GetLastBlockRequest, GetLastBlockResponse,
|
||||
GetProgramIdsRequest, GetProgramIdsResponse, GetProofForCommitmentRequest,
|
||||
GetProofForCommitmentResponse, GetTransactionByHashRequest,
|
||||
GetTransactionByHashResponse, SendTxRequest, SendTxResponse,
|
||||
},
|
||||
},
|
||||
transaction::NSSATransaction,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
struct SequencerRpcResponse {
|
||||
#[serde(rename = "jsonrpc")]
|
||||
_jsonrpc: String,
|
||||
result: serde_json::Value,
|
||||
#[serde(rename = "id")]
|
||||
_id: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SequencerClient {
|
||||
pub client: reqwest::Client,
|
||||
pub sequencer_addr: Url,
|
||||
pub basic_auth: Option<BasicAuth>,
|
||||
}
|
||||
|
||||
impl SequencerClient {
|
||||
pub fn new(sequencer_addr: Url) -> Result<Self> {
|
||||
Self::new_with_auth(sequencer_addr, None)
|
||||
}
|
||||
|
||||
pub fn new_with_auth(sequencer_addr: Url, basic_auth: Option<BasicAuth>) -> Result<Self> {
|
||||
Ok(Self {
|
||||
client: Client::builder()
|
||||
// Add more fields if needed
|
||||
.timeout(std::time::Duration::from_secs(60))
|
||||
// Should be kept in sync with server keep-alive settings
|
||||
.pool_idle_timeout(std::time::Duration::from_secs(5))
|
||||
.build()?,
|
||||
sequencer_addr,
|
||||
basic_auth,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn call_method_with_payload(
|
||||
&self,
|
||||
method: &str,
|
||||
payload: Value,
|
||||
) -> Result<Value, SequencerClientError> {
|
||||
let request =
|
||||
rpc_primitives::message::Request::from_payload_version_2_0(method.to_owned(), payload);
|
||||
|
||||
log::debug!(
|
||||
"Calling method {method} with payload {request:?} to sequencer at {}",
|
||||
self.sequencer_addr
|
||||
);
|
||||
|
||||
let strategy = tokio_retry::strategy::FixedInterval::from_millis(10000).take(60);
|
||||
|
||||
let response_vall = tokio_retry::Retry::spawn(strategy, || async {
|
||||
let mut call_builder = self.client.post(self.sequencer_addr.clone());
|
||||
|
||||
if let Some(BasicAuth { username, password }) = &self.basic_auth {
|
||||
call_builder = call_builder.basic_auth(username, password.as_deref());
|
||||
}
|
||||
|
||||
let call_res_res = call_builder.json(&request).send().await;
|
||||
|
||||
match call_res_res {
|
||||
Err(err) => Err(err),
|
||||
Ok(call_res) => call_res.json::<Value>().await,
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
|
||||
if let Ok(response) = serde_json::from_value::<SequencerRpcResponse>(response_vall.clone())
|
||||
{
|
||||
Ok(response.result)
|
||||
} else {
|
||||
let err_resp = serde_json::from_value::<SequencerRpcError>(response_vall)?;
|
||||
|
||||
Err(err_resp.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Get block data at `block_id` from sequencer.
|
||||
pub async fn get_block(
|
||||
&self,
|
||||
block_id: u64,
|
||||
) -> Result<GetBlockDataResponse, SequencerClientError> {
|
||||
let block_req = GetBlockDataRequest { block_id };
|
||||
|
||||
let req = serde_json::to_value(block_req)?;
|
||||
|
||||
let resp = self.call_method_with_payload("get_block", req).await?;
|
||||
|
||||
let resp_deser = serde_json::from_value(resp)?;
|
||||
|
||||
Ok(resp_deser)
|
||||
}
|
||||
|
||||
pub async fn get_block_range(
|
||||
&self,
|
||||
range: RangeInclusive<u64>,
|
||||
) -> Result<GetBlockRangeDataResponse, SequencerClientError> {
|
||||
let block_req = GetBlockRangeDataRequest {
|
||||
start_block_id: *range.start(),
|
||||
end_block_id: *range.end(),
|
||||
};
|
||||
|
||||
let req = serde_json::to_value(block_req)?;
|
||||
|
||||
let resp = self
|
||||
.call_method_with_payload("get_block_range", req)
|
||||
.await?;
|
||||
|
||||
let resp_deser = serde_json::from_value(resp)?;
|
||||
|
||||
Ok(resp_deser)
|
||||
}
|
||||
|
||||
/// Get last known `blokc_id` from sequencer.
|
||||
pub async fn get_last_block(&self) -> Result<GetLastBlockResponse, SequencerClientError> {
|
||||
let block_req = GetLastBlockRequest {};
|
||||
|
||||
let req = serde_json::to_value(block_req)?;
|
||||
|
||||
let resp = self.call_method_with_payload("get_last_block", req).await?;
|
||||
|
||||
let resp_deser = serde_json::from_value(resp)?;
|
||||
|
||||
Ok(resp_deser)
|
||||
}
|
||||
|
||||
/// Get account public balance for `account_id`. `account_id` must be a valid hex-string for 32
|
||||
/// bytes.
|
||||
pub async fn get_account_balance(
|
||||
&self,
|
||||
account_id: AccountId,
|
||||
) -> Result<GetAccountBalanceResponse, SequencerClientError> {
|
||||
let block_req = GetAccountBalanceRequest { account_id };
|
||||
|
||||
let req = serde_json::to_value(block_req)?;
|
||||
|
||||
let resp = self
|
||||
.call_method_with_payload("get_account_balance", req)
|
||||
.await?;
|
||||
|
||||
let resp_deser = serde_json::from_value(resp)?;
|
||||
|
||||
Ok(resp_deser)
|
||||
}
|
||||
|
||||
/// Get accounts nonces for `account_ids`. `account_ids` must be a list of valid hex-strings for
|
||||
/// 32 bytes.
|
||||
pub async fn get_accounts_nonces(
|
||||
&self,
|
||||
account_ids: Vec<AccountId>,
|
||||
) -> Result<GetAccountsNoncesResponse, SequencerClientError> {
|
||||
let block_req = GetAccountsNoncesRequest { account_ids };
|
||||
|
||||
let req = serde_json::to_value(block_req)?;
|
||||
|
||||
let resp = self
|
||||
.call_method_with_payload("get_accounts_nonces", req)
|
||||
.await?;
|
||||
|
||||
let resp_deser = serde_json::from_value(resp)?;
|
||||
|
||||
Ok(resp_deser)
|
||||
}
|
||||
|
||||
pub async fn get_account(
|
||||
&self,
|
||||
account_id: AccountId,
|
||||
) -> Result<GetAccountResponse, SequencerClientError> {
|
||||
let block_req = GetAccountRequest { account_id };
|
||||
|
||||
let req = serde_json::to_value(block_req)?;
|
||||
|
||||
let resp = self.call_method_with_payload("get_account", req).await?;
|
||||
|
||||
let resp_deser = serde_json::from_value(resp)?;
|
||||
|
||||
Ok(resp_deser)
|
||||
}
|
||||
|
||||
/// Get transaction details for `hash`.
|
||||
pub async fn get_transaction_by_hash(
|
||||
&self,
|
||||
hash: HashType,
|
||||
) -> Result<GetTransactionByHashResponse, SequencerClientError> {
|
||||
let block_req = GetTransactionByHashRequest { hash };
|
||||
|
||||
let req = serde_json::to_value(block_req)?;
|
||||
|
||||
let resp = self
|
||||
.call_method_with_payload("get_transaction_by_hash", req)
|
||||
.await?;
|
||||
|
||||
let resp_deser = serde_json::from_value(resp)?;
|
||||
|
||||
Ok(resp_deser)
|
||||
}
|
||||
|
||||
/// Send transaction to sequencer.
|
||||
pub async fn send_tx_public(
|
||||
&self,
|
||||
transaction: nssa::PublicTransaction,
|
||||
) -> Result<SendTxResponse, SequencerClientError> {
|
||||
let transaction = NSSATransaction::Public(transaction);
|
||||
|
||||
let tx_req = SendTxRequest {
|
||||
transaction: borsh::to_vec(&transaction).unwrap(),
|
||||
};
|
||||
|
||||
let req = serde_json::to_value(tx_req)?;
|
||||
|
||||
let resp = self.call_method_with_payload("send_tx", req).await?;
|
||||
|
||||
let resp_deser = serde_json::from_value(resp)?;
|
||||
|
||||
Ok(resp_deser)
|
||||
}
|
||||
|
||||
/// Send transaction to sequencer.
|
||||
pub async fn send_tx_private(
|
||||
&self,
|
||||
transaction: nssa::PrivacyPreservingTransaction,
|
||||
) -> Result<SendTxResponse, SequencerClientError> {
|
||||
let transaction = NSSATransaction::PrivacyPreserving(transaction);
|
||||
|
||||
let tx_req = SendTxRequest {
|
||||
transaction: borsh::to_vec(&transaction).unwrap(),
|
||||
};
|
||||
|
||||
let req = serde_json::to_value(tx_req)?;
|
||||
|
||||
let resp = self.call_method_with_payload("send_tx", req).await?;
|
||||
|
||||
let resp_deser = serde_json::from_value(resp)?;
|
||||
|
||||
Ok(resp_deser)
|
||||
}
|
||||
|
||||
/// Get genesis id from sequencer.
|
||||
pub async fn get_genesis_id(&self) -> Result<GetGenesisIdResponse, SequencerClientError> {
|
||||
let genesis_req = GetGenesisIdRequest {};
|
||||
|
||||
let req = serde_json::to_value(genesis_req).unwrap();
|
||||
|
||||
let resp = self
|
||||
.call_method_with_payload("get_genesis", req)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let resp_deser = serde_json::from_value(resp).unwrap();
|
||||
|
||||
Ok(resp_deser)
|
||||
}
|
||||
|
||||
/// Get initial testnet accounts from sequencer.
|
||||
pub async fn get_initial_testnet_accounts(
|
||||
&self,
|
||||
) -> Result<Vec<GetInitialTestnetAccountsResponse>, SequencerClientError> {
|
||||
let acc_req = GetInitialTestnetAccountsRequest {};
|
||||
|
||||
let req = serde_json::to_value(acc_req).unwrap();
|
||||
|
||||
let resp = self
|
||||
.call_method_with_payload("get_initial_testnet_accounts", req)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let resp_deser = serde_json::from_value(resp).unwrap();
|
||||
|
||||
Ok(resp_deser)
|
||||
}
|
||||
|
||||
/// Get proof for commitment.
|
||||
pub async fn get_proof_for_commitment(
|
||||
&self,
|
||||
commitment: nssa_core::Commitment,
|
||||
) -> Result<Option<nssa_core::MembershipProof>, SequencerClientError> {
|
||||
let acc_req = GetProofForCommitmentRequest { commitment };
|
||||
|
||||
let req = serde_json::to_value(acc_req).unwrap();
|
||||
|
||||
let resp = self
|
||||
.call_method_with_payload("get_proof_for_commitment", req)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let resp_deser = serde_json::from_value::<GetProofForCommitmentResponse>(resp)
|
||||
.unwrap()
|
||||
.membership_proof;
|
||||
|
||||
Ok(resp_deser)
|
||||
}
|
||||
|
||||
pub async fn send_tx_program(
|
||||
&self,
|
||||
transaction: nssa::ProgramDeploymentTransaction,
|
||||
) -> Result<SendTxResponse, SequencerClientError> {
|
||||
let transaction = NSSATransaction::ProgramDeployment(transaction);
|
||||
|
||||
let tx_req = SendTxRequest {
|
||||
transaction: borsh::to_vec(&transaction).unwrap(),
|
||||
};
|
||||
|
||||
let req = serde_json::to_value(tx_req)?;
|
||||
|
||||
let resp = self.call_method_with_payload("send_tx", req).await?;
|
||||
|
||||
let resp_deser = serde_json::from_value(resp)?;
|
||||
|
||||
Ok(resp_deser)
|
||||
}
|
||||
|
||||
/// Get Ids of the programs used by the node.
|
||||
pub async fn get_program_ids(
|
||||
&self,
|
||||
) -> Result<HashMap<String, ProgramId>, SequencerClientError> {
|
||||
let acc_req = GetProgramIdsRequest {};
|
||||
|
||||
let req = serde_json::to_value(acc_req).unwrap();
|
||||
|
||||
let resp = self
|
||||
.call_method_with_payload("get_program_ids", req)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let resp_deser = serde_json::from_value::<GetProgramIdsResponse>(resp)
|
||||
.unwrap()
|
||||
.program_ids;
|
||||
|
||||
Ok(resp_deser)
|
||||
}
|
||||
}
|
||||
@ -68,7 +68,7 @@ pub fn create_transaction_native_token_transfer(
|
||||
signing_key: &nssa::PrivateKey,
|
||||
) -> NSSATransaction {
|
||||
let account_ids = vec![from, to];
|
||||
let nonces = vec![nonce];
|
||||
let nonces = vec![nonce.into()];
|
||||
let program_id = nssa::program::Program::authenticated_transfer_program().id();
|
||||
let message = nssa::public_transaction::Message::try_new(
|
||||
program_id,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use log::warn;
|
||||
use nssa::{AccountId, V02State};
|
||||
use nssa::{AccountId, V03State};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::HashType;
|
||||
@ -12,6 +12,18 @@ pub enum NSSATransaction {
|
||||
ProgramDeployment(nssa::ProgramDeploymentTransaction),
|
||||
}
|
||||
|
||||
impl Serialize for NSSATransaction {
|
||||
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
crate::borsh_base64::serialize(self, serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for NSSATransaction {
|
||||
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
crate::borsh_base64::deserialize(deserializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl NSSATransaction {
|
||||
#[must_use]
|
||||
pub fn hash(&self) -> HashType {
|
||||
@ -55,7 +67,7 @@ impl NSSATransaction {
|
||||
|
||||
pub fn execute_check_on_state(
|
||||
self,
|
||||
state: &mut V02State,
|
||||
state: &mut V03State,
|
||||
) -> Result<Self, nssa::error::NssaError> {
|
||||
match &self {
|
||||
Self::Public(tx) => state.transition_from_public_transaction(tx),
|
||||
@ -87,7 +99,7 @@ impl From<nssa::ProgramDeploymentTransaction> for NSSATransaction {
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, BorshSerialize, BorshDeserialize,
|
||||
Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, BorshSerialize, BorshDeserialize,
|
||||
)]
|
||||
pub enum TxKind {
|
||||
Public,
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
{
|
||||
"home": "/var/lib/sequencer_runner",
|
||||
"override_rust_log": null,
|
||||
"home": "/var/lib/sequencer_service",
|
||||
"genesis_id": 1,
|
||||
"is_genesis_random": true,
|
||||
"max_num_tx_in_block": 20,
|
||||
@ -8,7 +7,6 @@
|
||||
"mempool_max_size": 10000,
|
||||
"block_create_timeout": "10s",
|
||||
"retry_pending_blocks_timeout": "7s",
|
||||
"port": 3040,
|
||||
"bedrock_config": {
|
||||
"backoff": {
|
||||
"start_delay": "100ms",
|
||||
@ -7,21 +7,21 @@ services:
|
||||
environment:
|
||||
- RUST_LOG=error
|
||||
|
||||
sequencer_runner:
|
||||
sequencer_service:
|
||||
depends_on:
|
||||
- logos-blockchain-node-0
|
||||
- indexer_service
|
||||
volumes: !override
|
||||
- ./configs/docker-all-in-one/sequencer:/etc/sequencer_runner
|
||||
volumes:
|
||||
- ./configs/docker-all-in-one/sequencer_config.json:/etc/sequencer_service/sequencer_config.json
|
||||
|
||||
indexer_service:
|
||||
depends_on:
|
||||
- logos-blockchain-node-0
|
||||
volumes:
|
||||
- ./configs/docker-all-in-one/indexer/indexer_config.json:/etc/indexer_service/indexer_config.json
|
||||
- ./configs/docker-all-in-one/indexer_config.json:/etc/indexer_service/indexer_config.json
|
||||
|
||||
explorer_service:
|
||||
depends_on:
|
||||
- indexer_service
|
||||
environment:
|
||||
- INDEXER_RPC_URL=http://indexer_service:8779
|
||||
- INDEXER_RPC_URL=http://indexer_service:8779
|
||||
|
||||
@ -6,7 +6,7 @@ include:
|
||||
- path:
|
||||
bedrock/docker-compose.yml
|
||||
- path:
|
||||
sequencer_runner/docker-compose.yml
|
||||
sequencer/service/docker-compose.yml
|
||||
- path:
|
||||
indexer/service/docker-compose.yml
|
||||
- path:
|
||||
|
||||
@ -8,8 +8,10 @@ license = { workspace = true }
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
common.workspace = true
|
||||
nssa.workspace = true
|
||||
nssa_core.workspace = true
|
||||
sequencer_service_rpc = { workspace = true, features = ["client"] }
|
||||
wallet.workspace = true
|
||||
|
||||
tokio = { workspace = true, features = ["macros"] }
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
use common::transaction::NSSATransaction;
|
||||
use nssa::{
|
||||
AccountId, PublicTransaction,
|
||||
program::Program,
|
||||
public_transaction::{Message, WitnessSet},
|
||||
};
|
||||
use sequencer_service_rpc::RpcClient as _;
|
||||
use wallet::WalletCore;
|
||||
|
||||
// Before running this example, compile the `hello_world.rs` guest program with:
|
||||
@ -58,7 +60,7 @@ async fn main() {
|
||||
// Submit the transaction
|
||||
let _response = wallet_core
|
||||
.sequencer_client
|
||||
.send_tx_public(tx)
|
||||
.send_transaction(NSSATransaction::Public(tx))
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
use common::transaction::NSSATransaction;
|
||||
use nssa::{
|
||||
AccountId, PublicTransaction,
|
||||
program::Program,
|
||||
public_transaction::{Message, WitnessSet},
|
||||
};
|
||||
use sequencer_service_rpc::RpcClient as _;
|
||||
use wallet::WalletCore;
|
||||
|
||||
// Before running this example, compile the `simple_tail_call.rs` guest program with:
|
||||
@ -54,7 +56,7 @@ async fn main() {
|
||||
// Submit the transaction
|
||||
let _response = wallet_core
|
||||
.sequencer_client
|
||||
.send_tx_public(tx)
|
||||
.send_transaction(NSSATransaction::Public(tx))
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
use common::transaction::NSSATransaction;
|
||||
use nssa::{
|
||||
AccountId, PublicTransaction,
|
||||
program::Program,
|
||||
public_transaction::{Message, WitnessSet},
|
||||
};
|
||||
use sequencer_service_rpc::RpcClient as _;
|
||||
use wallet::WalletCore;
|
||||
|
||||
// Before running this example, compile the `hello_world_with_authorization.rs` guest program with:
|
||||
@ -71,7 +73,7 @@ async fn main() {
|
||||
// Submit the transaction
|
||||
let _response = wallet_core
|
||||
.sequencer_client
|
||||
.send_tx_public(tx)
|
||||
.send_transaction(NSSATransaction::Public(tx))
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
@ -3,12 +3,14 @@
|
||||
reason = "This is an example program, it's fine to print to stdout"
|
||||
)]
|
||||
|
||||
use common::transaction::NSSATransaction;
|
||||
use nssa::{
|
||||
AccountId, PublicTransaction,
|
||||
program::Program,
|
||||
public_transaction::{Message, WitnessSet},
|
||||
};
|
||||
use nssa_core::program::PdaSeed;
|
||||
use sequencer_service_rpc::RpcClient as _;
|
||||
use wallet::WalletCore;
|
||||
|
||||
// Before running this example, compile the `simple_tail_call.rs` guest program with:
|
||||
@ -56,7 +58,7 @@ async fn main() {
|
||||
// Submit the transaction
|
||||
let _response = wallet_core
|
||||
.sequencer_client
|
||||
.send_tx_public(tx)
|
||||
.send_transaction(NSSATransaction::Public(tx))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
use clap::{Parser, Subcommand};
|
||||
use common::transaction::NSSATransaction;
|
||||
use nssa::{PublicTransaction, program::Program, public_transaction};
|
||||
use sequencer_service_rpc::RpcClient as _;
|
||||
use wallet::{PrivacyPreservingAccount, WalletCore};
|
||||
|
||||
// Before running this example, compile the `hello_world_with_move_function.rs` guest program with:
|
||||
@ -87,7 +89,7 @@ async fn main() {
|
||||
// Submit the transaction
|
||||
let _response = wallet_core
|
||||
.sequencer_client
|
||||
.send_tx_public(tx)
|
||||
.send_transaction(NSSATransaction::Public(tx))
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
@ -126,7 +128,7 @@ async fn main() {
|
||||
// Submit the transaction
|
||||
let _response = wallet_core
|
||||
.sequencer_client
|
||||
.send_tx_public(tx)
|
||||
.send_transaction(NSSATransaction::Public(tx))
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
@ -22,7 +22,13 @@ WORKDIR /explorer_service
|
||||
COPY . .
|
||||
|
||||
# Build the app
|
||||
RUN cargo leptos build --release -vv
|
||||
RUN --mount=type=cache,target=/usr/local/cargo/registry/index \
|
||||
--mount=type=cache,target=/usr/local/cargo/registry/cache \
|
||||
--mount=type=cache,target=/usr/local/cargo/git \
|
||||
--mount=type=cache,target=/explorer_service/target \
|
||||
cargo leptos build --release -vv \
|
||||
&& cp /explorer_service/target/release/explorer_service /usr/local/bin/explorer_service \
|
||||
&& cp -r /explorer_service/target/site /explorer_service/site_output
|
||||
|
||||
FROM debian:trixie-slim AS runtime
|
||||
WORKDIR /explorer_service
|
||||
@ -33,10 +39,10 @@ RUN apt-get update -y \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Copy the server binary to the /explorer_service directory
|
||||
COPY --from=builder /explorer_service/target/release/explorer_service /explorer_service/
|
||||
COPY --from=builder /usr/local/bin/explorer_service /explorer_service/
|
||||
|
||||
# /target/site contains our JS/WASM/CSS, etc.
|
||||
COPY --from=builder /explorer_service/target/site /explorer_service/site
|
||||
COPY --from=builder /explorer_service/site_output /explorer_service/site
|
||||
|
||||
# Copy Cargo.toml as it’s needed at runtime
|
||||
COPY --from=builder /explorer_service/Cargo.toml /explorer_service/
|
||||
|
||||
@ -41,12 +41,12 @@ pub async fn search(query: String) -> Result<SearchResults, ServerFnError> {
|
||||
// Try as hash
|
||||
if let Ok(hash) = HashType::from_str(&query) {
|
||||
// Try as block hash
|
||||
if let Ok(block) = client.get_block_by_hash(hash).await {
|
||||
if let Ok(Some(block)) = client.get_block_by_hash(hash).await {
|
||||
blocks.push(block);
|
||||
}
|
||||
|
||||
// Try as transaction hash
|
||||
if let Ok(tx) = client.get_transaction(hash).await {
|
||||
if let Ok(Some(tx)) = client.get_transaction(hash).await {
|
||||
transactions.push(tx);
|
||||
}
|
||||
}
|
||||
@ -60,7 +60,7 @@ pub async fn search(query: String) -> Result<SearchResults, ServerFnError> {
|
||||
|
||||
// Try as block ID
|
||||
if let Ok(block_id) = query.parse::<u64>()
|
||||
&& let Ok(block) = client.get_block_by_id(block_id).await
|
||||
&& let Ok(Some(block)) = client.get_block_by_id(block_id).await
|
||||
{
|
||||
blocks.push(block);
|
||||
}
|
||||
@ -81,6 +81,7 @@ pub async fn get_block_by_id(block_id: BlockId) -> Result<Block, ServerFnError>
|
||||
.get_block_by_id(block_id)
|
||||
.await
|
||||
.map_err(|e| ServerFnError::ServerError(format!("RPC error: {e}")))
|
||||
.and_then(|opt| opt.ok_or_else(|| ServerFnError::ServerError("Block not found".to_owned())))
|
||||
}
|
||||
|
||||
/// Get latest block ID
|
||||
@ -103,6 +104,7 @@ pub async fn get_block_by_hash(block_hash: HashType) -> Result<Block, ServerFnEr
|
||||
.get_block_by_hash(block_hash)
|
||||
.await
|
||||
.map_err(|e| ServerFnError::ServerError(format!("RPC error: {e}")))
|
||||
.and_then(|opt| opt.ok_or_else(|| ServerFnError::ServerError("Block not found".to_owned())))
|
||||
}
|
||||
|
||||
/// Get transaction by hash
|
||||
@ -114,6 +116,9 @@ pub async fn get_transaction(tx_hash: HashType) -> Result<Transaction, ServerFnE
|
||||
.get_transaction(tx_hash)
|
||||
.await
|
||||
.map_err(|e| ServerFnError::ServerError(format!("RPC error: {e}")))
|
||||
.and_then(|opt| {
|
||||
opt.ok_or_else(|| ServerFnError::ServerError("Transaction not found".to_owned()))
|
||||
})
|
||||
}
|
||||
|
||||
/// Get blocks with pagination
|
||||
|
||||
@ -84,7 +84,7 @@ pub fn TransactionPage() -> impl IntoView {
|
||||
} = witness_set;
|
||||
|
||||
let program_id_str = program_id.to_string();
|
||||
let proof_len = proof.0.len();
|
||||
let proof_len = proof.map_or(0, |p| p.0.len());
|
||||
let signatures_count = signatures_and_public_keys.len();
|
||||
|
||||
view! {
|
||||
@ -183,7 +183,7 @@ pub fn TransactionPage() -> impl IntoView {
|
||||
proof,
|
||||
} = witness_set;
|
||||
|
||||
let proof_len = proof.0.len();
|
||||
let proof_len = proof.map_or(0, |p| p.0.len());
|
||||
view! {
|
||||
<div class="transaction-details">
|
||||
<h2>"Privacy-Preserving Transaction Details"</h2>
|
||||
|
||||
@ -28,4 +28,3 @@ async-stream.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile.workspace = true
|
||||
|
||||
|
||||
@ -6,14 +6,14 @@ use common::{
|
||||
block::{BedrockStatus, Block, BlockId},
|
||||
transaction::NSSATransaction,
|
||||
};
|
||||
use nssa::{Account, AccountId, V02State};
|
||||
use nssa::{Account, AccountId, V03State};
|
||||
use storage::indexer::RocksDBIO;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct IndexerStore {
|
||||
dbio: Arc<RocksDBIO>,
|
||||
current_state: Arc<RwLock<V02State>>,
|
||||
current_state: Arc<RwLock<V03State>>,
|
||||
}
|
||||
|
||||
impl IndexerStore {
|
||||
@ -24,7 +24,7 @@ impl IndexerStore {
|
||||
pub fn open_db_with_genesis(
|
||||
location: &Path,
|
||||
genesis_block: &Block,
|
||||
initial_state: &V02State,
|
||||
initial_state: &V03State,
|
||||
) -> Result<Self> {
|
||||
let dbio = RocksDBIO::open_or_create(location, genesis_block, initial_state)?;
|
||||
let current_state = dbio.final_state()?;
|
||||
@ -46,7 +46,7 @@ impl IndexerStore {
|
||||
Ok(self.dbio.get_meta_last_block_in_db()?)
|
||||
}
|
||||
|
||||
pub fn get_block_at_id(&self, id: u64) -> Result<Block> {
|
||||
pub fn get_block_at_id(&self, id: u64) -> Result<Option<Block>> {
|
||||
Ok(self.dbio.get_block(id)?)
|
||||
}
|
||||
|
||||
@ -54,20 +54,25 @@ impl IndexerStore {
|
||||
Ok(self.dbio.get_block_batch(before, limit)?)
|
||||
}
|
||||
|
||||
pub fn get_transaction_by_hash(&self, tx_hash: [u8; 32]) -> Result<NSSATransaction> {
|
||||
let block = self.get_block_at_id(self.dbio.get_block_id_by_tx_hash(tx_hash)?)?;
|
||||
let transaction = block
|
||||
pub fn get_transaction_by_hash(&self, tx_hash: [u8; 32]) -> Result<Option<NSSATransaction>> {
|
||||
let Some(block_id) = self.dbio.get_block_id_by_tx_hash(tx_hash)? else {
|
||||
return Ok(None);
|
||||
};
|
||||
let Some(block) = self.get_block_at_id(block_id)? else {
|
||||
return Ok(None);
|
||||
};
|
||||
Ok(block
|
||||
.body
|
||||
.transactions
|
||||
.iter()
|
||||
.find(|enc_tx| enc_tx.hash().0 == tx_hash)
|
||||
.ok_or_else(|| anyhow::anyhow!("Transaction not found in DB"))?;
|
||||
|
||||
Ok(transaction.clone())
|
||||
.into_iter()
|
||||
.find(|enc_tx| enc_tx.hash().0 == tx_hash))
|
||||
}
|
||||
|
||||
pub fn get_block_by_hash(&self, hash: [u8; 32]) -> Result<Block> {
|
||||
self.get_block_at_id(self.dbio.get_block_id_by_hash(hash)?)
|
||||
pub fn get_block_by_hash(&self, hash: [u8; 32]) -> Result<Option<Block>> {
|
||||
let Some(id) = self.dbio.get_block_id_by_hash(hash)? else {
|
||||
return Ok(None);
|
||||
};
|
||||
self.get_block_at_id(id)
|
||||
}
|
||||
|
||||
pub fn get_transactions_by_account(
|
||||
@ -93,14 +98,14 @@ impl IndexerStore {
|
||||
.expect("Must be set at the DB startup")
|
||||
}
|
||||
|
||||
pub fn get_state_at_block(&self, block_id: u64) -> Result<V02State> {
|
||||
pub fn get_state_at_block(&self, block_id: u64) -> Result<V03State> {
|
||||
Ok(self.dbio.calculate_state_for_id(block_id)?)
|
||||
}
|
||||
|
||||
/// Recalculation of final state directly from DB.
|
||||
///
|
||||
/// Used for indexer healthcheck.
|
||||
pub fn recalculate_final_state(&self) -> Result<V02State> {
|
||||
pub fn recalculate_final_state(&self) -> Result<V03State> {
|
||||
Ok(self.dbio.final_state()?)
|
||||
}
|
||||
|
||||
@ -167,11 +172,11 @@ mod tests {
|
||||
let storage = IndexerStore::open_db_with_genesis(
|
||||
home.as_ref(),
|
||||
&genesis_block(),
|
||||
&nssa::V02State::new_with_genesis_accounts(&[(acc1(), 10000), (acc2(), 20000)], &[]),
|
||||
&nssa::V03State::new_with_genesis_accounts(&[(acc1(), 10000), (acc2(), 20000)], &[]),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let block = storage.get_block_at_id(1).unwrap();
|
||||
let block = storage.get_block_at_id(1).unwrap().unwrap();
|
||||
let final_id = storage.get_last_block_id().unwrap();
|
||||
|
||||
assert_eq!(block.header.hash, genesis_block().header.hash);
|
||||
@ -185,7 +190,7 @@ mod tests {
|
||||
let storage = IndexerStore::open_db_with_genesis(
|
||||
home.as_ref(),
|
||||
&genesis_block(),
|
||||
&nssa::V02State::new_with_genesis_accounts(&[(acc1(), 10000), (acc2(), 20000)], &[]),
|
||||
&nssa::V03State::new_with_genesis_accounts(&[(acc1(), 10000), (acc2(), 20000)], &[]),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
||||
@ -80,7 +80,7 @@ impl IndexerCore {
|
||||
.map(|acc_data| (acc_data.account_id, acc_data.balance))
|
||||
.collect();
|
||||
|
||||
let mut state = nssa::V02State::new_with_genesis_accounts(&init_accs, &initial_commitments);
|
||||
let mut state = nssa::V03State::new_with_genesis_accounts(&init_accs, &initial_commitments);
|
||||
|
||||
// ToDo: Remove after testnet
|
||||
state.add_pinata_program(PINATA_BASE58.parse().unwrap());
|
||||
|
||||
@ -21,7 +21,6 @@ log.workspace = true
|
||||
jsonrpsee.workspace = true
|
||||
serde_json.workspace = true
|
||||
futures.workspace = true
|
||||
async-trait = "0.1.89"
|
||||
arc-swap = "1.8.1"
|
||||
|
||||
[features]
|
||||
|
||||
@ -51,32 +51,34 @@ RUN cargo chef prepare --bin indexer_service --recipe-path recipe.json
|
||||
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
|
||||
RUN --mount=type=cache,target=/usr/local/cargo/registry/index \
|
||||
--mount=type=cache,target=/usr/local/cargo/registry/cache \
|
||||
--mount=type=cache,target=/usr/local/cargo/git \
|
||||
--mount=type=cache,target=/indexer_service/target \
|
||||
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
|
||||
# Build the actual application and copy the binary out of the cache mount
|
||||
RUN --mount=type=cache,target=/usr/local/cargo/registry/index \
|
||||
--mount=type=cache,target=/usr/local/cargo/registry/cache \
|
||||
--mount=type=cache,target=/usr/local/cargo/git \
|
||||
--mount=type=cache,target=/indexer_service/target \
|
||||
cargo build --release --bin indexer_service \
|
||||
&& strip /indexer_service/target/release/indexer_service \
|
||||
&& cp /indexer_service/target/release/indexer_service /usr/local/bin/indexer_service
|
||||
|
||||
# Runtime stage - minimal image
|
||||
FROM debian:trixie-slim
|
||||
|
||||
# Install runtime dependencies
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y gosu jq \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Create non-root user for security
|
||||
RUN useradd -m -u 1000 -s /bin/bash indexer_service_user && \
|
||||
mkdir -p /indexer_service /etc/indexer_service && \
|
||||
chown -R indexer_service_user:indexer_service_user /indexer_service /etc/indexer_service
|
||||
mkdir -p /indexer_service /etc/indexer_service /var/lib/indexer_service && \
|
||||
chown -R indexer_service_user:indexer_service_user /indexer_service /etc/indexer_service /var/lib/indexer_service
|
||||
|
||||
# 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
|
||||
COPY --from=builder --chown=indexer_service_user:indexer_service_user /usr/local/bin/indexer_service /usr/local/bin/indexer_service
|
||||
|
||||
# Copy r0vm binary from builder
|
||||
COPY --from=builder --chown=indexer_service_user:indexer_service_user /usr/local/bin/r0vm /usr/local/bin/r0vm
|
||||
@ -84,9 +86,7 @@ COPY --from=builder --chown=indexer_service_user:indexer_service_user /usr/local
|
||||
# Copy logos blockchain circuits from builder
|
||||
COPY --from=builder --chown=indexer_service_user:indexer_service_user /root/.logos-blockchain-circuits /home/indexer_service_user/.logos-blockchain-circuits
|
||||
|
||||
# Copy entrypoint script
|
||||
COPY indexer/service/docker-entrypoint.sh /docker-entrypoint.sh
|
||||
RUN chmod +x /docker-entrypoint.sh
|
||||
VOLUME /var/lib/indexer_service
|
||||
|
||||
# Expose default port
|
||||
EXPOSE 8779
|
||||
@ -105,9 +105,7 @@ HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
# Run the application
|
||||
ENV RUST_LOG=info
|
||||
|
||||
USER root
|
||||
|
||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||
USER indexer_service_user
|
||||
|
||||
WORKDIR /indexer_service
|
||||
CMD ["indexer_service", "/etc/indexer_service/indexer_config.json"]
|
||||
|
||||
@ -11,50 +11,50 @@
|
||||
"channel_id": "0101010101010101010101010101010101010101010101010101010101010101",
|
||||
"initial_accounts": [
|
||||
{
|
||||
"account_id": "6iArKUXxhUJqS7kCaPNhwMWt3ro71PDyBj7jwAyE2VQV",
|
||||
"account_id": "CbgR6tj5kWx5oziiFptM7jMvrQeYY3Mzaao6ciuhSr2r",
|
||||
"balance": 10000
|
||||
},
|
||||
{
|
||||
"account_id": "7wHg9sbJwc6h3NP1S9bekfAzB8CHifEcxKswCKUt3YQo",
|
||||
"account_id": "2RHZhw9h534Zr3eq2RGhQete2Hh667foECzXPmSkGni2",
|
||||
"balance": 20000
|
||||
}
|
||||
],
|
||||
"initial_commitments": [
|
||||
{
|
||||
"npk":[
|
||||
177,
|
||||
64,
|
||||
1,
|
||||
"npk": [
|
||||
139,
|
||||
19,
|
||||
158,
|
||||
11,
|
||||
87,
|
||||
38,
|
||||
254,
|
||||
159,
|
||||
155,
|
||||
231,
|
||||
165,
|
||||
1,
|
||||
94,
|
||||
64,
|
||||
137,
|
||||
243,
|
||||
76,
|
||||
249,
|
||||
101,
|
||||
251,
|
||||
129,
|
||||
33,
|
||||
101,
|
||||
189,
|
||||
30,
|
||||
42,
|
||||
11,
|
||||
191,
|
||||
34,
|
||||
103,
|
||||
186,
|
||||
227,
|
||||
230
|
||||
] ,
|
||||
85,
|
||||
206,
|
||||
132,
|
||||
228,
|
||||
220,
|
||||
114,
|
||||
145,
|
||||
89,
|
||||
113,
|
||||
156,
|
||||
238,
|
||||
142,
|
||||
242,
|
||||
74,
|
||||
182,
|
||||
91,
|
||||
43,
|
||||
100,
|
||||
6,
|
||||
190,
|
||||
31,
|
||||
15,
|
||||
31,
|
||||
88,
|
||||
96,
|
||||
204
|
||||
],
|
||||
"account": {
|
||||
"program_owner": [
|
||||
0,
|
||||
@ -73,38 +73,38 @@
|
||||
},
|
||||
{
|
||||
"npk": [
|
||||
32,
|
||||
67,
|
||||
72,
|
||||
164,
|
||||
106,
|
||||
53,
|
||||
66,
|
||||
239,
|
||||
141,
|
||||
15,
|
||||
52,
|
||||
230,
|
||||
136,
|
||||
177,
|
||||
2,
|
||||
236,
|
||||
207,
|
||||
243,
|
||||
173,
|
||||
134,
|
||||
135,
|
||||
210,
|
||||
143,
|
||||
87,
|
||||
232,
|
||||
33,
|
||||
223,
|
||||
54,
|
||||
226,
|
||||
10,
|
||||
71,
|
||||
215,
|
||||
128,
|
||||
194,
|
||||
120,
|
||||
113,
|
||||
224,
|
||||
4,
|
||||
165
|
||||
254,
|
||||
143,
|
||||
172,
|
||||
24,
|
||||
244,
|
||||
243,
|
||||
208,
|
||||
65,
|
||||
112,
|
||||
118,
|
||||
70,
|
||||
217,
|
||||
240,
|
||||
69,
|
||||
100,
|
||||
129,
|
||||
3,
|
||||
121,
|
||||
25,
|
||||
213,
|
||||
132,
|
||||
42,
|
||||
45
|
||||
],
|
||||
"account": {
|
||||
"program_owner": [
|
||||
@ -157,4 +157,4 @@
|
||||
37,
|
||||
37
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -10,5 +10,8 @@ services:
|
||||
volumes:
|
||||
# Mount configuration
|
||||
- ./configs/indexer_config.json:/etc/indexer_service/indexer_config.json
|
||||
# Mount data folder
|
||||
- ./data:/var/lib/indexer_service
|
||||
# Mount data volume
|
||||
- indexer_data:/var/lib/indexer_service
|
||||
|
||||
volumes:
|
||||
indexer_data:
|
||||
|
||||
@ -1,29 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This is an entrypoint script for the indexer_service Docker container,
|
||||
# it's not meant to be executed outside of the container.
|
||||
|
||||
set -e
|
||||
|
||||
CONFIG="/etc/indexer_service/indexer_config.json"
|
||||
|
||||
# Check config file exists
|
||||
if [ ! -f "$CONFIG" ]; then
|
||||
echo "Config file not found: $CONFIG" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Parse home dir
|
||||
HOME_DIR=$(jq -r '.home' "$CONFIG")
|
||||
|
||||
if [ -z "$HOME_DIR" ] || [ "$HOME_DIR" = "null" ]; then
|
||||
echo "'home' key missing in config" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Give permissions to the data directory and switch to non-root user
|
||||
if [ "$(id -u)" = "0" ]; then
|
||||
mkdir -p "$HOME_DIR"
|
||||
chown -R indexer_service_user:indexer_service_user "$HOME_DIR"
|
||||
exec gosu indexer_service_user "$@"
|
||||
fi
|
||||
@ -1,5 +1,7 @@
|
||||
//! Conversions between `indexer_service_protocol` types and `nssa/nssa_core` types.
|
||||
|
||||
use nssa_core::account::Nonce;
|
||||
|
||||
use crate::{
|
||||
Account, AccountId, BedrockStatus, Block, BlockBody, BlockHeader, Ciphertext, Commitment,
|
||||
CommitmentSetDigest, Data, EncryptedAccountData, EphemeralPublicKey, HashType, MantleMsgId,
|
||||
@ -52,7 +54,7 @@ impl From<nssa_core::account::Account> for Account {
|
||||
program_owner: program_owner.into(),
|
||||
balance,
|
||||
data: data.into(),
|
||||
nonce,
|
||||
nonce: nonce.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -72,7 +74,7 @@ impl TryFrom<Account> for nssa_core::account::Account {
|
||||
program_owner: program_owner.into(),
|
||||
balance,
|
||||
data: data.try_into()?,
|
||||
nonce,
|
||||
nonce: Nonce(nonce),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -250,7 +252,7 @@ impl From<nssa::public_transaction::Message> for PublicMessage {
|
||||
Self {
|
||||
program_id: program_id.into(),
|
||||
account_ids: account_ids.into_iter().map(Into::into).collect(),
|
||||
nonces,
|
||||
nonces: nonces.iter().map(|x| x.0).collect(),
|
||||
instruction_data,
|
||||
}
|
||||
}
|
||||
@ -267,7 +269,10 @@ impl From<PublicMessage> for nssa::public_transaction::Message {
|
||||
Self::new_preserialized(
|
||||
program_id.into(),
|
||||
account_ids.into_iter().map(Into::into).collect(),
|
||||
nonces,
|
||||
nonces
|
||||
.iter()
|
||||
.map(|x| nssa_core::account::Nonce(*x))
|
||||
.collect(),
|
||||
instruction_data,
|
||||
)
|
||||
}
|
||||
@ -285,7 +290,7 @@ impl From<nssa::privacy_preserving_transaction::message::Message> for PrivacyPre
|
||||
} = value;
|
||||
Self {
|
||||
public_account_ids: public_account_ids.into_iter().map(Into::into).collect(),
|
||||
nonces,
|
||||
nonces: nonces.iter().map(|x| x.0).collect(),
|
||||
public_post_states: public_post_states.into_iter().map(Into::into).collect(),
|
||||
encrypted_private_post_states: encrypted_private_post_states
|
||||
.into_iter()
|
||||
@ -314,7 +319,10 @@ impl TryFrom<PrivacyPreservingMessage> for nssa::privacy_preserving_transaction:
|
||||
} = value;
|
||||
Ok(Self {
|
||||
public_account_ids: public_account_ids.into_iter().map(Into::into).collect(),
|
||||
nonces,
|
||||
nonces: nonces
|
||||
.iter()
|
||||
.map(|x| nssa_core::account::Nonce(*x))
|
||||
.collect(),
|
||||
public_post_states: public_post_states
|
||||
.into_iter()
|
||||
.map(TryInto::try_into)
|
||||
@ -351,12 +359,16 @@ impl From<ProgramDeploymentMessage> for nssa::program_deployment_transaction::Me
|
||||
// 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::public_transaction::WitnessSet> for WitnessSet {
|
||||
fn from(value: nssa::public_transaction::WitnessSet) -> Self {
|
||||
Self {
|
||||
signatures_and_public_keys: value
|
||||
.signatures_and_public_keys()
|
||||
.iter()
|
||||
.map(|(sig, pk)| (sig.clone().into(), pk.clone().into()))
|
||||
.collect(),
|
||||
proof: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -368,7 +380,7 @@ impl From<nssa::privacy_preserving_transaction::witness_set::WitnessSet> for Wit
|
||||
.into_iter()
|
||||
.map(|(sig, pk)| (sig.into(), pk.into()))
|
||||
.collect(),
|
||||
proof: proof.into(),
|
||||
proof: Some(proof.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -388,7 +400,9 @@ impl TryFrom<WitnessSet> for nssa::privacy_preserving_transaction::witness_set::
|
||||
|
||||
Ok(Self::from_raw_parts(
|
||||
signatures_and_public_keys,
|
||||
proof.into(),
|
||||
proof
|
||||
.map(Into::into)
|
||||
.ok_or_else(|| nssa::error::NssaError::InvalidInput("Missing proof".to_owned()))?,
|
||||
))
|
||||
}
|
||||
}
|
||||
@ -408,14 +422,7 @@ impl From<nssa::PublicTransaction> for PublicTransaction {
|
||||
Self {
|
||||
hash,
|
||||
message: message.into(),
|
||||
witness_set: WitnessSet {
|
||||
signatures_and_public_keys: 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
|
||||
},
|
||||
witness_set: witness_set.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -240,7 +240,7 @@ pub struct PrivacyPreservingMessage {
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct WitnessSet {
|
||||
pub signatures_and_public_keys: Vec<(Signature, PublicKey)>,
|
||||
pub proof: Proof,
|
||||
pub proof: Option<Proof>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
|
||||
|
||||
@ -30,16 +30,22 @@ pub trait Rpc {
|
||||
async fn get_last_finalized_block_id(&self) -> Result<BlockId, ErrorObjectOwned>;
|
||||
|
||||
#[method(name = "getBlockById")]
|
||||
async fn get_block_by_id(&self, block_id: BlockId) -> Result<Block, ErrorObjectOwned>;
|
||||
async fn get_block_by_id(&self, block_id: BlockId) -> Result<Option<Block>, ErrorObjectOwned>;
|
||||
|
||||
#[method(name = "getBlockByHash")]
|
||||
async fn get_block_by_hash(&self, block_hash: HashType) -> Result<Block, ErrorObjectOwned>;
|
||||
async fn get_block_by_hash(
|
||||
&self,
|
||||
block_hash: HashType,
|
||||
) -> Result<Option<Block>, 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: HashType) -> Result<Transaction, ErrorObjectOwned>;
|
||||
async fn get_transaction(
|
||||
&self,
|
||||
tx_hash: HashType,
|
||||
) -> Result<Option<Transaction>, ErrorObjectOwned>;
|
||||
|
||||
#[method(name = "getBlocks")]
|
||||
async fn get_blocks(
|
||||
|
||||
@ -3,7 +3,7 @@ use std::net::SocketAddr;
|
||||
use anyhow::{Context as _, Result};
|
||||
pub use indexer_core::config::*;
|
||||
use indexer_service_rpc::RpcServer as _;
|
||||
use jsonrpsee::server::Server;
|
||||
use jsonrpsee::server::{Server, ServerHandle};
|
||||
use log::{error, info};
|
||||
|
||||
pub mod service;
|
||||
@ -13,10 +13,11 @@ pub mod mock_service;
|
||||
|
||||
pub struct IndexerHandle {
|
||||
addr: SocketAddr,
|
||||
server_handle: Option<jsonrpsee::server::ServerHandle>,
|
||||
/// Option because of `Drop` which forbids to simply move out of `self` in `stopped()`.
|
||||
server_handle: Option<ServerHandle>,
|
||||
}
|
||||
impl IndexerHandle {
|
||||
const fn new(addr: SocketAddr, server_handle: jsonrpsee::server::ServerHandle) -> Self {
|
||||
const fn new(addr: SocketAddr, server_handle: ServerHandle) -> Self {
|
||||
Self {
|
||||
addr,
|
||||
server_handle: Some(server_handle),
|
||||
@ -28,6 +29,7 @@ impl IndexerHandle {
|
||||
self.addr
|
||||
}
|
||||
|
||||
/// Wait for all Indexer tasks to stop.
|
||||
pub async fn stopped(mut self) {
|
||||
let handle = self
|
||||
.server_handle
|
||||
@ -37,15 +39,11 @@ impl IndexerHandle {
|
||||
handle.stopped().await;
|
||||
}
|
||||
|
||||
#[expect(
|
||||
clippy::redundant_closure_for_method_calls,
|
||||
reason = "Clippy suggested path jsonrpsee::jsonrpsee_server::ServerHandle is not accessible"
|
||||
)]
|
||||
#[must_use]
|
||||
pub fn is_stopped(&self) -> bool {
|
||||
pub fn is_healthy(&self) -> bool {
|
||||
self.server_handle
|
||||
.as_ref()
|
||||
.is_none_or(|handle| handle.is_stopped())
|
||||
.is_some_and(|handle| !handle.is_stopped())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -15,7 +15,10 @@ use indexer_service_protocol::{
|
||||
ProgramDeploymentTransaction, ProgramId, PublicMessage, PublicTransaction, Signature,
|
||||
Transaction, WitnessSet,
|
||||
};
|
||||
use jsonrpsee::{core::SubscriptionResult, types::ErrorObjectOwned};
|
||||
use jsonrpsee::{
|
||||
core::{SubscriptionResult, async_trait},
|
||||
types::ErrorObjectOwned,
|
||||
};
|
||||
|
||||
/// A mock implementation of the `IndexerService` RPC for testing purposes.
|
||||
pub struct MockIndexerService {
|
||||
@ -92,7 +95,7 @@ impl MockIndexerService {
|
||||
},
|
||||
witness_set: WitnessSet {
|
||||
signatures_and_public_keys: vec![],
|
||||
proof: indexer_service_protocol::Proof(vec![0; 32]),
|
||||
proof: None,
|
||||
},
|
||||
}),
|
||||
// PrivacyPreserving transactions
|
||||
@ -124,7 +127,7 @@ impl MockIndexerService {
|
||||
},
|
||||
witness_set: WitnessSet {
|
||||
signatures_and_public_keys: vec![],
|
||||
proof: indexer_service_protocol::Proof(vec![0; 32]),
|
||||
proof: Some(indexer_service_protocol::Proof(vec![0; 32])),
|
||||
},
|
||||
}),
|
||||
// ProgramDeployment transactions (rare)
|
||||
@ -171,7 +174,7 @@ impl MockIndexerService {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
#[async_trait]
|
||||
impl indexer_service_rpc::RpcServer for MockIndexerService {
|
||||
async fn subscribe_to_finalized_blocks(
|
||||
&self,
|
||||
@ -198,26 +201,23 @@ impl indexer_service_rpc::RpcServer for MockIndexerService {
|
||||
})
|
||||
}
|
||||
|
||||
async fn get_block_by_id(&self, block_id: BlockId) -> Result<Block, ErrorObjectOwned> {
|
||||
self.blocks
|
||||
async fn get_block_by_id(&self, block_id: BlockId) -> Result<Option<Block>, ErrorObjectOwned> {
|
||||
Ok(self
|
||||
.blocks
|
||||
.iter()
|
||||
.find(|b| b.header.block_id == block_id)
|
||||
.cloned()
|
||||
.ok_or_else(|| {
|
||||
ErrorObjectOwned::owned(
|
||||
-32001,
|
||||
format!("Block with ID {block_id} not found"),
|
||||
None::<()>,
|
||||
)
|
||||
})
|
||||
.cloned())
|
||||
}
|
||||
|
||||
async fn get_block_by_hash(&self, block_hash: HashType) -> Result<Block, ErrorObjectOwned> {
|
||||
self.blocks
|
||||
async fn get_block_by_hash(
|
||||
&self,
|
||||
block_hash: HashType,
|
||||
) -> Result<Option<Block>, ErrorObjectOwned> {
|
||||
Ok(self
|
||||
.blocks
|
||||
.iter()
|
||||
.find(|b| b.header.hash == block_hash)
|
||||
.cloned()
|
||||
.ok_or_else(|| ErrorObjectOwned::owned(-32001, "Block with hash not found", None::<()>))
|
||||
.cloned())
|
||||
}
|
||||
|
||||
async fn get_account(&self, account_id: AccountId) -> Result<Account, ErrorObjectOwned> {
|
||||
@ -227,11 +227,11 @@ impl indexer_service_rpc::RpcServer for MockIndexerService {
|
||||
.ok_or_else(|| ErrorObjectOwned::owned(-32001, "Account not found", None::<()>))
|
||||
}
|
||||
|
||||
async fn get_transaction(&self, tx_hash: HashType) -> Result<Transaction, ErrorObjectOwned> {
|
||||
self.transactions
|
||||
.get(&tx_hash)
|
||||
.map(|(tx, _)| tx.clone())
|
||||
.ok_or_else(|| ErrorObjectOwned::owned(-32001, "Transaction not found", None::<()>))
|
||||
async fn get_transaction(
|
||||
&self,
|
||||
tx_hash: HashType,
|
||||
) -> Result<Option<Transaction>, ErrorObjectOwned> {
|
||||
Ok(self.transactions.get(&tx_hash).map(|(tx, _)| tx.clone()))
|
||||
}
|
||||
|
||||
async fn get_blocks(
|
||||
|
||||
@ -7,7 +7,7 @@ use indexer_core::{IndexerCore, config::IndexerConfig};
|
||||
use indexer_service_protocol::{Account, AccountId, Block, BlockId, HashType, Transaction};
|
||||
use jsonrpsee::{
|
||||
SubscriptionSink,
|
||||
core::{Serialize, SubscriptionResult},
|
||||
core::{Serialize, SubscriptionResult, async_trait},
|
||||
types::{ErrorCode, ErrorObject, ErrorObjectOwned},
|
||||
};
|
||||
use log::{debug, error, info, warn};
|
||||
@ -30,7 +30,7 @@ impl IndexerService {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
#[async_trait]
|
||||
impl indexer_service_rpc::RpcServer for IndexerService {
|
||||
async fn subscribe_to_finalized_blocks(
|
||||
&self,
|
||||
@ -52,22 +52,25 @@ impl indexer_service_rpc::RpcServer for IndexerService {
|
||||
self.indexer.store.get_last_block_id().map_err(db_error)
|
||||
}
|
||||
|
||||
async fn get_block_by_id(&self, block_id: BlockId) -> Result<Block, ErrorObjectOwned> {
|
||||
async fn get_block_by_id(&self, block_id: BlockId) -> Result<Option<Block>, ErrorObjectOwned> {
|
||||
Ok(self
|
||||
.indexer
|
||||
.store
|
||||
.get_block_at_id(block_id)
|
||||
.map_err(db_error)?
|
||||
.into())
|
||||
.map(Into::into))
|
||||
}
|
||||
|
||||
async fn get_block_by_hash(&self, block_hash: HashType) -> Result<Block, ErrorObjectOwned> {
|
||||
async fn get_block_by_hash(
|
||||
&self,
|
||||
block_hash: HashType,
|
||||
) -> Result<Option<Block>, ErrorObjectOwned> {
|
||||
Ok(self
|
||||
.indexer
|
||||
.store
|
||||
.get_block_by_hash(block_hash.0)
|
||||
.map_err(db_error)?
|
||||
.into())
|
||||
.map(Into::into))
|
||||
}
|
||||
|
||||
async fn get_account(&self, account_id: AccountId) -> Result<Account, ErrorObjectOwned> {
|
||||
@ -80,13 +83,16 @@ impl indexer_service_rpc::RpcServer for IndexerService {
|
||||
.into())
|
||||
}
|
||||
|
||||
async fn get_transaction(&self, tx_hash: HashType) -> Result<Transaction, ErrorObjectOwned> {
|
||||
async fn get_transaction(
|
||||
&self,
|
||||
tx_hash: HashType,
|
||||
) -> Result<Option<Transaction>, ErrorObjectOwned> {
|
||||
Ok(self
|
||||
.indexer
|
||||
.store
|
||||
.get_transaction_by_hash(tx_hash.0)
|
||||
.map_err(db_error)?
|
||||
.into())
|
||||
.map(Into::into))
|
||||
}
|
||||
|
||||
async fn get_blocks(
|
||||
|
||||
@ -11,7 +11,7 @@ workspace = true
|
||||
nssa_core = { workspace = true, features = ["host"] }
|
||||
nssa.workspace = true
|
||||
sequencer_core = { workspace = true, features = ["default", "testnet"] }
|
||||
sequencer_runner.workspace = true
|
||||
sequencer_service.workspace = true
|
||||
wallet.workspace = true
|
||||
common.workspace = true
|
||||
key_protocol.workspace = true
|
||||
@ -19,6 +19,7 @@ indexer_service.workspace = true
|
||||
serde_json.workspace = true
|
||||
token_core.workspace = true
|
||||
indexer_service_rpc.workspace = true
|
||||
sequencer_service_rpc = { workspace = true, features = ["client"] }
|
||||
wallet-ffi.workspace = true
|
||||
|
||||
url.workspace = true
|
||||
@ -26,11 +27,9 @@ url.workspace = true
|
||||
anyhow.workspace = true
|
||||
env_logger.workspace = true
|
||||
log.workspace = true
|
||||
base64.workspace = true
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
|
||||
hex.workspace = true
|
||||
tempfile.workspace = true
|
||||
borsh.workspace = true
|
||||
bytesize.workspace = true
|
||||
futures.workspace = true
|
||||
testcontainers = { version = "0.27.0", features = ["docker-compose"] }
|
||||
|
||||
@ -59,11 +59,11 @@ impl InitialData {
|
||||
|
||||
let mut private_charlie_key_chain = KeyChain::new_os_random();
|
||||
let mut private_charlie_account_id =
|
||||
AccountId::from(&private_charlie_key_chain.nullifer_public_key);
|
||||
AccountId::from(&private_charlie_key_chain.nullifier_public_key);
|
||||
|
||||
let mut private_david_key_chain = KeyChain::new_os_random();
|
||||
let mut private_david_account_id =
|
||||
AccountId::from(&private_david_key_chain.nullifer_public_key);
|
||||
AccountId::from(&private_david_key_chain.nullifier_public_key);
|
||||
|
||||
// Ensure consistent ordering
|
||||
if private_charlie_account_id > private_david_account_id {
|
||||
@ -86,7 +86,7 @@ impl InitialData {
|
||||
balance: 10_000,
|
||||
data: Data::default(),
|
||||
program_owner: DEFAULT_PROGRAM_ID,
|
||||
nonce: 0,
|
||||
nonce: 0_u128.into(),
|
||||
},
|
||||
),
|
||||
(
|
||||
@ -95,7 +95,7 @@ impl InitialData {
|
||||
balance: 20_000,
|
||||
data: Data::default(),
|
||||
program_owner: DEFAULT_PROGRAM_ID,
|
||||
nonce: 0,
|
||||
nonce: 0_u128.into(),
|
||||
},
|
||||
),
|
||||
],
|
||||
@ -120,7 +120,7 @@ impl InitialData {
|
||||
self.private_accounts
|
||||
.iter()
|
||||
.map(|(key_chain, account)| CommitmentsInitialData {
|
||||
npk: key_chain.nullifer_public_key.clone(),
|
||||
npk: key_chain.nullifier_public_key.clone(),
|
||||
account: account.clone(),
|
||||
})
|
||||
.collect()
|
||||
@ -138,7 +138,7 @@ impl InitialData {
|
||||
})
|
||||
})
|
||||
.chain(self.private_accounts.iter().map(|(key_chain, account)| {
|
||||
let account_id = AccountId::from(&key_chain.nullifer_public_key);
|
||||
let account_id = AccountId::from(&key_chain.nullifier_public_key);
|
||||
InitialAccountData::Private(Box::new(InitialAccountDataPrivate {
|
||||
account_id,
|
||||
account: account.clone(),
|
||||
@ -204,7 +204,6 @@ pub fn sequencer_config(
|
||||
|
||||
Ok(SequencerConfig {
|
||||
home,
|
||||
override_rust_log: None,
|
||||
genesis_id: 1,
|
||||
is_genesis_random: true,
|
||||
max_num_tx_in_block,
|
||||
@ -212,7 +211,6 @@ pub fn sequencer_config(
|
||||
mempool_max_size,
|
||||
block_create_timeout,
|
||||
retry_pending_blocks_timeout: Duration::from_secs(120),
|
||||
port: 0,
|
||||
initial_accounts: initial_data.sequencer_initial_accounts(),
|
||||
initial_commitments: initial_data.sequencer_initial_commitments(),
|
||||
signing_key: [37; 32],
|
||||
@ -236,7 +234,6 @@ pub fn wallet_config(
|
||||
initial_data: &InitialData,
|
||||
) -> Result<WalletConfig> {
|
||||
Ok(WalletConfig {
|
||||
override_rust_log: None,
|
||||
sequencer_addr: addr_to_url(UrlProtocol::Http, sequencer_addr)
|
||||
.context("Failed to convert sequencer addr to URL")?,
|
||||
seq_poll_timeout: Duration::from_secs(30),
|
||||
|
||||
@ -3,15 +3,15 @@
|
||||
use std::{net::SocketAddr, path::PathBuf, sync::LazyLock};
|
||||
|
||||
use anyhow::{Context as _, Result, bail};
|
||||
use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64};
|
||||
use common::{HashType, sequencer_client::SequencerClient, transaction::NSSATransaction};
|
||||
use common::{HashType, transaction::NSSATransaction};
|
||||
use futures::FutureExt as _;
|
||||
use indexer_service::IndexerHandle;
|
||||
use log::{debug, error, warn};
|
||||
use nssa::{AccountId, PrivacyPreservingTransaction};
|
||||
use nssa_core::Commitment;
|
||||
use sequencer_core::indexer_client::{IndexerClient, IndexerClientTrait as _};
|
||||
use sequencer_runner::SequencerHandle;
|
||||
use sequencer_service::SequencerHandle;
|
||||
use sequencer_service_rpc::{RpcClient as _, SequencerClient, SequencerClientBuilder};
|
||||
use tempfile::TempDir;
|
||||
use testcontainers::compose::DockerCompose;
|
||||
use wallet::{WalletCore, config::WalletConfigOverrides};
|
||||
@ -38,7 +38,8 @@ pub struct TestContext {
|
||||
indexer_client: IndexerClient,
|
||||
wallet: WalletCore,
|
||||
wallet_password: String,
|
||||
sequencer_handle: SequencerHandle,
|
||||
/// Optional to move out value in Drop.
|
||||
sequencer_handle: Option<SequencerHandle>,
|
||||
indexer_handle: IndexerHandle,
|
||||
bedrock_compose: DockerCompose,
|
||||
_temp_indexer_dir: TempDir,
|
||||
@ -90,8 +91,9 @@ impl TestContext {
|
||||
.context("Failed to convert sequencer addr to URL")?;
|
||||
let indexer_url = config::addr_to_url(config::UrlProtocol::Ws, indexer_handle.addr())
|
||||
.context("Failed to convert indexer addr to URL")?;
|
||||
let sequencer_client =
|
||||
SequencerClient::new(sequencer_url).context("Failed to create sequencer client")?;
|
||||
let sequencer_client = SequencerClientBuilder::default()
|
||||
.build(sequencer_url)
|
||||
.context("Failed to create sequencer client")?;
|
||||
let indexer_client = IndexerClient::new(&indexer_url)
|
||||
.await
|
||||
.context("Failed to create indexer client")?;
|
||||
@ -102,7 +104,7 @@ impl TestContext {
|
||||
wallet,
|
||||
wallet_password,
|
||||
bedrock_compose,
|
||||
sequencer_handle,
|
||||
sequencer_handle: Some(sequencer_handle),
|
||||
indexer_handle,
|
||||
_temp_indexer_dir: temp_indexer_dir,
|
||||
_temp_sequencer_dir: temp_sequencer_dir,
|
||||
@ -229,7 +231,7 @@ impl TestContext {
|
||||
)
|
||||
.context("Failed to create Sequencer config")?;
|
||||
|
||||
let sequencer_handle = sequencer_runner::startup_sequencer(config).await?;
|
||||
let sequencer_handle = sequencer_service::run(config, 0).await?;
|
||||
|
||||
Ok((sequencer_handle, temp_sequencer_dir))
|
||||
}
|
||||
@ -333,18 +335,20 @@ impl Drop for TestContext {
|
||||
wallet_password: _,
|
||||
} = self;
|
||||
|
||||
if sequencer_handle.is_finished() {
|
||||
let Err(err) = self
|
||||
.sequencer_handle
|
||||
.run_forever()
|
||||
let sequencer_handle = sequencer_handle
|
||||
.take()
|
||||
.expect("Sequencer handle should be present in TestContext drop");
|
||||
if !sequencer_handle.is_healthy() {
|
||||
let Err(err) = sequencer_handle
|
||||
.failed()
|
||||
.now_or_never()
|
||||
.expect("Future is finished and should be ready");
|
||||
.expect("Sequencer handle should not be running");
|
||||
error!(
|
||||
"Sequencer handle has unexpectedly finished before TestContext drop with error: {err:#}"
|
||||
"Sequencer handle has unexpectedly stopped before TestContext drop with error: {err:#}"
|
||||
);
|
||||
}
|
||||
|
||||
if indexer_handle.is_stopped() {
|
||||
if !indexer_handle.is_healthy() {
|
||||
error!("Indexer handle has unexpectedly stopped before TestContext drop");
|
||||
}
|
||||
|
||||
@ -459,15 +463,8 @@ pub async fn fetch_privacy_preserving_tx(
|
||||
seq_client: &SequencerClient,
|
||||
tx_hash: HashType,
|
||||
) -> PrivacyPreservingTransaction {
|
||||
let transaction_encoded = seq_client
|
||||
.get_transaction_by_hash(tx_hash)
|
||||
.await
|
||||
.unwrap()
|
||||
.transaction
|
||||
.unwrap();
|
||||
let tx = seq_client.get_transaction(tx_hash).await.unwrap().unwrap();
|
||||
|
||||
let tx_bytes = BASE64.decode(transaction_encoded).unwrap();
|
||||
let tx = borsh::from_slice(&tx_bytes).unwrap();
|
||||
match tx {
|
||||
NSSATransaction::PrivacyPreserving(privacy_preserving_transaction) => {
|
||||
privacy_preserving_transaction
|
||||
@ -480,8 +477,10 @@ pub async fn verify_commitment_is_in_state(
|
||||
commitment: Commitment,
|
||||
seq_client: &SequencerClient,
|
||||
) -> bool {
|
||||
matches!(
|
||||
seq_client.get_proof_for_commitment(commitment).await,
|
||||
Ok(Some(_))
|
||||
)
|
||||
seq_client
|
||||
.get_proof_for_commitment(commitment)
|
||||
.await
|
||||
.ok()
|
||||
.flatten()
|
||||
.is_some()
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ use anyhow::Result;
|
||||
use integration_tests::TestContext;
|
||||
use log::info;
|
||||
use nssa::program::Program;
|
||||
use sequencer_service_rpc::RpcClient as _;
|
||||
use tokio::test;
|
||||
use wallet::cli::{
|
||||
Command,
|
||||
@ -21,8 +22,7 @@ async fn get_existing_account() -> Result<()> {
|
||||
let account = ctx
|
||||
.sequencer_client()
|
||||
.get_account(ctx.existing_public_accounts()[0])
|
||||
.await?
|
||||
.account;
|
||||
.await?;
|
||||
|
||||
assert_eq!(
|
||||
account.program_owner,
|
||||
@ -30,7 +30,7 @@ async fn get_existing_account() -> Result<()> {
|
||||
);
|
||||
assert_eq!(account.balance, 10000);
|
||||
assert!(account.data.is_empty());
|
||||
assert_eq!(account.nonce, 0);
|
||||
assert_eq!(account.nonce.0, 0);
|
||||
|
||||
info!("Successfully retrieved account with correct details");
|
||||
|
||||
|
||||
@ -9,6 +9,7 @@ use std::time::Duration;
|
||||
use anyhow::Result;
|
||||
use integration_tests::{TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, format_public_account_id};
|
||||
use log::info;
|
||||
use sequencer_service_rpc::RpcClient as _;
|
||||
use tokio::test;
|
||||
use wallet::cli::{
|
||||
Command, SubcommandReturnValue,
|
||||
@ -194,20 +195,14 @@ async fn amm_public() -> Result<()> {
|
||||
let user_holding_a_acc = ctx
|
||||
.sequencer_client()
|
||||
.get_account(recipient_account_id_1)
|
||||
.await?
|
||||
.account;
|
||||
.await?;
|
||||
|
||||
let user_holding_b_acc = ctx
|
||||
.sequencer_client()
|
||||
.get_account(recipient_account_id_2)
|
||||
.await?
|
||||
.account;
|
||||
.await?;
|
||||
|
||||
let user_holding_lp_acc = ctx
|
||||
.sequencer_client()
|
||||
.get_account(user_holding_lp)
|
||||
.await?
|
||||
.account;
|
||||
let user_holding_lp_acc = ctx.sequencer_client().get_account(user_holding_lp).await?;
|
||||
|
||||
assert_eq!(
|
||||
u128::from_le_bytes(user_holding_a_acc.data[33..].try_into().unwrap()),
|
||||
@ -243,20 +238,14 @@ async fn amm_public() -> Result<()> {
|
||||
let user_holding_a_acc = ctx
|
||||
.sequencer_client()
|
||||
.get_account(recipient_account_id_1)
|
||||
.await?
|
||||
.account;
|
||||
.await?;
|
||||
|
||||
let user_holding_b_acc = ctx
|
||||
.sequencer_client()
|
||||
.get_account(recipient_account_id_2)
|
||||
.await?
|
||||
.account;
|
||||
.await?;
|
||||
|
||||
let user_holding_lp_acc = ctx
|
||||
.sequencer_client()
|
||||
.get_account(user_holding_lp)
|
||||
.await?
|
||||
.account;
|
||||
let user_holding_lp_acc = ctx.sequencer_client().get_account(user_holding_lp).await?;
|
||||
|
||||
assert_eq!(
|
||||
u128::from_le_bytes(user_holding_a_acc.data[33..].try_into().unwrap()),
|
||||
@ -292,20 +281,14 @@ async fn amm_public() -> Result<()> {
|
||||
let user_holding_a_acc = ctx
|
||||
.sequencer_client()
|
||||
.get_account(recipient_account_id_1)
|
||||
.await?
|
||||
.account;
|
||||
.await?;
|
||||
|
||||
let user_holding_b_acc = ctx
|
||||
.sequencer_client()
|
||||
.get_account(recipient_account_id_2)
|
||||
.await?
|
||||
.account;
|
||||
.await?;
|
||||
|
||||
let user_holding_lp_acc = ctx
|
||||
.sequencer_client()
|
||||
.get_account(user_holding_lp)
|
||||
.await?
|
||||
.account;
|
||||
let user_holding_lp_acc = ctx.sequencer_client().get_account(user_holding_lp).await?;
|
||||
|
||||
assert_eq!(
|
||||
u128::from_le_bytes(user_holding_a_acc.data[33..].try_into().unwrap()),
|
||||
@ -342,20 +325,14 @@ async fn amm_public() -> Result<()> {
|
||||
let user_holding_a_acc = ctx
|
||||
.sequencer_client()
|
||||
.get_account(recipient_account_id_1)
|
||||
.await?
|
||||
.account;
|
||||
.await?;
|
||||
|
||||
let user_holding_b_acc = ctx
|
||||
.sequencer_client()
|
||||
.get_account(recipient_account_id_2)
|
||||
.await?
|
||||
.account;
|
||||
.await?;
|
||||
|
||||
let user_holding_lp_acc = ctx
|
||||
.sequencer_client()
|
||||
.get_account(user_holding_lp)
|
||||
.await?
|
||||
.account;
|
||||
let user_holding_lp_acc = ctx.sequencer_client().get_account(user_holding_lp).await?;
|
||||
|
||||
assert_eq!(
|
||||
u128::from_le_bytes(user_holding_a_acc.data[33..].try_into().unwrap()),
|
||||
@ -392,20 +369,14 @@ async fn amm_public() -> Result<()> {
|
||||
let user_holding_a_acc = ctx
|
||||
.sequencer_client()
|
||||
.get_account(recipient_account_id_1)
|
||||
.await?
|
||||
.account;
|
||||
.await?;
|
||||
|
||||
let user_holding_b_acc = ctx
|
||||
.sequencer_client()
|
||||
.get_account(recipient_account_id_2)
|
||||
.await?
|
||||
.account;
|
||||
.await?;
|
||||
|
||||
let user_holding_lp_acc = ctx
|
||||
.sequencer_client()
|
||||
.get_account(user_holding_lp)
|
||||
.await?
|
||||
.account;
|
||||
let user_holding_lp_acc = ctx.sequencer_client().get_account(user_holding_lp).await?;
|
||||
|
||||
assert_eq!(
|
||||
u128::from_le_bytes(user_holding_a_acc.data[33..].try_into().unwrap()),
|
||||
|
||||
@ -8,6 +8,7 @@ use integration_tests::{
|
||||
use log::info;
|
||||
use nssa::{AccountId, program::Program};
|
||||
use nssa_core::{NullifierPublicKey, encryption::shared_key_derivation::Secp256k1Point};
|
||||
use sequencer_service_rpc::RpcClient as _;
|
||||
use tokio::test;
|
||||
use wallet::cli::{
|
||||
Command, SubcommandReturnValue,
|
||||
@ -135,7 +136,7 @@ async fn deshielded_transfer_to_public_account() -> Result<()> {
|
||||
let acc_2_balance = ctx.sequencer_client().get_account_balance(to).await?;
|
||||
|
||||
assert_eq!(from_acc.balance, 9900);
|
||||
assert_eq!(acc_2_balance.balance, 20100);
|
||||
assert_eq!(acc_2_balance, 20100);
|
||||
|
||||
info!("Successfully deshielded transfer to public account");
|
||||
|
||||
@ -175,7 +176,7 @@ async fn private_transfer_to_owned_account_using_claiming_path() -> Result<()> {
|
||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||
from: format_private_account_id(from),
|
||||
to: None,
|
||||
to_npk: Some(hex::encode(to_keys.nullifer_public_key.0)),
|
||||
to_npk: Some(hex::encode(to_keys.nullifier_public_key.0)),
|
||||
to_vpk: Some(hex::encode(to_keys.viewing_public_key.0)),
|
||||
amount: 100,
|
||||
});
|
||||
@ -245,7 +246,7 @@ async fn shielded_transfer_to_owned_private_account() -> Result<()> {
|
||||
|
||||
let acc_from_balance = ctx.sequencer_client().get_account_balance(from).await?;
|
||||
|
||||
assert_eq!(acc_from_balance.balance, 9900);
|
||||
assert_eq!(acc_from_balance, 9900);
|
||||
assert_eq!(acc_to.balance, 20100);
|
||||
|
||||
info!("Successfully shielded transfer to owned private account");
|
||||
@ -290,7 +291,7 @@ async fn shielded_transfer_to_foreign_account() -> Result<()> {
|
||||
.await
|
||||
);
|
||||
|
||||
assert_eq!(acc_1_balance.balance, 9900);
|
||||
assert_eq!(acc_1_balance, 9900);
|
||||
|
||||
info!("Successfully shielded transfer to foreign account");
|
||||
|
||||
@ -335,7 +336,7 @@ async fn private_transfer_to_owned_account_continuous_run_path() -> Result<()> {
|
||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||
from: format_private_account_id(from),
|
||||
to: None,
|
||||
to_npk: Some(hex::encode(to_keys.nullifer_public_key.0)),
|
||||
to_npk: Some(hex::encode(to_keys.nullifier_public_key.0)),
|
||||
to_vpk: Some(hex::encode(to_keys.viewing_public_key.0)),
|
||||
amount: 100,
|
||||
});
|
||||
|
||||
@ -4,6 +4,7 @@ use anyhow::Result;
|
||||
use integration_tests::{TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, format_public_account_id};
|
||||
use log::info;
|
||||
use nssa::program::Program;
|
||||
use sequencer_service_rpc::RpcClient as _;
|
||||
use tokio::test;
|
||||
use wallet::cli::{
|
||||
Command, SubcommandReturnValue,
|
||||
@ -41,8 +42,8 @@ async fn successful_transfer_to_existing_account() -> Result<()> {
|
||||
info!("Balance of sender: {acc_1_balance:#?}");
|
||||
info!("Balance of receiver: {acc_2_balance:#?}");
|
||||
|
||||
assert_eq!(acc_1_balance.balance, 9900);
|
||||
assert_eq!(acc_2_balance.balance, 20100);
|
||||
assert_eq!(acc_1_balance, 9900);
|
||||
assert_eq!(acc_2_balance, 20100);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -97,8 +98,8 @@ pub async fn successful_transfer_to_new_account() -> Result<()> {
|
||||
info!("Balance of sender: {acc_1_balance:#?}");
|
||||
info!("Balance of receiver: {acc_2_balance:#?}");
|
||||
|
||||
assert_eq!(acc_1_balance.balance, 9900);
|
||||
assert_eq!(acc_2_balance.balance, 100);
|
||||
assert_eq!(acc_1_balance, 9900);
|
||||
assert_eq!(acc_2_balance, 100);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -134,8 +135,8 @@ async fn failed_transfer_with_insufficient_balance() -> Result<()> {
|
||||
info!("Balance of sender: {acc_1_balance:#?}");
|
||||
info!("Balance of receiver: {acc_2_balance:#?}");
|
||||
|
||||
assert_eq!(acc_1_balance.balance, 10000);
|
||||
assert_eq!(acc_2_balance.balance, 20000);
|
||||
assert_eq!(acc_1_balance, 10000);
|
||||
assert_eq!(acc_2_balance, 20000);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -171,8 +172,8 @@ async fn two_consecutive_successful_transfers() -> Result<()> {
|
||||
info!("Balance of sender: {acc_1_balance:#?}");
|
||||
info!("Balance of receiver: {acc_2_balance:#?}");
|
||||
|
||||
assert_eq!(acc_1_balance.balance, 9900);
|
||||
assert_eq!(acc_2_balance.balance, 20100);
|
||||
assert_eq!(acc_1_balance, 9900);
|
||||
assert_eq!(acc_2_balance, 20100);
|
||||
|
||||
info!("First TX Success!");
|
||||
|
||||
@ -203,8 +204,8 @@ async fn two_consecutive_successful_transfers() -> Result<()> {
|
||||
info!("Balance of sender: {acc_1_balance:#?}");
|
||||
info!("Balance of receiver: {acc_2_balance:#?}");
|
||||
|
||||
assert_eq!(acc_1_balance.balance, 9800);
|
||||
assert_eq!(acc_2_balance.balance, 20200);
|
||||
assert_eq!(acc_1_balance, 9800);
|
||||
assert_eq!(acc_2_balance, 20200);
|
||||
|
||||
info!("Second TX Success!");
|
||||
|
||||
@ -230,18 +231,14 @@ async fn initialize_public_account() -> Result<()> {
|
||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
|
||||
|
||||
info!("Checking correct execution");
|
||||
let account = ctx
|
||||
.sequencer_client()
|
||||
.get_account(account_id)
|
||||
.await?
|
||||
.account;
|
||||
let account = ctx.sequencer_client().get_account(account_id).await?;
|
||||
|
||||
assert_eq!(
|
||||
account.program_owner,
|
||||
Program::authenticated_transfer_program().id()
|
||||
);
|
||||
assert_eq!(account.balance, 0);
|
||||
assert_eq!(account.nonce, 1);
|
||||
assert_eq!(account.nonce.0, 1);
|
||||
assert!(account.data.is_empty());
|
||||
|
||||
info!("Successfully initialized public account");
|
||||
|
||||
@ -8,11 +8,12 @@ use std::time::Duration;
|
||||
|
||||
use anyhow::Result;
|
||||
use bytesize::ByteSize;
|
||||
use common::{block::HashableBlockData, transaction::NSSATransaction};
|
||||
use common::transaction::NSSATransaction;
|
||||
use integration_tests::{
|
||||
TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, config::SequencerPartialConfig,
|
||||
};
|
||||
use nssa::program::Program;
|
||||
use sequencer_service_rpc::RpcClient as _;
|
||||
use tokio::test;
|
||||
|
||||
#[test]
|
||||
@ -36,7 +37,10 @@ async fn reject_oversized_transaction() -> Result<()> {
|
||||
let tx = nssa::ProgramDeploymentTransaction::new(message);
|
||||
|
||||
// Try to submit the transaction and expect an error
|
||||
let result = ctx.sequencer_client().send_tx_program(tx).await;
|
||||
let result = ctx
|
||||
.sequencer_client()
|
||||
.send_transaction(NSSATransaction::ProgramDeployment(tx))
|
||||
.await;
|
||||
|
||||
assert!(
|
||||
result.is_err(),
|
||||
@ -74,7 +78,10 @@ async fn accept_transaction_within_limit() -> Result<()> {
|
||||
let tx = nssa::ProgramDeploymentTransaction::new(message);
|
||||
|
||||
// This should succeed
|
||||
let result = ctx.sequencer_client().send_tx_program(tx).await;
|
||||
let result = ctx
|
||||
.sequencer_client()
|
||||
.send_transaction(NSSATransaction::ProgramDeployment(tx))
|
||||
.await;
|
||||
|
||||
assert!(
|
||||
result.is_ok(),
|
||||
@ -112,33 +119,38 @@ async fn transaction_deferred_to_next_block_when_current_full() -> Result<()> {
|
||||
let burner_id = Program::new(burner_bytecode.clone())?.id();
|
||||
let chain_caller_id = Program::new(chain_caller_bytecode.clone())?.id();
|
||||
|
||||
let initial_block_height = ctx.sequencer_client().get_last_block().await?.last_block;
|
||||
let initial_block_height = ctx.sequencer_client().get_last_block_id().await?;
|
||||
|
||||
// Submit both program deployments
|
||||
ctx.sequencer_client()
|
||||
.send_tx_program(nssa::ProgramDeploymentTransaction::new(
|
||||
nssa::program_deployment_transaction::Message::new(burner_bytecode),
|
||||
.send_transaction(NSSATransaction::ProgramDeployment(
|
||||
nssa::ProgramDeploymentTransaction::new(
|
||||
nssa::program_deployment_transaction::Message::new(burner_bytecode),
|
||||
),
|
||||
))
|
||||
.await?;
|
||||
|
||||
ctx.sequencer_client()
|
||||
.send_tx_program(nssa::ProgramDeploymentTransaction::new(
|
||||
nssa::program_deployment_transaction::Message::new(chain_caller_bytecode),
|
||||
.send_transaction(NSSATransaction::ProgramDeployment(
|
||||
nssa::ProgramDeploymentTransaction::new(
|
||||
nssa::program_deployment_transaction::Message::new(chain_caller_bytecode),
|
||||
),
|
||||
))
|
||||
.await?;
|
||||
|
||||
// Wait for first block
|
||||
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
|
||||
|
||||
let block1_response = ctx
|
||||
let block1 = ctx
|
||||
.sequencer_client()
|
||||
.get_block(initial_block_height + 1)
|
||||
.await?;
|
||||
let block1: HashableBlockData = borsh::from_slice(&block1_response.block)?;
|
||||
.await?
|
||||
.unwrap();
|
||||
|
||||
// Check which program is in block 1
|
||||
let get_program_ids = |block: &HashableBlockData| -> Vec<nssa::ProgramId> {
|
||||
let get_program_ids = |block: &common::block::Block| -> Vec<nssa::ProgramId> {
|
||||
block
|
||||
.body
|
||||
.transactions
|
||||
.iter()
|
||||
.filter_map(|tx| {
|
||||
@ -168,11 +180,11 @@ async fn transaction_deferred_to_next_block_when_current_full() -> Result<()> {
|
||||
// Wait for second block
|
||||
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
|
||||
|
||||
let block2_response = ctx
|
||||
let block2 = ctx
|
||||
.sequencer_client()
|
||||
.get_block(initial_block_height + 2)
|
||||
.await?;
|
||||
let block2: HashableBlockData = borsh::from_slice(&block2_response.block)?;
|
||||
.await?
|
||||
.unwrap();
|
||||
let block2_program_ids = get_program_ids(&block2);
|
||||
|
||||
// The other program should be in block 2
|
||||
|
||||
@ -22,12 +22,8 @@ async fn indexer_test_run() -> Result<()> {
|
||||
// RUN OBSERVATION
|
||||
tokio::time::sleep(std::time::Duration::from_millis(L2_TO_L1_TIMEOUT_MILLIS)).await;
|
||||
|
||||
let last_block_seq = ctx
|
||||
.sequencer_client()
|
||||
.get_last_block()
|
||||
.await
|
||||
.unwrap()
|
||||
.last_block;
|
||||
let last_block_seq =
|
||||
sequencer_service_rpc::RpcClient::get_last_block_id(ctx.sequencer_client()).await?;
|
||||
|
||||
info!("Last block on seq now is {last_block_seq}");
|
||||
|
||||
@ -100,20 +96,22 @@ async fn indexer_state_consistency() -> Result<()> {
|
||||
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
|
||||
|
||||
info!("Checking correct balance move");
|
||||
let acc_1_balance = ctx
|
||||
.sequencer_client()
|
||||
.get_account_balance(ctx.existing_public_accounts()[0])
|
||||
.await?;
|
||||
let acc_2_balance = ctx
|
||||
.sequencer_client()
|
||||
.get_account_balance(ctx.existing_public_accounts()[1])
|
||||
.await?;
|
||||
let acc_1_balance = sequencer_service_rpc::RpcClient::get_account_balance(
|
||||
ctx.sequencer_client(),
|
||||
ctx.existing_public_accounts()[0],
|
||||
)
|
||||
.await?;
|
||||
let acc_2_balance = sequencer_service_rpc::RpcClient::get_account_balance(
|
||||
ctx.sequencer_client(),
|
||||
ctx.existing_public_accounts()[1],
|
||||
)
|
||||
.await?;
|
||||
|
||||
info!("Balance of sender: {acc_1_balance:#?}");
|
||||
info!("Balance of receiver: {acc_2_balance:#?}");
|
||||
|
||||
assert_eq!(acc_1_balance.balance, 9900);
|
||||
assert_eq!(acc_2_balance.balance, 20100);
|
||||
assert_eq!(acc_1_balance, 9900);
|
||||
assert_eq!(acc_2_balance, 20100);
|
||||
|
||||
// WAIT
|
||||
info!("Waiting for indexer to parse blocks");
|
||||
@ -131,16 +129,16 @@ async fn indexer_state_consistency() -> Result<()> {
|
||||
.unwrap();
|
||||
|
||||
info!("Checking correct state transition");
|
||||
let acc1_seq_state = ctx
|
||||
.sequencer_client()
|
||||
.get_account(ctx.existing_public_accounts()[0])
|
||||
.await?
|
||||
.account;
|
||||
let acc2_seq_state = ctx
|
||||
.sequencer_client()
|
||||
.get_account(ctx.existing_public_accounts()[1])
|
||||
.await?
|
||||
.account;
|
||||
let acc1_seq_state = sequencer_service_rpc::RpcClient::get_account(
|
||||
ctx.sequencer_client(),
|
||||
ctx.existing_public_accounts()[0],
|
||||
)
|
||||
.await?;
|
||||
let acc2_seq_state = sequencer_service_rpc::RpcClient::get_account(
|
||||
ctx.sequencer_client(),
|
||||
ctx.existing_public_accounts()[1],
|
||||
)
|
||||
.await?;
|
||||
|
||||
assert_eq!(acc1_ind_state, acc1_seq_state.into());
|
||||
assert_eq!(acc2_ind_state, acc2_seq_state.into());
|
||||
|
||||
@ -14,6 +14,7 @@ use integration_tests::{
|
||||
use key_protocol::key_management::key_tree::chain_index::ChainIndex;
|
||||
use log::info;
|
||||
use nssa::{AccountId, program::Program};
|
||||
use sequencer_service_rpc::RpcClient as _;
|
||||
use tokio::test;
|
||||
use wallet::cli::{
|
||||
Command, SubcommandReturnValue,
|
||||
@ -70,7 +71,7 @@ async fn sync_private_account_with_non_zero_chain_index() -> Result<()> {
|
||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||
from: format_private_account_id(from),
|
||||
to: None,
|
||||
to_npk: Some(hex::encode(to_keys.nullifer_public_key.0)),
|
||||
to_npk: Some(hex::encode(to_keys.nullifier_public_key.0)),
|
||||
to_vpk: Some(hex::encode(to_keys.viewing_public_key.0)),
|
||||
amount: 100,
|
||||
});
|
||||
@ -305,8 +306,8 @@ async fn restore_keys_from_seed() -> Result<()> {
|
||||
.get_account_balance(to_account_id4)
|
||||
.await?;
|
||||
|
||||
assert_eq!(acc3.balance, 91); // 102 - 11
|
||||
assert_eq!(acc4.balance, 114); // 103 + 11
|
||||
assert_eq!(acc3, 91); // 102 - 11
|
||||
assert_eq!(acc4, 114); // 103 + 11
|
||||
|
||||
info!("Successfully restored keys and verified transactions");
|
||||
|
||||
|
||||
@ -13,6 +13,7 @@ use integration_tests::{
|
||||
format_public_account_id, verify_commitment_is_in_state,
|
||||
};
|
||||
use log::info;
|
||||
use sequencer_service_rpc::RpcClient as _;
|
||||
use tokio::test;
|
||||
use wallet::cli::{
|
||||
Command, SubcommandReturnValue,
|
||||
@ -46,8 +47,7 @@ async fn claim_pinata_to_uninitialized_public_account_fails_fast() -> Result<()>
|
||||
let pinata_balance_pre = ctx
|
||||
.sequencer_client()
|
||||
.get_account_balance(PINATA_BASE58.parse().unwrap())
|
||||
.await?
|
||||
.balance;
|
||||
.await?;
|
||||
|
||||
let claim_result = wallet::cli::execute_subcommand(
|
||||
ctx.wallet_mut(),
|
||||
@ -70,8 +70,7 @@ async fn claim_pinata_to_uninitialized_public_account_fails_fast() -> Result<()>
|
||||
let pinata_balance_post = ctx
|
||||
.sequencer_client()
|
||||
.get_account_balance(PINATA_BASE58.parse().unwrap())
|
||||
.await?
|
||||
.balance;
|
||||
.await?;
|
||||
|
||||
assert_eq!(pinata_balance_post, pinata_balance_pre);
|
||||
|
||||
@ -102,8 +101,7 @@ async fn claim_pinata_to_uninitialized_private_account_fails_fast() -> Result<()
|
||||
let pinata_balance_pre = ctx
|
||||
.sequencer_client()
|
||||
.get_account_balance(PINATA_BASE58.parse().unwrap())
|
||||
.await?
|
||||
.balance;
|
||||
.await?;
|
||||
|
||||
let claim_result = wallet::cli::execute_subcommand(
|
||||
ctx.wallet_mut(),
|
||||
@ -126,8 +124,7 @@ async fn claim_pinata_to_uninitialized_private_account_fails_fast() -> Result<()
|
||||
let pinata_balance_post = ctx
|
||||
.sequencer_client()
|
||||
.get_account_balance(PINATA_BASE58.parse().unwrap())
|
||||
.await?
|
||||
.balance;
|
||||
.await?;
|
||||
|
||||
assert_eq!(pinata_balance_post, pinata_balance_pre);
|
||||
|
||||
@ -146,8 +143,7 @@ async fn claim_pinata_to_existing_public_account() -> Result<()> {
|
||||
let pinata_balance_pre = ctx
|
||||
.sequencer_client()
|
||||
.get_account_balance(PINATA_BASE58.parse().unwrap())
|
||||
.await?
|
||||
.balance;
|
||||
.await?;
|
||||
|
||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
|
||||
|
||||
@ -158,14 +154,12 @@ async fn claim_pinata_to_existing_public_account() -> Result<()> {
|
||||
let pinata_balance_post = ctx
|
||||
.sequencer_client()
|
||||
.get_account_balance(PINATA_BASE58.parse().unwrap())
|
||||
.await?
|
||||
.balance;
|
||||
.await?;
|
||||
|
||||
let winner_balance_post = ctx
|
||||
.sequencer_client()
|
||||
.get_account_balance(ctx.existing_public_accounts()[0])
|
||||
.await?
|
||||
.balance;
|
||||
.await?;
|
||||
|
||||
assert_eq!(pinata_balance_post, pinata_balance_pre - pinata_prize);
|
||||
assert_eq!(winner_balance_post, 10000 + pinata_prize);
|
||||
@ -187,8 +181,7 @@ async fn claim_pinata_to_existing_private_account() -> Result<()> {
|
||||
let pinata_balance_pre = ctx
|
||||
.sequencer_client()
|
||||
.get_account_balance(PINATA_BASE58.parse().unwrap())
|
||||
.await?
|
||||
.balance;
|
||||
.await?;
|
||||
|
||||
let result = wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
|
||||
let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash: _ } = result else {
|
||||
@ -211,8 +204,7 @@ async fn claim_pinata_to_existing_private_account() -> Result<()> {
|
||||
let pinata_balance_post = ctx
|
||||
.sequencer_client()
|
||||
.get_account_balance(PINATA_BASE58.parse().unwrap())
|
||||
.await?
|
||||
.balance;
|
||||
.await?;
|
||||
|
||||
assert_eq!(pinata_balance_post, pinata_balance_pre - pinata_prize);
|
||||
|
||||
@ -268,8 +260,7 @@ async fn claim_pinata_to_new_private_account() -> Result<()> {
|
||||
let pinata_balance_pre = ctx
|
||||
.sequencer_client()
|
||||
.get_account_balance(PINATA_BASE58.parse().unwrap())
|
||||
.await?
|
||||
.balance;
|
||||
.await?;
|
||||
|
||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
|
||||
|
||||
@ -285,8 +276,7 @@ async fn claim_pinata_to_new_private_account() -> Result<()> {
|
||||
let pinata_balance_post = ctx
|
||||
.sequencer_client()
|
||||
.get_account_balance(PINATA_BASE58.parse().unwrap())
|
||||
.await?
|
||||
.balance;
|
||||
.await?;
|
||||
|
||||
assert_eq!(pinata_balance_post, pinata_balance_pre - pinata_prize);
|
||||
|
||||
|
||||
@ -6,11 +6,13 @@
|
||||
use std::{path::PathBuf, time::Duration};
|
||||
|
||||
use anyhow::Result;
|
||||
use common::transaction::NSSATransaction;
|
||||
use integration_tests::{
|
||||
NSSA_PROGRAM_FOR_TEST_DATA_CHANGER, TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext,
|
||||
};
|
||||
use log::info;
|
||||
use nssa::{AccountId, program::Program};
|
||||
use sequencer_service_rpc::RpcClient as _;
|
||||
use tokio::test;
|
||||
use wallet::cli::Command;
|
||||
|
||||
@ -47,23 +49,22 @@ async fn deploy_and_execute_program() -> Result<()> {
|
||||
)?;
|
||||
let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[]);
|
||||
let transaction = nssa::PublicTransaction::new(message, witness_set);
|
||||
let _response = ctx.sequencer_client().send_tx_public(transaction).await?;
|
||||
let _response = ctx
|
||||
.sequencer_client()
|
||||
.send_transaction(NSSATransaction::Public(transaction))
|
||||
.await?;
|
||||
|
||||
info!("Waiting for next block creation");
|
||||
// Waiting for long time as it may take some time for such a big transaction to be included in a
|
||||
// block
|
||||
tokio::time::sleep(Duration::from_secs(2 * TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
|
||||
|
||||
let post_state_account = ctx
|
||||
.sequencer_client()
|
||||
.get_account(account_id)
|
||||
.await?
|
||||
.account;
|
||||
let post_state_account = ctx.sequencer_client().get_account(account_id).await?;
|
||||
|
||||
assert_eq!(post_state_account.program_owner, data_changer.id());
|
||||
assert_eq!(post_state_account.balance, 0);
|
||||
assert_eq!(post_state_account.data.as_ref(), &[0]);
|
||||
assert_eq!(post_state_account.nonce, 0);
|
||||
assert_eq!(post_state_account.nonce.0, 0);
|
||||
|
||||
info!("Successfully deployed and executed program");
|
||||
|
||||
|
||||
@ -14,6 +14,7 @@ use integration_tests::{
|
||||
use key_protocol::key_management::key_tree::chain_index::ChainIndex;
|
||||
use log::info;
|
||||
use nssa::program::Program;
|
||||
use sequencer_service_rpc::RpcClient as _;
|
||||
use token_core::{TokenDefinition, TokenHolding};
|
||||
use tokio::test;
|
||||
use wallet::cli::{
|
||||
@ -92,8 +93,7 @@ async fn create_and_transfer_public_token() -> Result<()> {
|
||||
let definition_acc = ctx
|
||||
.sequencer_client()
|
||||
.get_account(definition_account_id)
|
||||
.await?
|
||||
.account;
|
||||
.await?;
|
||||
let token_definition = TokenDefinition::try_from(&definition_acc.data)?;
|
||||
|
||||
assert_eq!(definition_acc.program_owner, Program::token().id());
|
||||
@ -110,8 +110,7 @@ async fn create_and_transfer_public_token() -> Result<()> {
|
||||
let supply_acc = ctx
|
||||
.sequencer_client()
|
||||
.get_account(supply_account_id)
|
||||
.await?
|
||||
.account;
|
||||
.await?;
|
||||
|
||||
// The account must be owned by the token program
|
||||
assert_eq!(supply_acc.program_owner, Program::token().id());
|
||||
@ -143,8 +142,7 @@ async fn create_and_transfer_public_token() -> Result<()> {
|
||||
let supply_acc = ctx
|
||||
.sequencer_client()
|
||||
.get_account(supply_account_id)
|
||||
.await?
|
||||
.account;
|
||||
.await?;
|
||||
assert_eq!(supply_acc.program_owner, Program::token().id());
|
||||
let token_holding = TokenHolding::try_from(&supply_acc.data)?;
|
||||
assert_eq!(
|
||||
@ -159,8 +157,7 @@ async fn create_and_transfer_public_token() -> Result<()> {
|
||||
let recipient_acc = ctx
|
||||
.sequencer_client()
|
||||
.get_account(recipient_account_id)
|
||||
.await?
|
||||
.account;
|
||||
.await?;
|
||||
assert_eq!(recipient_acc.program_owner, Program::token().id());
|
||||
let token_holding = TokenHolding::try_from(&recipient_acc.data)?;
|
||||
assert_eq!(
|
||||
@ -188,8 +185,7 @@ async fn create_and_transfer_public_token() -> Result<()> {
|
||||
let definition_acc = ctx
|
||||
.sequencer_client()
|
||||
.get_account(definition_account_id)
|
||||
.await?
|
||||
.account;
|
||||
.await?;
|
||||
let token_definition = TokenDefinition::try_from(&definition_acc.data)?;
|
||||
|
||||
assert_eq!(
|
||||
@ -205,8 +201,7 @@ async fn create_and_transfer_public_token() -> Result<()> {
|
||||
let recipient_acc = ctx
|
||||
.sequencer_client()
|
||||
.get_account(recipient_account_id)
|
||||
.await?
|
||||
.account;
|
||||
.await?;
|
||||
let token_holding = TokenHolding::try_from(&recipient_acc.data)?;
|
||||
|
||||
assert_eq!(
|
||||
@ -236,8 +231,7 @@ async fn create_and_transfer_public_token() -> Result<()> {
|
||||
let definition_acc = ctx
|
||||
.sequencer_client()
|
||||
.get_account(definition_account_id)
|
||||
.await?
|
||||
.account;
|
||||
.await?;
|
||||
let token_definition = TokenDefinition::try_from(&definition_acc.data)?;
|
||||
|
||||
assert_eq!(
|
||||
@ -253,8 +247,7 @@ async fn create_and_transfer_public_token() -> Result<()> {
|
||||
let recipient_acc = ctx
|
||||
.sequencer_client()
|
||||
.get_account(recipient_account_id)
|
||||
.await?
|
||||
.account;
|
||||
.await?;
|
||||
let token_holding = TokenHolding::try_from(&recipient_acc.data)?;
|
||||
|
||||
assert_eq!(
|
||||
@ -341,8 +334,7 @@ async fn create_and_transfer_token_with_private_supply() -> Result<()> {
|
||||
let definition_acc = ctx
|
||||
.sequencer_client()
|
||||
.get_account(definition_account_id)
|
||||
.await?
|
||||
.account;
|
||||
.await?;
|
||||
let token_definition = TokenDefinition::try_from(&definition_acc.data)?;
|
||||
|
||||
assert_eq!(definition_acc.program_owner, Program::token().id());
|
||||
@ -405,8 +397,7 @@ async fn create_and_transfer_token_with_private_supply() -> Result<()> {
|
||||
let definition_acc = ctx
|
||||
.sequencer_client()
|
||||
.get_account(definition_account_id)
|
||||
.await?
|
||||
.account;
|
||||
.await?;
|
||||
let token_definition = TokenDefinition::try_from(&definition_acc.data)?;
|
||||
|
||||
assert_eq!(
|
||||
@ -506,8 +497,7 @@ async fn create_token_with_private_definition() -> Result<()> {
|
||||
let supply_acc = ctx
|
||||
.sequencer_client()
|
||||
.get_account(supply_account_id)
|
||||
.await?
|
||||
.account;
|
||||
.await?;
|
||||
|
||||
assert_eq!(supply_acc.program_owner, Program::token().id());
|
||||
let token_holding = TokenHolding::try_from(&supply_acc.data)?;
|
||||
@ -586,8 +576,7 @@ async fn create_token_with_private_definition() -> Result<()> {
|
||||
let recipient_acc = ctx
|
||||
.sequencer_client()
|
||||
.get_account(recipient_account_id_public)
|
||||
.await?
|
||||
.account;
|
||||
.await?;
|
||||
let token_holding = TokenHolding::try_from(&recipient_acc.data)?;
|
||||
|
||||
assert_eq!(
|
||||
@ -882,8 +871,7 @@ async fn shielded_token_transfer() -> Result<()> {
|
||||
let supply_acc = ctx
|
||||
.sequencer_client()
|
||||
.get_account(supply_account_id)
|
||||
.await?
|
||||
.account;
|
||||
.await?;
|
||||
let token_holding = TokenHolding::try_from(&supply_acc.data)?;
|
||||
assert_eq!(
|
||||
token_holding,
|
||||
@ -1026,8 +1014,7 @@ async fn deshielded_token_transfer() -> Result<()> {
|
||||
let recipient_acc = ctx
|
||||
.sequencer_client()
|
||||
.get_account(recipient_account_id)
|
||||
.await?
|
||||
.account;
|
||||
.await?;
|
||||
let token_holding = TokenHolding::try_from(&recipient_acc.data)?;
|
||||
assert_eq!(
|
||||
token_holding,
|
||||
@ -1123,7 +1110,7 @@ async fn token_claiming_path_with_private_accounts() -> Result<()> {
|
||||
let subcommand = TokenProgramAgnosticSubcommand::Mint {
|
||||
definition: format_private_account_id(definition_account_id),
|
||||
holder: None,
|
||||
holder_npk: Some(hex::encode(holder_keys.nullifer_public_key.0)),
|
||||
holder_npk: Some(hex::encode(holder_keys.nullifier_public_key.0)),
|
||||
holder_vpk: Some(hex::encode(holder_keys.viewing_public_key.0)),
|
||||
amount: mint_amount,
|
||||
};
|
||||
|
||||
@ -13,6 +13,7 @@ use std::time::{Duration, Instant};
|
||||
|
||||
use anyhow::Result;
|
||||
use bytesize::ByteSize;
|
||||
use common::transaction::NSSATransaction;
|
||||
use integration_tests::{
|
||||
TestContext,
|
||||
config::{InitialData, SequencerPartialConfig},
|
||||
@ -27,9 +28,10 @@ use nssa::{
|
||||
};
|
||||
use nssa_core::{
|
||||
MembershipProof, NullifierPublicKey,
|
||||
account::{AccountWithMetadata, data::Data},
|
||||
account::{AccountWithMetadata, Nonce, data::Data},
|
||||
encryption::ViewingPublicKey,
|
||||
};
|
||||
use sequencer_service_rpc::RpcClient as _;
|
||||
use tokio::test;
|
||||
|
||||
pub(crate) struct TpsTestManager {
|
||||
@ -78,7 +80,7 @@ impl TpsTestManager {
|
||||
let message = putx::Message::try_new(
|
||||
program.id(),
|
||||
[pair[0].1, pair[1].1].to_vec(),
|
||||
[0_u128].to_vec(),
|
||||
[Nonce(0_u128)].to_vec(),
|
||||
amount,
|
||||
)
|
||||
.unwrap();
|
||||
@ -107,7 +109,7 @@ impl TpsTestManager {
|
||||
let key_chain = KeyChain::new_os_random();
|
||||
let account = Account {
|
||||
balance: 100,
|
||||
nonce: 0xdead_beef,
|
||||
nonce: Nonce(0xdead_beef),
|
||||
program_owner: Program::authenticated_transfer_program().id(),
|
||||
data: Data::default(),
|
||||
};
|
||||
@ -153,10 +155,9 @@ pub async fn tps_test() -> Result<()> {
|
||||
for (i, tx) in txs.into_iter().enumerate() {
|
||||
let tx_hash = ctx
|
||||
.sequencer_client()
|
||||
.send_tx_public(tx)
|
||||
.send_transaction(NSSATransaction::Public(tx))
|
||||
.await
|
||||
.unwrap()
|
||||
.tx_hash;
|
||||
.unwrap();
|
||||
info!("Sent tx {i}");
|
||||
tx_hashes.push(tx_hash);
|
||||
}
|
||||
@ -170,15 +171,13 @@ pub async fn tps_test() -> Result<()> {
|
||||
|
||||
let tx_obj = ctx
|
||||
.sequencer_client()
|
||||
.get_transaction_by_hash(*tx_hash)
|
||||
.get_transaction(*tx_hash)
|
||||
.await
|
||||
.inspect_err(|err| {
|
||||
log::warn!("Failed to get transaction by hash {tx_hash} with error: {err:#?}");
|
||||
});
|
||||
|
||||
if let Ok(tx_obj) = tx_obj
|
||||
&& tx_obj.transaction.is_some()
|
||||
{
|
||||
if tx_obj.is_ok_and(|opt| opt.is_some()) {
|
||||
info!("Found tx {i} with hash {tx_hash}");
|
||||
break;
|
||||
}
|
||||
@ -216,7 +215,7 @@ fn build_privacy_transaction() -> PrivacyPreservingTransaction {
|
||||
let sender_pre = AccountWithMetadata::new(
|
||||
Account {
|
||||
balance: 100,
|
||||
nonce: 0xdead_beef,
|
||||
nonce: Nonce(0xdead_beef),
|
||||
program_owner: program.id(),
|
||||
data: Data::default(),
|
||||
},
|
||||
@ -250,7 +249,6 @@ fn build_privacy_transaction() -> PrivacyPreservingTransaction {
|
||||
vec![sender_pre, recipient_pre],
|
||||
Program::serialize_instruction(balance_to_move).unwrap(),
|
||||
vec![1, 2],
|
||||
vec![0xdead_beef1, 0xdead_beef2],
|
||||
vec![
|
||||
(sender_npk.clone(), sender_ss),
|
||||
(recipient_npk.clone(), recipient_ss),
|
||||
|
||||
@ -491,7 +491,7 @@ fn test_wallet_ffi_get_account_public() -> Result<()> {
|
||||
);
|
||||
assert_eq!(account.balance, 10000);
|
||||
assert!(account.data.is_empty());
|
||||
assert_eq!(account.nonce, 0);
|
||||
assert_eq!(account.nonce.0, 0);
|
||||
|
||||
unsafe {
|
||||
wallet_ffi_free_account_data(&raw mut out_account);
|
||||
@ -528,7 +528,7 @@ fn test_wallet_ffi_get_account_private() -> Result<()> {
|
||||
);
|
||||
assert_eq!(account.balance, 10000);
|
||||
assert!(account.data.is_empty());
|
||||
assert_eq!(account.nonce, 0);
|
||||
assert_eq!(account.nonce, 0_u128.into());
|
||||
|
||||
unsafe {
|
||||
wallet_ffi_free_account_data(&raw mut out_account);
|
||||
@ -606,7 +606,7 @@ fn test_wallet_ffi_get_private_account_keys() -> Result<()> {
|
||||
.unwrap()
|
||||
.0;
|
||||
|
||||
let expected_npk = &key_chain.nullifer_public_key;
|
||||
let expected_npk = &key_chain.nullifier_public_key;
|
||||
let expected_vpk = &key_chain.viewing_public_key;
|
||||
|
||||
assert_eq!(&keys.npk(), expected_npk);
|
||||
|
||||
@ -19,10 +19,12 @@ serde.workspace = true
|
||||
k256.workspace = true
|
||||
sha2.workspace = true
|
||||
rand.workspace = true
|
||||
base58.workspace = true
|
||||
hex.workspace = true
|
||||
aes-gcm.workspace = true
|
||||
bip39.workspace = true
|
||||
hmac-sha512.workspace = true
|
||||
thiserror.workspace = true
|
||||
itertools.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
base58.workspace = true
|
||||
|
||||
@ -39,7 +39,7 @@ impl KeyNode for ChildKeysPrivate {
|
||||
value: (
|
||||
KeyChain {
|
||||
secret_spending_key: ssk,
|
||||
nullifer_public_key: npk,
|
||||
nullifier_public_key: npk,
|
||||
viewing_public_key: vpk,
|
||||
private_key_holder: PrivateKeyHolder {
|
||||
nullifier_secret_key: nsk,
|
||||
@ -54,10 +54,7 @@ impl KeyNode for ChildKeysPrivate {
|
||||
}
|
||||
|
||||
fn nth_child(&self, cci: u32) -> Self {
|
||||
#[expect(
|
||||
clippy::arithmetic_side_effects,
|
||||
reason = "Multiplying finite field scalars gives no unexpected side effects"
|
||||
)]
|
||||
#[expect(clippy::arithmetic_side_effects, reason = "TODO: fix later")]
|
||||
let parent_pt =
|
||||
Scalar::from_repr(self.value.0.private_key_holder.nullifier_secret_key.into())
|
||||
.expect("Key generated as scalar, must be valid representation")
|
||||
@ -67,7 +64,8 @@ impl KeyNode for ChildKeysPrivate {
|
||||
|
||||
input.extend_from_slice(b"LEE_seed_priv");
|
||||
input.extend_from_slice(&parent_pt.to_bytes());
|
||||
input.extend_from_slice(&cci.to_le_bytes());
|
||||
#[expect(clippy::big_endian_bytes, reason = "BIP-032 uses big endian")]
|
||||
input.extend_from_slice(&cci.to_be_bytes());
|
||||
|
||||
let hash_value = hmac_sha512::HMAC::mac(input, self.ccc);
|
||||
|
||||
@ -90,7 +88,7 @@ impl KeyNode for ChildKeysPrivate {
|
||||
value: (
|
||||
KeyChain {
|
||||
secret_spending_key: ssk,
|
||||
nullifer_public_key: npk,
|
||||
nullifier_public_key: npk,
|
||||
viewing_public_key: vpk,
|
||||
private_key_holder: PrivateKeyHolder {
|
||||
nullifier_secret_key: nsk,
|
||||
@ -113,18 +111,26 @@ impl KeyNode for ChildKeysPrivate {
|
||||
}
|
||||
|
||||
fn account_id(&self) -> nssa::AccountId {
|
||||
nssa::AccountId::from(&self.value.0.nullifer_public_key)
|
||||
nssa::AccountId::from(&self.value.0.nullifier_public_key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'keys> From<&'keys ChildKeysPrivate> for &'keys (KeyChain, nssa::Account) {
|
||||
fn from(value: &'keys ChildKeysPrivate) -> Self {
|
||||
#[expect(
|
||||
clippy::single_char_lifetime_names,
|
||||
reason = "TODO add meaningful name"
|
||||
)]
|
||||
impl<'a> From<&'a ChildKeysPrivate> for &'a (KeyChain, nssa::Account) {
|
||||
fn from(value: &'a ChildKeysPrivate) -> Self {
|
||||
&value.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<'keys> From<&'keys mut ChildKeysPrivate> for &'keys mut (KeyChain, nssa::Account) {
|
||||
fn from(value: &'keys mut ChildKeysPrivate) -> Self {
|
||||
#[expect(
|
||||
clippy::single_char_lifetime_names,
|
||||
reason = "TODO add meaningful name"
|
||||
)]
|
||||
impl<'a> From<&'a mut ChildKeysPrivate> for &'a mut (KeyChain, nssa::Account) {
|
||||
fn from(value: &'a mut ChildKeysPrivate) -> Self {
|
||||
&mut value.value
|
||||
}
|
||||
}
|
||||
@ -166,7 +172,7 @@ mod tests {
|
||||
7, 123, 125, 191, 233, 183, 201, 4, 20, 214, 155, 210, 45, 234, 27, 240, 194, 111, 97,
|
||||
247, 155, 113, 122, 246, 192, 0, 70, 61, 76, 71, 70, 2,
|
||||
]);
|
||||
let expected_vsk: ViewingSecretKey = [
|
||||
let expected_vsk = [
|
||||
155, 90, 54, 75, 228, 130, 68, 201, 129, 251, 180, 195, 250, 64, 34, 230, 241, 204,
|
||||
216, 50, 149, 156, 10, 67, 208, 74, 9, 10, 47, 59, 50, 202,
|
||||
];
|
||||
@ -179,7 +185,7 @@ mod tests {
|
||||
assert!(expected_ssk == keys.value.0.secret_spending_key);
|
||||
assert!(expected_ccc == keys.ccc);
|
||||
assert!(expected_nsk == keys.value.0.private_key_holder.nullifier_secret_key);
|
||||
assert!(expected_npk == keys.value.0.nullifer_public_key);
|
||||
assert!(expected_npk == keys.value.0.nullifier_public_key);
|
||||
assert!(expected_vsk == keys.value.0.private_key_holder.viewing_secret_key);
|
||||
assert!(expected_vpk_as_bytes == keys.value.0.viewing_public_key.to_bytes());
|
||||
}
|
||||
@ -197,31 +203,31 @@ mod tests {
|
||||
let child_node = ChildKeysPrivate::nth_child(&root_node, 42_u32);
|
||||
|
||||
let expected_ccc: [u8; 32] = [
|
||||
145, 59, 225, 32, 54, 168, 14, 45, 60, 253, 57, 202, 31, 86, 142, 234, 51, 57, 154, 88,
|
||||
132, 200, 92, 191, 220, 144, 42, 184, 108, 35, 226, 146,
|
||||
27, 73, 133, 213, 214, 63, 217, 184, 164, 17, 172, 140, 223, 95, 255, 157, 11, 0, 58,
|
||||
53, 82, 147, 121, 120, 199, 50, 30, 28, 103, 24, 121, 187,
|
||||
];
|
||||
|
||||
let expected_nsk: NullifierSecretKey = [
|
||||
19, 100, 119, 73, 191, 225, 234, 219, 129, 88, 40, 229, 63, 225, 189, 136, 69, 172,
|
||||
221, 186, 147, 83, 150, 207, 70, 17, 228, 70, 113, 87, 227, 31,
|
||||
124, 61, 40, 92, 33, 135, 3, 41, 200, 234, 3, 69, 102, 184, 57, 191, 106, 151, 194,
|
||||
192, 103, 132, 141, 112, 249, 108, 192, 117, 24, 48, 70, 216,
|
||||
];
|
||||
let expected_npk = nssa_core::NullifierPublicKey([
|
||||
133, 235, 223, 151, 12, 69, 26, 222, 60, 125, 235, 125, 167, 212, 201, 168, 101, 242,
|
||||
111, 239, 1, 228, 12, 252, 146, 53, 75, 17, 187, 255, 122, 181,
|
||||
116, 231, 246, 189, 145, 240, 37, 59, 219, 223, 216, 246, 116, 171, 223, 55, 197, 200,
|
||||
134, 192, 221, 40, 218, 167, 239, 5, 11, 95, 147, 247, 162, 226,
|
||||
]);
|
||||
|
||||
let expected_vsk: ViewingSecretKey = [
|
||||
218, 219, 193, 132, 160, 6, 178, 194, 139, 248, 199, 81, 17, 133, 37, 201, 58, 104, 49,
|
||||
222, 187, 46, 156, 93, 14, 118, 209, 243, 38, 101, 77, 45,
|
||||
33, 155, 68, 60, 102, 70, 47, 105, 194, 129, 44, 26, 143, 198, 44, 244, 185, 31, 236,
|
||||
252, 205, 89, 138, 107, 39, 38, 154, 73, 109, 166, 41, 114,
|
||||
];
|
||||
let expected_vpk_as_bytes: [u8; 33] = [
|
||||
3, 164, 65, 167, 88, 167, 179, 51, 159, 27, 241, 174, 77, 174, 142, 106, 128, 96, 69,
|
||||
74, 117, 231, 42, 193, 235, 153, 206, 116, 102, 7, 101, 192, 45,
|
||||
2, 78, 213, 113, 117, 105, 162, 248, 175, 68, 128, 232, 106, 204, 208, 159, 11, 78, 48,
|
||||
244, 127, 112, 46, 0, 93, 184, 1, 77, 132, 160, 75, 152, 88,
|
||||
];
|
||||
|
||||
assert!(expected_ccc == child_node.ccc);
|
||||
assert!(expected_nsk == child_node.value.0.private_key_holder.nullifier_secret_key);
|
||||
assert!(expected_npk == child_node.value.0.nullifer_public_key);
|
||||
assert!(expected_npk == child_node.value.0.nullifier_public_key);
|
||||
assert!(expected_vsk == child_node.value.0.private_key_holder.viewing_secret_key);
|
||||
assert!(expected_vpk_as_bytes == child_node.value.0.viewing_public_key.to_bytes());
|
||||
}
|
||||
|
||||
@ -13,17 +13,25 @@ pub struct ChildKeysPublic {
|
||||
}
|
||||
|
||||
impl ChildKeysPublic {
|
||||
#[expect(clippy::big_endian_bytes, reason = "BIP-032 uses big endian")]
|
||||
fn compute_hash_value(&self, cci: u32) -> [u8; 64] {
|
||||
let mut hash_input = vec![];
|
||||
|
||||
if 2_u32.pow(31) > cci {
|
||||
// Non-harden
|
||||
hash_input.extend_from_slice(self.cpk.value());
|
||||
if ((2_u32).pow(31)).cmp(&cci) == std::cmp::Ordering::Greater {
|
||||
// Non-harden.
|
||||
// BIP-032 compatibility requires 1-byte header from the public_key;
|
||||
// Not stored in `self.cpk.value()`.
|
||||
let sk = secp256k1::SecretKey::from_byte_array(*self.csk.value())
|
||||
.expect("32 bytes, within curve order");
|
||||
let pk = secp256k1::PublicKey::from_secret_key(&secp256k1::Secp256k1::new(), &sk);
|
||||
hash_input.extend_from_slice(&secp256k1::PublicKey::serialize(&pk));
|
||||
} else {
|
||||
// Harden
|
||||
// Harden.
|
||||
hash_input.extend_from_slice(&[0_u8]);
|
||||
hash_input.extend_from_slice(self.csk.value());
|
||||
}
|
||||
hash_input.extend_from_slice(&cci.to_le_bytes());
|
||||
|
||||
hash_input.extend_from_slice(&cci.to_be_bytes());
|
||||
|
||||
hmac_sha512::HMAC::mac(hash_input, self.ccc)
|
||||
}
|
||||
@ -55,11 +63,13 @@ impl KeyNode for ChildKeysPublic {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let csk = nssa::PrivateKey::try_new(
|
||||
csk.add_tweak(&Scalar::from_le_bytes(*self.csk.value()).unwrap())
|
||||
let csk = nssa::PrivateKey::try_new({
|
||||
let scalar = Scalar::from_be_bytes(*self.csk.value()).unwrap();
|
||||
|
||||
csk.add_tweak(&scalar)
|
||||
.expect("Expect a valid Scalar")
|
||||
.secret_bytes(),
|
||||
)
|
||||
.secret_bytes()
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
assert!(
|
||||
@ -94,8 +104,12 @@ impl KeyNode for ChildKeysPublic {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'keys> From<&'keys ChildKeysPublic> for &'keys nssa::PrivateKey {
|
||||
fn from(value: &'keys ChildKeysPublic) -> Self {
|
||||
#[expect(
|
||||
clippy::single_char_lifetime_names,
|
||||
reason = "TODO add meaningful name"
|
||||
)]
|
||||
impl<'a> From<&'a ChildKeysPublic> for &'a nssa::PrivateKey {
|
||||
fn from(value: &'a ChildKeysPublic) -> Self {
|
||||
&value.csk
|
||||
}
|
||||
}
|
||||
@ -126,6 +140,7 @@ mod tests {
|
||||
202, 148, 181, 228, 35, 222, 58, 84, 156, 24, 146, 86,
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
let expected_cpk: PublicKey = PublicKey::try_new([
|
||||
219, 141, 130, 105, 11, 203, 187, 124, 112, 75, 223, 22, 11, 164, 153, 127, 59, 247,
|
||||
244, 166, 75, 66, 242, 224, 35, 156, 161, 75, 41, 51, 76, 245,
|
||||
@ -149,26 +164,20 @@ mod tests {
|
||||
let cci = (2_u32).pow(31) + 13;
|
||||
let child_keys = ChildKeysPublic::nth_child(&root_keys, cci);
|
||||
|
||||
print!(
|
||||
"{} {}",
|
||||
child_keys.csk.value()[0],
|
||||
child_keys.csk.value()[1]
|
||||
);
|
||||
|
||||
let expected_ccc = [
|
||||
126, 175, 244, 41, 41, 173, 134, 103, 139, 140, 195, 86, 194, 147, 116, 48, 71, 107,
|
||||
253, 235, 114, 139, 60, 115, 226, 205, 215, 248, 240, 190, 196, 6,
|
||||
149, 226, 13, 4, 194, 12, 69, 29, 9, 234, 209, 119, 98, 4, 128, 91, 37, 103, 192, 31,
|
||||
130, 126, 123, 20, 90, 34, 173, 209, 101, 248, 155, 36,
|
||||
];
|
||||
|
||||
let expected_csk: PrivateKey = PrivateKey::try_new([
|
||||
128, 148, 53, 165, 222, 155, 163, 108, 186, 182, 124, 67, 90, 86, 59, 123, 95, 224,
|
||||
171, 4, 51, 131, 254, 57, 241, 178, 82, 161, 204, 206, 79, 107,
|
||||
9, 65, 33, 228, 25, 82, 219, 117, 91, 217, 11, 223, 144, 85, 246, 26, 123, 216, 107,
|
||||
213, 33, 52, 188, 22, 198, 246, 71, 46, 245, 174, 16, 47,
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
let expected_cpk: PublicKey = PublicKey::try_new([
|
||||
149, 240, 55, 15, 178, 67, 245, 254, 44, 141, 95, 223, 238, 62, 85, 11, 248, 9, 11, 40,
|
||||
69, 211, 116, 13, 189, 35, 8, 95, 233, 154, 129, 58,
|
||||
142, 143, 238, 159, 105, 165, 224, 252, 108, 62, 53, 209, 176, 219, 249, 38, 90, 241,
|
||||
201, 81, 194, 146, 236, 5, 83, 152, 238, 243, 138, 16, 229, 15,
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
@ -189,26 +198,20 @@ mod tests {
|
||||
let cci = 13;
|
||||
let child_keys = ChildKeysPublic::nth_child(&root_keys, cci);
|
||||
|
||||
print!(
|
||||
"{} {}",
|
||||
child_keys.csk.value()[0],
|
||||
child_keys.csk.value()[1]
|
||||
);
|
||||
|
||||
let expected_ccc = [
|
||||
50, 29, 113, 102, 49, 130, 64, 0, 247, 95, 135, 187, 118, 162, 65, 65, 194, 53, 189,
|
||||
242, 66, 178, 168, 2, 51, 193, 155, 72, 209, 2, 207, 251,
|
||||
79, 228, 242, 119, 211, 203, 198, 175, 95, 36, 4, 234, 139, 45, 137, 138, 54, 211, 187,
|
||||
16, 28, 79, 80, 232, 216, 101, 145, 19, 101, 220, 217, 141,
|
||||
];
|
||||
|
||||
let expected_csk: PrivateKey = PrivateKey::try_new([
|
||||
162, 32, 211, 190, 180, 74, 151, 246, 189, 93, 8, 57, 182, 239, 125, 245, 192, 255, 24,
|
||||
186, 251, 23, 194, 186, 252, 121, 190, 54, 147, 199, 1, 109,
|
||||
185, 147, 32, 242, 145, 91, 123, 77, 42, 33, 134, 84, 12, 165, 117, 70, 158, 201, 95,
|
||||
153, 14, 12, 92, 235, 128, 156, 194, 169, 68, 35, 165, 127,
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
let expected_cpk: PublicKey = PublicKey::try_new([
|
||||
183, 48, 207, 170, 221, 111, 118, 9, 40, 67, 123, 162, 159, 169, 34, 157, 23, 37, 232,
|
||||
102, 231, 187, 199, 191, 205, 146, 159, 22, 79, 100, 10, 223,
|
||||
119, 16, 145, 121, 97, 244, 186, 35, 136, 34, 140, 171, 206, 139, 11, 208, 207, 121,
|
||||
158, 45, 28, 22, 140, 98, 161, 179, 212, 173, 238, 220, 2, 34,
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
@ -230,19 +233,19 @@ mod tests {
|
||||
let child_keys = ChildKeysPublic::nth_child(&root_keys, cci);
|
||||
|
||||
let expected_ccc = [
|
||||
101, 15, 69, 152, 144, 22, 105, 89, 175, 21, 13, 50, 160, 167, 93, 80, 94, 99, 192,
|
||||
252, 1, 126, 196, 217, 149, 164, 60, 75, 237, 90, 104, 83,
|
||||
221, 208, 47, 189, 174, 152, 33, 25, 151, 114, 233, 191, 57, 15, 40, 140, 46, 87, 126,
|
||||
58, 215, 40, 246, 111, 166, 113, 183, 145, 173, 11, 27, 182,
|
||||
];
|
||||
|
||||
let expected_csk: PrivateKey = PrivateKey::try_new([
|
||||
46, 196, 131, 199, 190, 180, 250, 222, 41, 188, 221, 156, 255, 239, 251, 207, 239, 202,
|
||||
166, 216, 107, 236, 195, 48, 167, 69, 97, 13, 132, 117, 76, 89,
|
||||
223, 29, 87, 189, 126, 24, 117, 225, 190, 57, 0, 143, 207, 168, 231, 139, 170, 192, 81,
|
||||
254, 126, 10, 115, 42, 141, 157, 70, 171, 199, 231, 198, 132,
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
let expected_cpk: PublicKey = PublicKey::try_new([
|
||||
93, 151, 154, 238, 175, 198, 53, 146, 255, 43, 37, 52, 214, 165, 69, 161, 38, 20, 68,
|
||||
166, 143, 80, 149, 216, 124, 203, 240, 114, 168, 111, 33, 83,
|
||||
96, 123, 245, 51, 214, 216, 215, 205, 70, 145, 105, 221, 166, 169, 122, 27, 94, 112,
|
||||
228, 110, 249, 177, 85, 173, 180, 248, 185, 199, 112, 246, 83, 33,
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
use std::{collections::BTreeMap, sync::Arc};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use anyhow::Result;
|
||||
use common::sequencer_client::SequencerClient;
|
||||
use nssa::{Account, AccountId};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::key_management::{
|
||||
@ -197,40 +197,6 @@ impl<N: KeyNode> KeyTree<N> {
|
||||
}
|
||||
|
||||
impl KeyTree<ChildKeysPrivate> {
|
||||
/// Cleanup of all non-initialized accounts in a private tree.
|
||||
///
|
||||
/// For given `depth` checks children to a tree such that their `ChainIndex::depth(&self) <
|
||||
/// depth`.
|
||||
///
|
||||
/// If account is default, removes them.
|
||||
///
|
||||
/// Chain must be parsed for accounts beforehand.
|
||||
///
|
||||
/// Fast, leaves gaps between accounts.
|
||||
pub fn cleanup_tree_remove_uninit_for_depth(&mut self, depth: u32) {
|
||||
let mut id_stack = vec![ChainIndex::root()];
|
||||
|
||||
while let Some(curr_id) = id_stack.pop() {
|
||||
if let Some(node) = self.key_map.get(&curr_id)
|
||||
&& node.value.1 == nssa::Account::default()
|
||||
&& curr_id != ChainIndex::root()
|
||||
{
|
||||
let addr = node.account_id();
|
||||
self.remove(addr);
|
||||
}
|
||||
|
||||
let mut next_id = curr_id.nth_child(0);
|
||||
|
||||
while (next_id.depth()) < depth {
|
||||
id_stack.push(next_id.clone());
|
||||
next_id = match next_id.next_in_line() {
|
||||
Some(id) => id,
|
||||
None => break,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Cleanup of non-initialized accounts in a private tree.
|
||||
///
|
||||
/// If account is default, removes them, stops at first non-default account.
|
||||
@ -259,56 +225,17 @@ impl KeyTree<ChildKeysPrivate> {
|
||||
}
|
||||
|
||||
impl KeyTree<ChildKeysPublic> {
|
||||
/// Cleanup of all non-initialized accounts in a public tree.
|
||||
///
|
||||
/// For given `depth` checks children to a tree such that their `ChainIndex::depth(&self) <
|
||||
/// depth`.
|
||||
///
|
||||
/// If account is default, removes them.
|
||||
///
|
||||
/// Fast, leaves gaps between accounts.
|
||||
pub async fn cleanup_tree_remove_ininit_for_depth(
|
||||
&mut self,
|
||||
depth: u32,
|
||||
client: Arc<SequencerClient>,
|
||||
) -> Result<()> {
|
||||
let mut id_stack = vec![ChainIndex::root()];
|
||||
|
||||
while let Some(curr_id) = id_stack.pop() {
|
||||
if let Some(node) = self.key_map.get(&curr_id) {
|
||||
let address = node.account_id();
|
||||
let node_acc = client.get_account(address).await?.account;
|
||||
|
||||
if node_acc == nssa::Account::default() && curr_id != ChainIndex::root() {
|
||||
self.remove(address);
|
||||
}
|
||||
}
|
||||
|
||||
let mut next_id = curr_id.nth_child(0);
|
||||
|
||||
while (next_id.depth()) < depth {
|
||||
id_stack.push(next_id.clone());
|
||||
next_id = match next_id.next_in_line() {
|
||||
Some(id) => id,
|
||||
None => break,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Cleanup of non-initialized accounts in a public tree.
|
||||
///
|
||||
/// If account is default, removes them, stops at first non-default account.
|
||||
///
|
||||
/// Walks through tree in lairs of same depth using `ChainIndex::chain_ids_at_depth()`.
|
||||
/// Walks through tree in layers of same depth using `ChainIndex::chain_ids_at_depth()`.
|
||||
///
|
||||
/// Slow, maintains tree consistency.
|
||||
pub async fn cleanup_tree_remove_uninit_layered(
|
||||
pub async fn cleanup_tree_remove_uninit_layered<F: Future<Output = Result<Account>>>(
|
||||
&mut self,
|
||||
depth: u32,
|
||||
client: Arc<SequencerClient>,
|
||||
get_account: impl Fn(AccountId) -> F,
|
||||
) -> Result<()> {
|
||||
let depth = usize::try_from(depth).expect("Depth is expected to fit in usize");
|
||||
'outer: for i in (1..depth).rev() {
|
||||
@ -316,7 +243,7 @@ impl KeyTree<ChildKeysPublic> {
|
||||
for id in ChainIndex::chain_ids_at_depth(i) {
|
||||
if let Some(node) = self.key_map.get(&id) {
|
||||
let address = node.account_id();
|
||||
let node_acc = client.get_account(address).await?.account;
|
||||
let node_acc = get_account(address).await?;
|
||||
|
||||
if node_acc == nssa::Account::default() {
|
||||
let addr = node.account_id();
|
||||
|
||||
@ -16,7 +16,7 @@ pub type PublicAccountSigningKey = [u8; 32];
|
||||
pub struct KeyChain {
|
||||
pub secret_spending_key: SecretSpendingKey,
|
||||
pub private_key_holder: PrivateKeyHolder,
|
||||
pub nullifer_public_key: NullifierPublicKey,
|
||||
pub nullifier_public_key: NullifierPublicKey,
|
||||
pub viewing_public_key: ViewingPublicKey,
|
||||
}
|
||||
|
||||
@ -30,13 +30,13 @@ impl KeyChain {
|
||||
|
||||
let private_key_holder = secret_spending_key.produce_private_key_holder(None);
|
||||
|
||||
let nullifer_public_key = private_key_holder.generate_nullifier_public_key();
|
||||
let nullifier_public_key = private_key_holder.generate_nullifier_public_key();
|
||||
let viewing_public_key = private_key_holder.generate_viewing_public_key();
|
||||
|
||||
Self {
|
||||
secret_spending_key,
|
||||
private_key_holder,
|
||||
nullifer_public_key,
|
||||
nullifier_public_key,
|
||||
viewing_public_key,
|
||||
}
|
||||
}
|
||||
@ -50,13 +50,13 @@ impl KeyChain {
|
||||
|
||||
let private_key_holder = secret_spending_key.produce_private_key_holder(None);
|
||||
|
||||
let nullifer_public_key = private_key_holder.generate_nullifier_public_key();
|
||||
let nullifier_public_key = private_key_holder.generate_nullifier_public_key();
|
||||
let viewing_public_key = private_key_holder.generate_viewing_public_key();
|
||||
|
||||
Self {
|
||||
secret_spending_key,
|
||||
private_key_holder,
|
||||
nullifer_public_key,
|
||||
nullifier_public_key,
|
||||
viewing_public_key,
|
||||
}
|
||||
}
|
||||
@ -93,7 +93,7 @@ mod tests {
|
||||
|
||||
// Check that key holder fields are initialized with expected types
|
||||
assert_ne!(
|
||||
account_id_key_holder.nullifer_public_key.as_ref(),
|
||||
account_id_key_holder.nullifier_public_key.as_ref(),
|
||||
&[0_u8; 32]
|
||||
);
|
||||
}
|
||||
@ -119,7 +119,7 @@ mod tests {
|
||||
|
||||
let utxo_secret_key_holder = top_secret_key_holder.produce_private_key_holder(None);
|
||||
|
||||
let nullifer_public_key = utxo_secret_key_holder.generate_nullifier_public_key();
|
||||
let nullifier_public_key = utxo_secret_key_holder.generate_nullifier_public_key();
|
||||
let viewing_public_key = utxo_secret_key_holder.generate_viewing_public_key();
|
||||
|
||||
let pub_account_signing_key = nssa::PrivateKey::new_os_random();
|
||||
@ -150,7 +150,7 @@ mod tests {
|
||||
println!("Account {:?}", account.value().to_base58());
|
||||
println!(
|
||||
"Nulifier public key {:?}",
|
||||
hex::encode(nullifer_public_key.to_byte_array())
|
||||
hex::encode(nullifier_public_key.to_byte_array())
|
||||
);
|
||||
println!(
|
||||
"Viewing public key {:?}",
|
||||
@ -183,7 +183,7 @@ mod tests {
|
||||
fn non_trivial_chain_index() {
|
||||
let keys = account_with_chain_index_2_for_tests();
|
||||
|
||||
let eph_key_holder = EphemeralKeyHolder::new(&keys.nullifer_public_key);
|
||||
let eph_key_holder = EphemeralKeyHolder::new(&keys.nullifier_public_key);
|
||||
|
||||
let key_sender = eph_key_holder.calculate_shared_secret_sender(&keys.viewing_public_key);
|
||||
let key_receiver = keys.calculate_shared_secret_receiver(
|
||||
|
||||
@ -10,16 +10,16 @@ use sha2::{Digest as _, digest::FixedOutput as _};
|
||||
|
||||
const NSSA_ENTROPY_BYTES: [u8; 32] = [0; 32];
|
||||
|
||||
#[derive(Debug)]
|
||||
/// Seed holder. Non-clonable to ensure that different holders use different seeds.
|
||||
/// Produces `TopSecretKeyHolder` objects.
|
||||
#[derive(Debug)]
|
||||
pub struct SeedHolder {
|
||||
// ToDo: Needs to be vec as serde derives is not implemented for [u8; 64]
|
||||
pub(crate) seed: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
/// Secret spending key object. Can produce `PrivateKeyHolder` objects.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct SecretSpendingKey(pub(crate) [u8; 32]);
|
||||
|
||||
pub type ViewingSecretKey = Scalar;
|
||||
@ -79,6 +79,7 @@ impl SeedHolder {
|
||||
|
||||
impl SecretSpendingKey {
|
||||
#[must_use]
|
||||
#[expect(clippy::big_endian_bytes, reason = "BIP-032 uses big endian")]
|
||||
pub fn generate_nullifier_secret_key(&self, index: Option<u32>) -> NullifierSecretKey {
|
||||
const PREFIX: &[u8; 8] = b"LEE/keys";
|
||||
const SUFFIX_1: &[u8; 1] = &[1];
|
||||
@ -93,13 +94,14 @@ impl SecretSpendingKey {
|
||||
hasher.update(PREFIX);
|
||||
hasher.update(self.0);
|
||||
hasher.update(SUFFIX_1);
|
||||
hasher.update(index.to_le_bytes());
|
||||
hasher.update(index.to_be_bytes());
|
||||
hasher.update(SUFFIX_2);
|
||||
|
||||
<NullifierSecretKey>::from(hasher.finalize_fixed())
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
#[expect(clippy::big_endian_bytes, reason = "BIP-032 uses big endian")]
|
||||
pub fn generate_viewing_secret_key(&self, index: Option<u32>) -> ViewingSecretKey {
|
||||
const PREFIX: &[u8; 8] = b"LEE/keys";
|
||||
const SUFFIX_1: &[u8; 1] = &[2];
|
||||
@ -114,7 +116,7 @@ impl SecretSpendingKey {
|
||||
hasher.update(PREFIX);
|
||||
hasher.update(self.0);
|
||||
hasher.update(SUFFIX_1);
|
||||
hasher.update(index.to_le_bytes());
|
||||
hasher.update(index.to_be_bytes());
|
||||
hasher.update(SUFFIX_2);
|
||||
|
||||
hasher.finalize_fixed().into()
|
||||
|
||||
@ -46,7 +46,7 @@ impl NSSAUserData {
|
||||
) -> bool {
|
||||
let mut check_res = true;
|
||||
for (account_id, (key, _)) in accounts_keys_map {
|
||||
let expected_account_id = nssa::AccountId::from(&key.nullifer_public_key);
|
||||
let expected_account_id = nssa::AccountId::from(&key.nullifier_public_key);
|
||||
if expected_account_id != *account_id {
|
||||
println!("{expected_account_id}, {account_id}");
|
||||
check_res = false;
|
||||
@ -66,13 +66,13 @@ impl NSSAUserData {
|
||||
) -> Result<Self> {
|
||||
if !Self::valid_public_key_transaction_pairing_check(&default_accounts_keys) {
|
||||
anyhow::bail!(
|
||||
"Key transaction pairing check not satisfied, there is account_ids, which is not derived from keys"
|
||||
"Key transaction pairing check not satisfied, there are public account_ids, which are not derived from keys"
|
||||
);
|
||||
}
|
||||
|
||||
if !Self::valid_private_key_transaction_pairing_check(&default_accounts_key_chains) {
|
||||
anyhow::bail!(
|
||||
"Key transaction pairing check not satisfied, there is account_ids, which is not derived from keys"
|
||||
"Key transaction pairing check not satisfied, there are private account_ids, which are not derived from keys"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -14,6 +14,7 @@ anyhow.workspace = true
|
||||
thiserror.workspace = true
|
||||
risc0-zkvm.workspace = true
|
||||
serde.workspace = true
|
||||
serde_with.workspace = true
|
||||
sha2.workspace = true
|
||||
rand.workspace = true
|
||||
borsh.workspace = true
|
||||
@ -37,4 +38,4 @@ test-case = "3.3.1"
|
||||
[features]
|
||||
default = []
|
||||
prove = ["risc0-zkvm/prove"]
|
||||
test-utils = []
|
||||
test-utils = []
|
||||
|
||||
@ -6,14 +6,89 @@ use std::{
|
||||
use base58::{FromBase58 as _, ToBase58 as _};
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
pub use data::Data;
|
||||
use risc0_zkvm::sha::{Impl, Sha256 as _};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::{DeserializeFromStr, SerializeDisplay};
|
||||
|
||||
use crate::program::ProgramId;
|
||||
use crate::{NullifierPublicKey, NullifierSecretKey, program::ProgramId};
|
||||
|
||||
pub mod data;
|
||||
|
||||
pub type Nonce = u128;
|
||||
#[derive(Copy, Debug, Default, Clone, Eq, PartialEq)]
|
||||
pub struct Nonce(pub u128);
|
||||
|
||||
impl Nonce {
|
||||
pub const fn public_account_nonce_increment(&mut self) {
|
||||
self.0 = self
|
||||
.0
|
||||
.checked_add(1)
|
||||
.expect("Overflow when incrementing nonce");
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn private_account_nonce_init(npk: &NullifierPublicKey) -> Self {
|
||||
let mut bytes: [u8; 64] = [0_u8; 64];
|
||||
bytes[..32].copy_from_slice(&npk.0);
|
||||
let result: [u8; 32] = Impl::hash_bytes(&bytes).as_bytes().try_into().unwrap();
|
||||
let result = result.first_chunk::<16>().unwrap();
|
||||
|
||||
Self(u128::from_le_bytes(*result))
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn private_account_nonce_increment(self, nsk: &NullifierSecretKey) -> Self {
|
||||
let mut bytes: [u8; 64] = [0_u8; 64];
|
||||
bytes[..32].copy_from_slice(nsk);
|
||||
bytes[32..48].copy_from_slice(&self.0.to_le_bytes());
|
||||
let result: [u8; 32] = Impl::hash_bytes(&bytes).as_bytes().try_into().unwrap();
|
||||
let result = result.first_chunk::<16>().unwrap();
|
||||
|
||||
Self(u128::from_le_bytes(*result))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u128> for Nonce {
|
||||
fn from(value: u128) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Nonce> for u128 {
|
||||
fn from(value: Nonce) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Nonce {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
Serialize::serialize(&self.0, serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Nonce {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
Ok(<u128 as Deserialize>::deserialize(deserializer)?.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl BorshSerialize for Nonce {
|
||||
fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
|
||||
BorshSerialize::serialize(&self.0, writer)
|
||||
}
|
||||
}
|
||||
|
||||
impl BorshDeserialize for Nonce {
|
||||
fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
|
||||
Ok(<u128 as BorshDeserialize>::deserialize_reader(reader)?.into())
|
||||
}
|
||||
}
|
||||
|
||||
pub type Balance = u128;
|
||||
|
||||
/// Account to be used both in public and private contexts.
|
||||
@ -154,7 +229,7 @@ mod tests {
|
||||
fn zero_nonce_account_data_creation() {
|
||||
let new_acc = Account::default();
|
||||
|
||||
assert_eq!(new_acc.nonce, 0);
|
||||
assert_eq!(new_acc.nonce.0, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -181,7 +256,7 @@ mod tests {
|
||||
.to_vec()
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
nonce: 0xdead_beef,
|
||||
nonce: Nonce(0xdead_beef),
|
||||
};
|
||||
let fingerprint = AccountId::new([8; 32]);
|
||||
let new_acc_with_metadata = AccountWithMetadata::new(account.clone(), true, fingerprint);
|
||||
@ -228,4 +303,52 @@ mod tests {
|
||||
let expected_account_id = AccountId::new([0; 32]);
|
||||
assert!(default_account_id == expected_account_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn initialize_private_nonce() {
|
||||
let npk = NullifierPublicKey([42; 32]);
|
||||
let nonce = Nonce::private_account_nonce_init(&npk);
|
||||
let expected_nonce = Nonce(37_937_661_125_547_691_021_612_781_941_709_513_486);
|
||||
assert_eq!(nonce, expected_nonce);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn increment_private_nonce() {
|
||||
let nsk: NullifierSecretKey = [42_u8; 32];
|
||||
let nonce = Nonce(37_937_661_125_547_691_021_612_781_941_709_513_486)
|
||||
.private_account_nonce_increment(&nsk);
|
||||
let expected_nonce = Nonce(327_300_903_218_789_900_388_409_116_014_290_259_894);
|
||||
assert_eq!(nonce, expected_nonce);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn increment_public_nonce() {
|
||||
let value = 42_u128;
|
||||
let mut nonce = Nonce(value);
|
||||
nonce.public_account_nonce_increment();
|
||||
let expected_nonce = Nonce(value + 1);
|
||||
assert_eq!(nonce, expected_nonce);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serde_roundtrip_for_nonce() {
|
||||
let nonce: Nonce = 7_u128.into();
|
||||
|
||||
let serde_serialized_nonce = serde_json::to_vec(&nonce).unwrap();
|
||||
|
||||
let nonce_restored = serde_json::from_slice(&serde_serialized_nonce).unwrap();
|
||||
|
||||
assert_eq!(nonce, nonce_restored);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn borsh_roundtrip_for_nonce() {
|
||||
let nonce: Nonce = 7_u128.into();
|
||||
|
||||
let borsh_serialized_nonce = borsh::to_vec(&nonce).unwrap();
|
||||
|
||||
let nonce_restored = borsh::from_slice(&borsh_serialized_nonce).unwrap();
|
||||
|
||||
assert_eq!(nonce, nonce_restored);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};
|
||||
use crate::{
|
||||
Commitment, CommitmentSetDigest, MembershipProof, Nullifier, NullifierPublicKey,
|
||||
NullifierSecretKey, SharedSecretKey,
|
||||
account::{Account, AccountWithMetadata, Nonce},
|
||||
account::{Account, AccountWithMetadata},
|
||||
encryption::Ciphertext,
|
||||
program::{ProgramId, ProgramOutput},
|
||||
};
|
||||
@ -18,8 +18,6 @@ pub struct PrivacyPreservingCircuitInput {
|
||||
/// - `1` - private account with authentication
|
||||
/// - `2` - private account without authentication
|
||||
pub visibility_mask: Vec<u8>,
|
||||
/// Nonces of private accounts.
|
||||
pub private_account_nonces: Vec<Nonce>,
|
||||
/// Public keys of private accounts.
|
||||
pub private_account_keys: Vec<(NullifierPublicKey, SharedSecretKey)>,
|
||||
/// Nullifier secret keys for authorized private accounts.
|
||||
@ -57,7 +55,7 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
Commitment, Nullifier, NullifierPublicKey,
|
||||
account::{Account, AccountId, AccountWithMetadata},
|
||||
account::{Account, AccountId, AccountWithMetadata, Nonce},
|
||||
};
|
||||
|
||||
#[test]
|
||||
@ -69,7 +67,7 @@ mod tests {
|
||||
program_owner: [1, 2, 3, 4, 5, 6, 7, 8],
|
||||
balance: 12_345_678_901_234_567_890,
|
||||
data: b"test data".to_vec().try_into().unwrap(),
|
||||
nonce: 0xFFFF_FFFF_FFFF_FFFE,
|
||||
nonce: Nonce(0xFFFF_FFFF_FFFF_FFFE),
|
||||
},
|
||||
true,
|
||||
AccountId::new([0; 32]),
|
||||
@ -79,7 +77,7 @@ mod tests {
|
||||
program_owner: [9, 9, 9, 8, 8, 8, 7, 7],
|
||||
balance: 123_123_123_456_456_567_112,
|
||||
data: b"test data".to_vec().try_into().unwrap(),
|
||||
nonce: 9_999_999_999_999_999_999_999,
|
||||
nonce: Nonce(9_999_999_999_999_999_999_999),
|
||||
},
|
||||
false,
|
||||
AccountId::new([1; 32]),
|
||||
@ -89,7 +87,7 @@ mod tests {
|
||||
program_owner: [1, 2, 3, 4, 5, 6, 7, 8],
|
||||
balance: 100,
|
||||
data: b"post state data".to_vec().try_into().unwrap(),
|
||||
nonce: 0xFFFF_FFFF_FFFF_FFFF,
|
||||
nonce: Nonce(0xFFFF_FFFF_FFFF_FFFF),
|
||||
}],
|
||||
ciphertexts: vec![Ciphertext(vec![255, 255, 1, 1, 2, 2])],
|
||||
new_commitments: vec![Commitment::new(
|
||||
|
||||
@ -12,8 +12,8 @@ use crate::{NullifierPublicKey, account::Account};
|
||||
/// DUMMY_COMMITMENT = hasher.digest()
|
||||
/// ```
|
||||
pub const DUMMY_COMMITMENT: Commitment = Commitment([
|
||||
130, 75, 48, 230, 171, 101, 121, 141, 159, 118, 21, 74, 135, 248, 16, 255, 238, 156, 61, 24,
|
||||
165, 33, 34, 172, 227, 30, 215, 20, 85, 47, 230, 29,
|
||||
55, 228, 215, 207, 112, 221, 239, 49, 238, 79, 71, 135, 155, 15, 184, 45, 104, 74, 51, 211,
|
||||
238, 42, 160, 243, 15, 124, 253, 62, 3, 229, 90, 27,
|
||||
]);
|
||||
|
||||
/// The hash of the dummy commitment.
|
||||
@ -24,8 +24,8 @@ pub const DUMMY_COMMITMENT: Commitment = Commitment([
|
||||
/// DUMMY_COMMITMENT_HASH = hasher.digest()
|
||||
/// ```
|
||||
pub const DUMMY_COMMITMENT_HASH: [u8; 32] = [
|
||||
170, 10, 217, 228, 20, 35, 189, 177, 238, 235, 97, 129, 132, 89, 96, 247, 86, 91, 222, 214, 38,
|
||||
194, 216, 67, 56, 251, 208, 226, 0, 117, 149, 39,
|
||||
250, 237, 192, 113, 155, 101, 119, 30, 235, 183, 20, 84, 26, 32, 196, 229, 154, 74, 254, 249,
|
||||
129, 241, 118, 39, 41, 253, 141, 171, 184, 71, 8, 41,
|
||||
];
|
||||
|
||||
#[derive(Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
|
||||
@ -50,10 +50,14 @@ impl std::fmt::Debug for Commitment {
|
||||
|
||||
impl Commitment {
|
||||
/// Generates the commitment to a private account owned by user for npk:
|
||||
/// SHA256(npk || `program_owner` || balance || nonce || SHA256(data)).
|
||||
/// SHA256( `Comm_DS` || npk || `program_owner` || balance || nonce || SHA256(data)).
|
||||
#[must_use]
|
||||
pub fn new(npk: &NullifierPublicKey, account: &Account) -> Self {
|
||||
const COMMITMENT_PREFIX: &[u8; 32] =
|
||||
b"/LEE/v0.3/Commitment/\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
|
||||
|
||||
let mut bytes = Vec::new();
|
||||
bytes.extend_from_slice(COMMITMENT_PREFIX);
|
||||
bytes.extend_from_slice(&npk.to_byte_array());
|
||||
let account_bytes_with_hashed_data = {
|
||||
let mut this = Vec::new();
|
||||
@ -61,7 +65,7 @@ impl Commitment {
|
||||
this.extend_from_slice(&word.to_le_bytes());
|
||||
}
|
||||
this.extend_from_slice(&account.balance.to_le_bytes());
|
||||
this.extend_from_slice(&account.nonce.to_le_bytes());
|
||||
this.extend_from_slice(&account.nonce.0.to_le_bytes());
|
||||
let hashed_data: [u8; 32] = Impl::hash_bytes(&account.data)
|
||||
.as_bytes()
|
||||
.try_into()
|
||||
|
||||
@ -25,8 +25,8 @@ impl Account {
|
||||
bytes.extend_from_slice(&word.to_le_bytes());
|
||||
}
|
||||
bytes.extend_from_slice(&self.balance.to_le_bytes());
|
||||
bytes.extend_from_slice(&self.nonce.to_le_bytes());
|
||||
let data_length: u32 = u32::try_from(self.data.len()).expect("data length fits in u32");
|
||||
bytes.extend_from_slice(&self.nonce.0.to_le_bytes());
|
||||
let data_length: u32 = u32::try_from(self.data.len()).expect("Invalid u32");
|
||||
bytes.extend_from_slice(&data_length.to_le_bytes());
|
||||
bytes.extend_from_slice(self.data.as_ref());
|
||||
bytes
|
||||
@ -35,7 +35,7 @@ impl Account {
|
||||
/// Deserializes an account from a cursor.
|
||||
#[cfg(feature = "host")]
|
||||
pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result<Self, NssaCoreError> {
|
||||
use crate::account::data::Data;
|
||||
use crate::account::{Nonce, data::Data};
|
||||
|
||||
let mut u32_bytes = [0_u8; 4];
|
||||
let mut u128_bytes = [0_u8; 16];
|
||||
@ -53,7 +53,7 @@ impl Account {
|
||||
|
||||
// nonce
|
||||
cursor.read_exact(&mut u128_bytes)?;
|
||||
let nonce = u128::from_le_bytes(u128_bytes);
|
||||
let nonce = Nonce(u128::from_le_bytes(u128_bytes));
|
||||
|
||||
// data
|
||||
let data = Data::from_cursor(cursor)?;
|
||||
@ -189,7 +189,7 @@ mod tests {
|
||||
let account = Account {
|
||||
program_owner: [1, 2, 3, 4, 5, 6, 7, 8],
|
||||
balance: 123_456_789_012_345_678_901_234_567_890_123_456,
|
||||
nonce: 42,
|
||||
nonce: 42_u128.into(),
|
||||
data: b"hola mundo".to_vec().try_into().unwrap(),
|
||||
};
|
||||
|
||||
@ -250,7 +250,7 @@ mod tests {
|
||||
let account = Account {
|
||||
program_owner: [1, 2, 3, 4, 5, 6, 7, 8],
|
||||
balance: 123_456_789_012_345_678_901_234_567_890_123_456,
|
||||
nonce: 42,
|
||||
nonce: 42_u128.into(),
|
||||
data: b"hola mundo".to_vec().try_into().unwrap(),
|
||||
};
|
||||
let bytes = account.to_bytes();
|
||||
|
||||
@ -76,7 +76,7 @@ impl Nullifier {
|
||||
/// Computes a nullifier for an account update.
|
||||
#[must_use]
|
||||
pub fn for_account_update(commitment: &Commitment, nsk: &NullifierSecretKey) -> Self {
|
||||
const UPDATE_PREFIX: &[u8; 32] = b"/NSSA/v0.2/Nullifier/Update/\x00\x00\x00\x00";
|
||||
const UPDATE_PREFIX: &[u8; 32] = b"/LEE/v0.3/Nullifier/Update/\x00\x00\x00\x00\x00";
|
||||
let mut bytes = UPDATE_PREFIX.to_vec();
|
||||
bytes.extend_from_slice(&commitment.to_byte_array());
|
||||
bytes.extend_from_slice(nsk);
|
||||
@ -86,7 +86,7 @@ impl Nullifier {
|
||||
/// Computes a nullifier for an account initialization.
|
||||
#[must_use]
|
||||
pub fn for_account_initialization(npk: &NullifierPublicKey) -> Self {
|
||||
const INIT_PREFIX: &[u8; 32] = b"/NSSA/v0.2/Nullifier/Initialize/";
|
||||
const INIT_PREFIX: &[u8; 32] = b"/LEE/v0.3/Nullifier/Initialize/\x00";
|
||||
let mut bytes = INIT_PREFIX.to_vec();
|
||||
bytes.extend_from_slice(&npk.to_byte_array());
|
||||
Self(Impl::hash_bytes(&bytes).as_bytes().try_into().unwrap())
|
||||
@ -102,8 +102,8 @@ mod tests {
|
||||
let commitment = Commitment((0..32_u8).collect::<Vec<_>>().try_into().unwrap());
|
||||
let nsk = [0x42; 32];
|
||||
let expected_nullifier = Nullifier([
|
||||
148, 243, 116, 209, 140, 231, 211, 61, 35, 62, 114, 110, 143, 224, 82, 201, 221, 34,
|
||||
53, 80, 185, 48, 174, 28, 203, 43, 94, 187, 85, 199, 115, 81,
|
||||
70, 162, 122, 15, 33, 237, 244, 216, 89, 223, 90, 50, 94, 184, 210, 144, 174, 64, 189,
|
||||
254, 62, 255, 5, 1, 139, 227, 194, 185, 16, 30, 55, 48,
|
||||
]);
|
||||
let nullifier = Nullifier::for_account_update(&commitment, &nsk);
|
||||
assert_eq!(nullifier, expected_nullifier);
|
||||
@ -116,8 +116,8 @@ mod tests {
|
||||
255, 29, 105, 42, 186, 43, 11, 157, 168, 132, 225, 17, 163,
|
||||
]);
|
||||
let expected_nullifier = Nullifier([
|
||||
1, 6, 59, 168, 16, 146, 65, 252, 255, 91, 48, 85, 116, 189, 110, 218, 110, 136, 163,
|
||||
193, 245, 103, 51, 27, 235, 170, 215, 115, 97, 144, 36, 238,
|
||||
149, 59, 95, 181, 2, 194, 20, 143, 72, 233, 104, 243, 59, 70, 67, 243, 110, 77, 109,
|
||||
132, 139, 111, 51, 125, 128, 92, 107, 46, 252, 4, 20, 149,
|
||||
]);
|
||||
let nullifier = Nullifier::for_account_initialization(&npk);
|
||||
assert_eq!(nullifier, expected_nullifier);
|
||||
|
||||
@ -348,7 +348,7 @@ mod tests {
|
||||
program_owner: [1, 2, 3, 4, 5, 6, 7, 8],
|
||||
balance: 1337,
|
||||
data: vec![0xde, 0xad, 0xbe, 0xef].try_into().unwrap(),
|
||||
nonce: 10,
|
||||
nonce: 10_u128.into(),
|
||||
};
|
||||
|
||||
let account_post_state = AccountPostState::new_claimed(account.clone());
|
||||
@ -363,7 +363,7 @@ mod tests {
|
||||
program_owner: [1, 2, 3, 4, 5, 6, 7, 8],
|
||||
balance: 1337,
|
||||
data: vec![0xde, 0xad, 0xbe, 0xef].try_into().unwrap(),
|
||||
nonce: 10,
|
||||
nonce: 10_u128.into(),
|
||||
};
|
||||
|
||||
let account_post_state = AccountPostState::new(account.clone());
|
||||
@ -378,7 +378,7 @@ mod tests {
|
||||
program_owner: [1, 2, 3, 4, 5, 6, 7, 8],
|
||||
balance: 1337,
|
||||
data: vec![0xde, 0xad, 0xbe, 0xef].try_into().unwrap(),
|
||||
nonce: 10,
|
||||
nonce: 10_u128.into(),
|
||||
};
|
||||
|
||||
let mut account_post_state = AccountPostState::new(account.clone());
|
||||
|
||||
@ -16,7 +16,7 @@ pub use program_deployment_transaction::ProgramDeploymentTransaction;
|
||||
pub use program_methods::PRIVACY_PRESERVING_CIRCUIT_ID;
|
||||
pub use public_transaction::PublicTransaction;
|
||||
pub use signature::{PrivateKey, PublicKey, Signature};
|
||||
pub use state::V02State;
|
||||
pub use state::V03State;
|
||||
|
||||
pub mod encoding;
|
||||
pub mod error;
|
||||
|
||||
@ -63,12 +63,11 @@ impl From<Program> for ProgramWithDependencies {
|
||||
|
||||
/// Generates a proof of the execution of a NSSA program inside the privacy preserving execution
|
||||
/// circuit.
|
||||
#[expect(clippy::too_many_arguments, reason = "TODO: fix later")]
|
||||
/// TODO: too many parameters.
|
||||
pub fn execute_and_prove(
|
||||
pre_states: Vec<AccountWithMetadata>,
|
||||
instruction_data: InstructionData,
|
||||
visibility_mask: Vec<u8>,
|
||||
private_account_nonces: Vec<u128>,
|
||||
private_account_keys: Vec<(NullifierPublicKey, SharedSecretKey)>,
|
||||
private_account_nsks: Vec<NullifierSecretKey>,
|
||||
private_account_membership_proofs: Vec<Option<MembershipProof>>,
|
||||
@ -127,7 +126,6 @@ pub fn execute_and_prove(
|
||||
let circuit_input = PrivacyPreservingCircuitInput {
|
||||
program_outputs,
|
||||
visibility_mask,
|
||||
private_account_nonces,
|
||||
private_account_keys,
|
||||
private_account_nsks,
|
||||
private_account_membership_proofs,
|
||||
@ -177,7 +175,7 @@ mod tests {
|
||||
|
||||
use nssa_core::{
|
||||
Commitment, DUMMY_COMMITMENT_HASH, EncryptionScheme, Nullifier,
|
||||
account::{Account, AccountId, AccountWithMetadata, data::Data},
|
||||
account::{Account, AccountId, AccountWithMetadata, Nonce, data::Data},
|
||||
};
|
||||
|
||||
use super::*;
|
||||
@ -215,14 +213,14 @@ mod tests {
|
||||
let expected_sender_post = Account {
|
||||
program_owner: program.id(),
|
||||
balance: 100 - balance_to_move,
|
||||
nonce: 0,
|
||||
nonce: Nonce::default(),
|
||||
data: Data::default(),
|
||||
};
|
||||
|
||||
let expected_recipient_post = Account {
|
||||
program_owner: program.id(),
|
||||
balance: balance_to_move,
|
||||
nonce: 0xdead_beef,
|
||||
nonce: Nonce::private_account_nonce_init(&recipient_keys.npk()),
|
||||
data: Data::default(),
|
||||
};
|
||||
|
||||
@ -235,7 +233,6 @@ mod tests {
|
||||
vec![sender, recipient],
|
||||
Program::serialize_instruction(balance_to_move).unwrap(),
|
||||
vec![0, 2],
|
||||
vec![0xdead_beef],
|
||||
vec![(recipient_keys.npk(), shared_secret)],
|
||||
vec![],
|
||||
vec![None],
|
||||
@ -269,10 +266,11 @@ mod tests {
|
||||
let sender_keys = test_private_account_keys_1();
|
||||
let recipient_keys = test_private_account_keys_2();
|
||||
|
||||
let sender_nonce = Nonce(0xdead_beef);
|
||||
let sender_pre = AccountWithMetadata::new(
|
||||
Account {
|
||||
balance: 100,
|
||||
nonce: 0xdead_beef,
|
||||
nonce: sender_nonce,
|
||||
program_owner: program.id(),
|
||||
data: Data::default(),
|
||||
},
|
||||
@ -307,13 +305,13 @@ mod tests {
|
||||
let expected_private_account_1 = Account {
|
||||
program_owner: program.id(),
|
||||
balance: 100 - balance_to_move,
|
||||
nonce: 0xdead_beef1,
|
||||
nonce: sender_nonce.private_account_nonce_increment(&sender_keys.nsk),
|
||||
..Default::default()
|
||||
};
|
||||
let expected_private_account_2 = Account {
|
||||
program_owner: program.id(),
|
||||
balance: balance_to_move,
|
||||
nonce: 0xdead_beef2,
|
||||
nonce: Nonce::private_account_nonce_init(&recipient_keys.npk()),
|
||||
..Default::default()
|
||||
};
|
||||
let expected_new_commitments = vec![
|
||||
@ -331,7 +329,6 @@ mod tests {
|
||||
vec![sender_pre, recipient],
|
||||
Program::serialize_instruction(balance_to_move).unwrap(),
|
||||
vec![1, 2],
|
||||
vec![0xdead_beef1, 0xdead_beef2],
|
||||
vec![
|
||||
(sender_keys.npk(), shared_secret_1),
|
||||
(recipient_keys.npk(), shared_secret_2),
|
||||
|
||||
@ -32,11 +32,11 @@ impl EncryptedAccountData {
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes the tag as the first byte of SHA256("/NSSA/v0.2/ViewTag/" || Npk || vpk).
|
||||
/// Computes the tag as the first byte of SHA256("/LEE/v0.3/ViewTag/" || Npk || vpk).
|
||||
#[must_use]
|
||||
pub fn compute_view_tag(npk: &NullifierPublicKey, vpk: &ViewingPublicKey) -> ViewTag {
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(b"/NSSA/v0.2/ViewTag/");
|
||||
hasher.update(b"/LEE/v0.3/ViewTag/");
|
||||
hasher.update(npk.to_byte_array());
|
||||
hasher.update(vpk.to_bytes());
|
||||
let digest: [u8; 32] = hasher.finalize().into();
|
||||
@ -140,7 +140,7 @@ pub mod tests {
|
||||
|
||||
let public_account_ids = vec![AccountId::new([1; 32])];
|
||||
|
||||
let nonces = vec![1, 2, 3];
|
||||
let nonces = vec![1_u128.into(), 2_u128.into(), 3_u128.into()];
|
||||
|
||||
let public_post_states = vec![Account::default()];
|
||||
|
||||
@ -179,7 +179,7 @@ pub mod tests {
|
||||
|
||||
let expected_view_tag = {
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(b"/NSSA/v0.2/ViewTag/");
|
||||
hasher.update(b"/LEE/v0.3/ViewTag/");
|
||||
hasher.update(npk.to_byte_array());
|
||||
hasher.update(vpk.to_bytes());
|
||||
let digest: [u8; 32] = hasher.finalize().into();
|
||||
|
||||
@ -12,7 +12,7 @@ use sha2::{Digest as _, digest::FixedOutput as _};
|
||||
|
||||
use super::{message::Message, witness_set::WitnessSet};
|
||||
use crate::{
|
||||
AccountId, V02State,
|
||||
AccountId, V03State,
|
||||
error::NssaError,
|
||||
privacy_preserving_transaction::{circuit::Proof, message::EncryptedAccountData},
|
||||
};
|
||||
@ -34,7 +34,7 @@ impl PrivacyPreservingTransaction {
|
||||
|
||||
pub(crate) fn validate_and_produce_public_state_diff(
|
||||
&self,
|
||||
state: &V02State,
|
||||
state: &V03State,
|
||||
) -> Result<HashMap<AccountId, Account>, NssaError> {
|
||||
let message = &self.message;
|
||||
let witness_set = &self.witness_set;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user