Compare commits

...

705 Commits
v0.1 ... main

Author SHA1 Message Date
Daniil Polyakov
18b88a6018
Merge pull request #498 from logos-blockchain/arjentix/speed-up-ci
feat(ci): speed up ci
2026-05-25 23:42:44 +03:00
Moudy
867d328cf3
Merge pull request #494 from logos-blockchain/moudy/bench-regression 2026-05-25 16:15:14 +02:00
Moudy
7d69254b3e ci: add bench-regression workflow with criterion-compare for crypto_primitives_bench 2026-05-25 14:00:20 +02:00
Daniil Polyakov
006647bc83
Merge pull request #497 from logos-blockchain/arjentix/expand-contributing-guide
docs(contributing): require fill PR template
2026-05-23 00:45:00 +03:00
Daniil Polyakov
23f427246d
Merge pull request #496 from logos-blockchain/arjentix/fix-risc0-features
fix: apply right features for risc0-zkvm
2026-05-23 00:44:43 +03:00
Daniil Polyakov
ac2d01e1b4 feat(ci): build integration tests binary once and reuse it 2026-05-22 20:09:35 +03:00
Daniil Polyakov
adf0d241c8 feat(ci): cache rust artifacts 2026-05-22 19:28:04 +03:00
Daniil Polyakov
b5cecdebc0 feat(ci): use separate job per each integration tests module 2026-05-22 19:27:47 +03:00
Daniil Polyakov
fa47d471af fix: disable default features for risc0-zkvm for workspace and enable client feature for nssa 2026-05-22 18:23:47 +03:00
Daniil Polyakov
bcd8577370 docs(contributing): add requirement to fill PR template before marking it as ready for review 2026-05-22 18:19:27 +03:00
Daniil Polyakov
7546e22cf6
Merge pull request #495 from logos-blockchain/Pravdyvy/revert-490
revert(490)
2026-05-22 15:47:57 +03:00
Pravdyvy
5f14ac1cfe revert(490): CI test 2026-05-22 07:28:49 +03:00
jonesmarvin8
cf9177a095
feat(wallet): add keycard support for public tx for auth-transfer (#451)
* feat: add basic commands for communicating with keycard

* initialize changes

* reorganization

* add script file for easier wallet access

* update commands

* fixes

* fixed load for non continuous run

* Updates for signatures with keycard

* fix BIP-340 signatures for fixed sized messages

* fmt

* refactor and add pin support to program facades

* fix unit test

* fixes

* Revert "fixes"

This reverts commit 41f34f4ff4145b7abb60fd9bec168ae4b60f23b4.

* fixes

* fixes

* Removed privacy keycard calls

* Revert "Removed privacy keycard calls"

This reverts commit d70ef505a1f40b87159099761f5fce5a31e3f17b.

* Add domain separators

* Removed privacy txs for keycard

* CI fixes

* CI fixes

* addressed some comments

* fix ci

* ci fixes

* fix integration test issue and updated keycard firmware

* addressed more comments

* fixed deny

* remove keycard-py

* fixed from earlier merge

* add hash_message tests

* add test

* fix deny

* CI fixes

* fixed integration tests

* Update public.rs

* update artifacts

* ci and comments

* addressed comments

* comment fixes

* fixes from merging main

* first round of comments

* Revert "Merge branch 'main' into marvin/keycard-commands"

This reverts commit 3fce53f663a3996938dddf77680854570063ca21, reversing
changes made to e7b42a5177641455a8917bd2e29db20afd9690e5.

* python comments

* addressed comments

* compile error fixed

* fix artifacts

* fix main merge error

* adjust signer logic workflow

* fmt

* merge main and shift keycard tests

* deny fix

* artifacts fix

* remove keycard scripts from root

* tps fix

* fmt
2026-05-21 20:46:13 -04:00
Moudy
5543e125ee
Merge pull request #488 from logos-blockchain/moudy/bench-criterion
feat: migrate bench tools to criterion harness
2026-05-21 21:41:25 +02:00
Moudy
ebfc3e5ad2 chore(deny): fix 2026-05-21 16:53:44 +02:00
Moudy
fdec52791d refactor(crypto_primitives_bench): derive account_id from key in encryption bench 2026-05-21 16:44:41 +02:00
Moudy
d064f87ad7 refactor: lift criterion html_reports feature to workspace declaration 2026-05-21 16:44:41 +02:00
Moudy
a9bf3fbfe7 feat(cycle_bench): add criterion verify bench for G_verify 2026-05-21 16:44:40 +02:00
Moudy
fb89e7549b refactor(cycle_bench): split into lib + binary, drop hand-rolled verify timing 2026-05-21 16:44:40 +02:00
Moudy
b608d10ca1 feat(crypto_primitives_bench): migrate to criterion harness 2026-05-21 16:44:40 +02:00
jonesmarvin8
694e484228
fix(nssa): audit 91 issue fix (#489)
* address audit-issue-91

* add privacy test version

* addressed comments
2026-05-21 09:00:27 -04:00
Daniil Polyakov
bc852925d4
Merge pull request #490 from ygd58/fix/disable-risc0-zkvm-default-features-v3
fix(workspace): disable risc0-zkvm default features to avoid ring in guest builds
2026-05-21 00:06:09 +03:00
ygd58
94096bcdc6
fix(workspace): disable risc0-zkvm default features to avoid ring in guest builds
- Cargo.toml: add default-features = false to risc0-zkvm
- nssa/Cargo.toml: add explicit prove feature for ExecutorEnv/default_prover
- Regenerate artifacts

Fixes #468
2026-05-20 17:23:51 +02:00
Moudy
bfdc087680
Merge pull request #487 from logos-blockchain/moudy/e2e-bench-tool
feat!: add integration_bench tool for end-to-end scenario latency, block, and tx-byte measurements
2026-05-20 16:05:52 +02:00
Moudy
715d52f605 chore(workspace): drop integration_tests workspace dep and clean test_fixtures docstring 2026-05-20 16:04:53 +02:00
moudyellaz
33b20bb480 ci(integration_bench): apply nightly rustfmt and drop integration_tests unused deps 2026-05-20 13:08:48 +02:00
moudyellaz
b0a5b3478b docs(integration_bench): add canonical run numbers from docker-compose sweep 2026-05-20 12:58:25 +02:00
moudyellaz
ab77c5d26a refactor(integration_bench): ScenarioOutput::step closure helper 2026-05-20 12:19:43 +02:00
moudyellaz
0119b38c1b refactor(integration_bench)!: pivot to docker-compose via TestContext, share one node per run
BREAKING CHANGE:
- crate renamed e2e_bench → integration_bench. Run via `cargo run -p integration_bench`.
- env vars removed: LEZ_BEDROCK_BIN, LEZ_BEDROCK_CONFIG_DIR, LEZ_BEDROCK_PORT. Replaced by a docker prerequisite (docker-compose Bedrock via test_fixtures::TestContext).
- output filenames: target/e2e_bench_{dev,prove}.json → target/integration_bench_{dev,prove}.json.
- JSON schema: per-scenario `setup_s` field removed; replaced by run-level `shared_setup_s` (one TestContext is shared across all scenarios in a run).
- internal: bedrock_handle.rs and bench_context.rs deleted; placeholder-string config (PLACEHOLDER_CHAIN_START_TIME) gone.
2026-05-20 11:04:06 +02:00
moudyellaz
563a9ce0f7 refactor: extract test_fixtures crate from integration_tests 2026-05-20 10:08:24 +02:00
moudyellaz
932763fcf2 refactor(e2e_bench): rename ScenarioResult to ScenarioOutput 2026-05-19 23:48:05 +02:00
moudyellaz
619db3846d refactor(e2e_bench)!: Duration-typed timings, seconds-float JSON, tokio::timeout
BREAKING CHANGE: bench JSON renames per-step / per-scenario timing fields from *_ms (float milliseconds) to *_s (float seconds). Renames: submit_ms → submit_s, inclusion_ms → inclusion_s, wallet_sync_ms → wallet_sync_s, total_ms → total_s, setup_ms → setup_s, bedrock_finality_ms → bedrock_finality_s, total_wall_seconds → total_wall_s. measure_bedrock_finality timeout floor also shifts slightly: on timeout the field is now ~60.000s rather than "first poll tick past 60s".
2026-05-19 22:59:02 +02:00
Sergio Chouhy
c0e837b65d
Merge pull request #479 from logos-blockchain/schouhy/fix-faucet-account-protection-mechanism
fix: Bug in faucet account protection mechanism
2026-05-19 16:36:29 -03:00
moudyellaz
c3daa9897d docs(e2e_bench): drop machine table and stale benchmark numbers 2026-05-19 18:54:11 +02:00
Moudy
832b21f74d fix: cli 2026-05-19 09:59:11 +02:00
Moudy
20b9868ace feat: add e2e_bench tool for end-to-end scenario latency, block, and tx-byte measurements 2026-05-19 09:45:26 +02:00
Daniil Polyakov
534b0f8ee1
Merge pull request #483 from logos-blockchain/arjentix/contributing-guide
docs(contributing): add CONTRIBUTING.md
2026-05-18 22:46:45 +03:00
Moudy
aa53e591d8
Merge pull request #480 from logos-blockchain/moudy/wallet-crypto-bench-tool 2026-05-18 20:06:21 +02:00
Daniil Polyakov
34b6e34642 docs(contributing): add CONTRIBUTING.md 2026-05-18 20:19:11 +03:00
Moudy
0ab3075e78 Merge remote-tracking branch 'origin/main' into moudy/wallet-crypto-bench-tool
# Conflicts:
#	Cargo.toml
#	docs/benchmarks/README.md
2026-05-18 18:25:58 +02:00
Moudy
3877b216e0
Merge pull request #478 from logos-blockchain/moudy/cycle-bench-tool
feat: add cycle_bench tool for executor, prove, PPE, and verify cycle measurements
2026-05-18 17:56:22 +02:00
Moudy
dbe8ac6160 chore(crypto_primitives_bench): switch allow to expect, fix doc_markdown inline 2026-05-18 17:36:07 +02:00
Moudy
8960df04d6 fix: fmt 2026-05-18 17:17:58 +02:00
Moudy
ba65b168dd rename(wallet_crypto_bench): rename to crypto_primitives_bench 2026-05-18 17:13:07 +02:00
Moudy
87170b93b0 refactor(cycle_bench): collapse 9 inline run_case calls into Case struct + iterator
- Introduce `struct Case` holding pre-serialized InstructionData,
  with new<I: Serialize>(...) -> Result<Self> constructor and
  fn run(self, prove, exec_iters) -> Result<BenchResult>.
- Replace 9 inline `run_case(...)?` push calls in main() with
  [Case::new(...)?, ...].into_iter().map(|c| c.run(prove, exec_iters))
  .collect::<Result<Vec<_>>>()?.
- Drop now-unused `needless_pass_by_value` and `too_many_arguments`
  from the crate-level #![expect] block.
2026-05-18 16:48:23 +02:00
Moudy
b84a3e8b44 docs(cycle_bench): document Stats fields and use Display instead of ::format()
- Add /// doc comments on Stats {n, best_ms, mean_ms, stdev_ms}
  clarifying units, semantics, and Bessel's correction.
- Replace pub fn format(&self) -> String with impl fmt::Display for
  Stats, idiomatic and lets println! use {} directly.
- Update three call sites accordingly.
2026-05-18 16:37:11 +02:00
Moudy
28db42315b chore(cycle_bench): tighten lint discipline (allow → expect, fix issues in code)
- Switch crate-level #![allow] to #![expect] in main.rs and prune
  17 entries the compiler reports as unfulfilled or workspace-allowed.
- Fix the underlying issues rather than allow: source ordering
  (mods/uses regrouped), doc_markdown (identifiers backticked),
  redundant_type_annotations, map_unwrap_or, unnecessary_wraps.
- Extract feature-gated mod ppe_impl into its own file
  tools/cycle_bench/src/ppe/ppe_impl.rs so the mod declaration can
  precede the public structs per arbitrary_source_item_ordering.

Net: 35 → 12 expects in main.rs, all load-bearing.
2026-05-18 15:49:24 +02:00
Sergio Chouhy
518c0e0205 remove test. Now directly modifying faucet account is forbidden 2026-05-15 21:07:17 -03:00
Sergio Chouhy
0e177f1eba replace unit tests with integration tests 2026-05-15 21:04:09 -03:00
Sergio Chouhy
58226fd0f7 fix test 2026-05-15 20:11:11 -03:00
Sergio Chouhy
57173cc140 make authorization propagate transitively through chain calls in the circuit like in the public execution 2026-05-15 17:24:24 -03:00
Moudy
891b23c18a fix: ci 2026-05-15 15:27:30 +02:00
Moudy
4a8825e63c fix: ci 2026-05-15 12:53:04 +02:00
Moudy
9efc26495b refactor: use canonical program IDs from nssa::program_methods 2026-05-15 12:19:49 +02:00
Moudy
84a1fec942 feat: add wallet_crypto_bench tool for wallet-side cryptographic primitives 2026-05-15 10:51:51 +02:00
Sergio Chouhy
2ae9e4da7f add tests and fix mechanism 2026-05-15 00:43:45 -03:00
Daniil Polyakov
4079b0c9c8
Merge pull request #450 from logos-blockchain/arjentix/move-configurable-initial-data-to-genesis
feat: move configurable initial data to genesis
2026-05-15 02:30:57 +03:00
Daniil Polyakov
8c8f5b57af fixup! refactor: use faucet program to manage faucet account 2026-05-15 01:46:39 +03:00
Daniil Polyakov
7e31aa39e3 fix(ci): increase integration-tests timeout 2026-05-15 01:34:07 +03:00
Daniil Polyakov
f721a00bdf fix: proper account authorization propagation 2026-05-15 01:34:07 +03:00
Daniil Polyakov
ee5a98fc48 refactor: use faucet program to manage faucet account 2026-05-15 01:34:07 +03:00
Daniil Polyakov
e359c1abe2 refactor: better check for db existence 2026-05-15 01:34:01 +03:00
Daniil Polyakov
879cd5096a fix: update configuration files 2026-05-15 01:34:01 +03:00
Daniil Polyakov
9075f30f19 refactor: use system faucet and vaults to supply accounts from genesis
Co-authored-by: Copilot <copilot@github.com>
2026-05-15 01:34:01 +03:00
Daniil Polyakov
89d1b95738 refactor: mark WalletFfiError as must_use
Co-authored-by: Copilot <copilot@github.com>
2026-05-15 01:33:50 +03:00
Daniil Polyakov
cff019dca4 fix: increase valid-proof-test timeout 2026-05-15 01:33:50 +03:00
Daniil Polyakov
5f207a3f02 feat: move initial accounts data into genesis 2026-05-15 01:33:50 +03:00
Moudy
e74fe36866
Update README.md 2026-05-15 00:24:56 +02:00
Moudy
75e1cc51d5
Update README.md 2026-05-15 00:24:16 +02:00
Moudy
1e2d41f941
Update cycle_bench.md 2026-05-15 00:23:49 +02:00
Moudy
e998ec7b99
Merge pull request #469 from logos-blockchain/moudy/refactor-ppc-submodules
refactor(privacy_preserving_circuit)!: split guest into bin-dir submodules
2026-05-15 00:18:32 +02:00
moudyellaz
ba84ba60ce fix(privacy_preserving_circuit): refresh artifact 2026-05-14 23:45:52 +02:00
moudyellaz
7679c50ea8 fix(privacy_preserving_circuit): lint 2026-05-14 23:07:15 +02:00
Moudy
2870bc364b fix: allow non_ascii_literal and redundant_type_annotations clippy lints 2026-05-14 18:58:53 +02:00
Moudy
5f30e382d1 fix: satisfy nightly rustfmt and pedantic clippy in cycle_bench 2026-05-14 18:43:26 +02:00
moudyellaz
3c8ff78319 merge: resolve conflicts with main 2026-05-14 18:40:49 +02:00
Moudy
05f41f81e9 feat: add cycle_bench tool for executor, prove, PPE, and verify cycle measurements 2026-05-14 18:14:37 +02:00
moudyellaz
497a8fa761 fix(ci): drop duplicate deny.toml and add hickory advisories to .deny.toml 2026-05-14 16:25:36 +02:00
Pravdyvy
41fa494e32
Merge pull request #477 from logos-blockchain/Pravdyvy/nix-fix
Nix fix
2026-05-14 17:24:18 +03:00
Pravdyvy
36b7228b6d fix: nix pin 2026-05-14 11:56:18 +03:00
Pravdyvy
cfabd91510 fix: nix pin 2026-05-14 11:54:32 +03:00
Pravdyvy
ec74dca893 fix: nix pin 2026-05-14 11:49:29 +03:00
Pravdyvy
904dd56187 fix: nix pin 2026-05-14 11:20:47 +03:00
Pravdyvy
130f35e997 fix(nix): nix fix 2026-05-14 07:52:14 +03:00
Sergio Chouhy
51b1e637a6
Merge pull request #475 from logos-blockchain/schouhy/fix-private-shared-account-preparation
fix: Private shared account preparation
2026-05-13 16:17:37 -03:00
Sergio Chouhy
bc65c877af fix private shared account preparation 2026-05-13 00:03:01 -03:00
Sergio Chouhy
d38ca35337
Merge pull request #464 from logos-blockchain/schouhy/diversify-private-pdas-by-identifier
feat!: Diversify private pdas by identifier
2026-05-12 14:30:49 -03:00
Sergio Chouhy
67b6916b72 clippy 2026-05-12 13:55:30 -03:00
Sergio Chouhy
f3389571f8 use identifier in private gms command 2026-05-12 13:29:06 -03:00
Sergio Chouhy
ee0fecee25 add privatepdashared variant 2026-05-11 20:30:23 -03:00
Sergio Chouhy
b3acd46f11 artifacts 2026-05-11 20:00:20 -03:00
Sergio Chouhy
355fe3842d rename program 2026-05-11 19:38:28 -03:00
Sergio Chouhy
927c24de68 Merge branch 'main' into schouhy/diversify-private-pdas-by-identifier 2026-05-11 18:57:44 -03:00
Sergio Chouhy
a9baf5d3dc rename progam 2026-05-11 17:09:46 -03:00
Sergio Chouhy
1ec145e7da assert equality on duplicate insert 2026-05-11 16:46:18 -03:00
Sergio Chouhy
b8a4d94d96
Merge pull request #473 from logos-blockchain/schouhy/remove-stale-files
chore: Remove stale files
2026-05-11 09:38:09 -03:00
Sergio Chouhy
b05da5b426 remove stale files 2026-05-08 23:30:46 -03:00
Sergio Chouhy
54c039f639 artifacts 2026-05-08 22:31:38 -03:00
Sergio Chouhy
d648505d89 fmt, clippy 2026-05-08 22:15:57 -03:00
Sergio Chouhy
190054c94d use borsh 2026-05-08 22:13:56 -03:00
Sergio Chouhy
e9c0aa0858 handle comments 2026-05-08 21:41:48 -03:00
Moudy
b44551225c
Merge pull request #460 from logos-blockchain/moudy/feat-group-cli
feat(wallet)!: wallet group commands with shared account derivation
2026-05-09 00:28:02 +02:00
Moudy
27e2850b5c refactor: make SealingPublicKey a newtype wrapper 2026-05-08 23:59:08 +02:00
Moudy
6e376900f7 fix: remove export/import commands, rewrite test to use invite/join 2026-05-08 20:28:39 +02:00
Moudy
cf699fde7c refactor: rename private_pda_spender to auth_transfer_proxy 2026-05-08 18:18:40 +02:00
Pravdyvy
fda862f5bc
Merge pull request #456 from logos-blockchain/Pravdyvy/indexer-query-api
Indexer query API
2026-05-08 19:05:09 +03:00
Moudy
1bed9ecef2 fix: resolve shared accounts in all facades, use SealingPublicKey alias, ignore fund test 2026-05-08 17:44:10 +02:00
moudyellaz
06fd4fc12e fix: artifacts and deny
Refs: #454
2026-05-08 17:32:28 +02:00
moudyellaz
3772046a39 refactor(privacy_preserving_circuit): extract execution_state module
Refs: #454
2026-05-08 17:30:55 +02:00
Moudy
2b2275ee74 fix: resolve shared accounts in auth-transfer commands 2026-05-08 11:03:13 +02:00
Moudy
4e7963c655 feat: add dedicated sealing key for GMS distribution 2026-05-08 08:19:55 +02:00
Moudy
4ace6e1570 fix: address review feedback 2026-05-07 22:48:32 +02:00
moudyellaz
01eb4a58b8 fix(privacy_preserving_circuit): satisfy CI lints and refresh artifact for #454
Refs: #454
2026-05-07 21:23:35 +02:00
Sergio Chouhy
89d28fb53e Merge branch 'main' into schouhy/diversify-private-pdas-by-identifier 2026-05-07 15:33:54 -03:00
Sergio Chouhy
75ab606dcf artifacts 2026-05-07 15:28:00 -03:00
Sergio Chouhy
a1a7924c85 ignore advisories since there's no available upgrade 2026-05-07 15:07:32 -03:00
Sergio Chouhy
8f6a519f0e clippy 2026-05-07 13:45:51 -03:00
Daniil Polyakov
f6d56cb3e4
Merge pull request #471 from Thompsonmina/add-getAccountAtBlock-rpc
Add getAccountAtBlock RPC to indexer
2026-05-07 19:26:04 +03:00
Moudy
69b81ea621 fix: address review feedback, persist group data in wallet storage 2026-05-07 17:35:51 +02:00
Sergio Chouhy
2d7d50646d add tests 2026-05-07 12:27:51 -03:00
moudyellaz
bda50f1d2f refactor(privacy_preserving_circuit): extract output module
Refs: #454
2026-05-07 16:54:57 +02:00
Pravdyvy
45ccf14e77 fix(indexer_ffi): move into indexer 2026-05-07 14:32:03 +03:00
Pravdyvy
6054ae113a fix(indexer_ffi): suggestion fix 2026-05-07 14:29:06 +03:00
Sergio Chouhy
f722d257a3 fmt 2026-05-07 01:41:35 -03:00
Sergio Chouhy
d4334c4694 move privateaccountkind to program module 2026-05-07 01:38:41 -03:00
Sergio Chouhy
755b49654e minor change to test 2026-05-07 01:32:37 -03:00
Thompsonmina
7250056f15 feat(indexer): add getAccountAtBlock RPC method 2026-05-07 04:17:51 +01:00
Sergio Chouhy
61dd8ec9b3 fix test 2026-05-07 00:16:28 -03:00
Sergio Chouhy
d24931c643 refactor proxy program 2026-05-06 15:59:42 -03:00
moudyellaz
ce3229f74f refactor(privacy_preserving_circuit)!: scaffold bin-dir layout
BREAKING-CHANGE: PRIVACY_PRESERVING_CIRCUIT_ID changes (one-time). In-flight proofs against the old guest become invalid.
Refs: #454
2026-05-06 19:10:05 +02:00
moudyellaz
fd82d0784d chore: open draft PR for #454
Refs: #454
2026-05-06 17:15:16 +02:00
Moudy
f73cd6738f refactor: delegate to auth-transfer, add shared account test 2026-05-06 14:22:50 +02:00
Moudy
5bf24b191d test: add unit tests for SharedAccountEntry and shared account derivation 2026-05-06 14:22:50 +02:00
Moudy
d0a88e91e1 feat: extend sync to scan shared accounts with GMS-derived keys 2026-05-06 14:22:49 +02:00
Moudy
cd545819e7 feat(wallet)!: add group CLI commands with --for-gms account creation
BREAKING CHANGE: `NewSubcommand::Private` has new required fields (`for_gms`, `pda`, `seed`, `program_id`). Code constructing this variant must include them (use `None`/`false` for defaults). `shared_accounts` value type changed from `Account` to `SharedAccountEntry`.
2026-05-06 14:22:40 +02:00
Moudy
7be0ed926c feat(wallet)!: add derive_keys_for_shared_account and PrivateShared variant
BREAKING CHANGE: `pda_accounts` field in NSSAUserData renamed to `shared_accounts`. `PrivacyPreservingAccount` enum has a new `PrivateShared` variant, exhaustive matches must handle it.
2026-05-06 14:22:10 +02:00
Sergio Chouhy
fb4ddb055a Merge branch 'main' into schouhy/diversify-private-pdas-by-identifier 2026-05-06 00:22:58 -03:00
Sergio Chouhy
1599fc655c add tests 2026-05-05 21:17:15 -03:00
Pravdyvy
0e914adf0a fix: merge fix 2026-05-05 15:45:24 +03:00
Pravdyvy
f3472ce87a
Merge branch 'main' into Pravdyvy/indexer-query-api 2026-05-05 14:39:14 +03:00
Daniil Polyakov
332bd29e93
Merge pull request #389 from logos-blockchain/pg/zone-sdk-lez
feat(sequencer, indexer): Use zone-sdk instead of bedrock client
2026-05-05 14:36:16 +03:00
Moudy
cf6eab2538
Merge pull request #449 from logos-blockchain/moudy/feat-group-key-holder
feat: GroupKeyHolder for GMS key management
2026-05-05 13:09:54 +02:00
Moudy
9e207450d6 fix: resolve merge conflicts with main 2026-05-05 12:37:54 +02:00
Sergio Chouhy
11949e9fa1 use privateaccountkind in storage and fix circuit 2026-05-04 21:40:30 -03:00
Sergio Chouhy
7d5e1492c4 update insert_private_account_data to take account kind instead of identifier 2026-05-04 20:12:54 -03:00
Sergio Chouhy
71ad4e0c85 fix account id computation in wallet to account for pdas 2026-05-04 18:11:38 -03:00
Sergio Chouhy
95afb2065d use privateaccuontkind in privacy circuit 2026-05-04 18:07:56 -03:00
Moudy
51f718d9fb
Merge pull request #462 from logos-blockchain/moudy/feat-strong-type-circuit-input 2026-05-04 20:10:41 +02:00
Petar Radovic
5bcb1d468b unstaged file 2026-05-01 09:25:37 +02:00
Petar Radovic
c263a98231 address comments 2026-05-01 09:12:24 +02:00
Sergio Chouhy
dd4670ab2f encrypt privateaccountkind instead of identifier 2026-05-01 01:21:48 -03:00
Sergio Chouhy
64a8ea5807 add privateaccountkind 2026-05-01 00:49:28 -03:00
Sergio Chouhy
0eb128e515 minor refactor 2026-05-01 00:45:51 -03:00
Sergio Chouhy
8d9fa1224e remove fixed identifier for pdas 2026-05-01 00:26:38 -03:00
Sergio Chouhy
fb48c82717 add identifier to private pda formula 2026-05-01 00:06:23 -03:00
Moudy
8a8bac8b69 ci: address fmt-rs, lint, and unit-tests failures from the merge 2026-04-30 22:16:26 +02:00
jonesmarvin8
f37454ed1e
Refactor signatures (#457)
* fix BIP-340 signatures for fixed sized messages

* fmt

* fix unit test

* Removed privacy keycard calls

* Revert "Removed privacy keycard calls"

This reverts commit d70ef505a1f40b87159099761f5fce5a31e3f17b.

* Add domain separators

* CI fixes

* add hash_message tests

* fix deny

* addressed comments
2026-04-30 14:21:47 -04:00
Moudy
8517906025 Merge branch 'main' into moudy/feat-strong-type-circuit-input 2026-04-30 20:17:47 +02:00
Daniil Polyakov
970696b217
Merge pull request #463 from logos-blockchain/arjentix/indexer-mock-improvements
chore: add new blocks every 30 seconds to mock indexer service
2026-04-30 19:02:42 +03:00
Moudy
4690ca56cc fix: remove epoch and ratcheting, fix PDA identifier 2026-04-30 16:34:26 +02:00
Moudy
b9ceda98cf fix: rebuild artifacts 2026-04-30 16:04:48 +02:00
Moudy
98da9b26cc fix: address PR review feedback
- Rename PrivacyPreservingCircuitInputAccount to InputAccountIdentity (drop the PrivacyPreservingCircuit prefix; add Identity suffix)
- Rename PrivacyPreservingCircuitInput.accounts to account_identities
- Rename AccountManager.accounts() to account_identities() and loop variables to account_identity
- Drop legacy mask-1/2/3 references from variant doc comments and guest comments
- Remove the explanatory comments about deleted parallel-vec tests; moved to the PR description
- Rebake privacy_preserving_circuit and test program artifacts
2026-04-30 15:46:36 +02:00
Moudy
4c28133448 fix: resolve merge conflicts 2026-04-30 15:04:33 +02:00
Petar Radovic
af3a31509e machete 2026-04-30 11:29:49 +02:00
Petar Radovic
0920e086d9 merge main 2026-04-30 11:21:01 +02:00
Petar Radovic
265a269da0 remove indexer dep from sequencer: 2026-04-30 11:13:50 +02:00
Moudy
f375a35929 fix: address PR review feedback
- Add SealingPublicKey/SealingSecretKey type aliases for seal_for/unseal
- Generalize PrivateGroupPda to PrivatePda with pre-resolved keys
- Rename group_pda_spender to private_pda_spender
- Rename group_pda_accounts to pda_accounts with serde alias
- Remove unused storage_mut()
- Remove stale group_pda_router.bin artifact
2026-04-30 09:11:08 +02:00
Sergio Chouhy
9894319389
Merge pull request #447 from logos-blockchain/schouhy/generalize-npk-to-multiple-accounts
Generalize Npk to multiple accounts
2026-04-29 21:40:22 -03:00
Daniil Polyakov
f3ab618268 chore: satisfy cargo deny 2026-04-29 23:58:24 +03:00
Daniil Polyakov
b0a48883e1 chore: add new blocks every 30 seconds to mock indexer service
Co-authored-by: Copilot <copilot@github.com>
2026-04-29 23:58:24 +03:00
Sergio Chouhy
8025780e26 fix deny 2026-04-29 16:46:07 -03:00
Sergio Chouhy
18642f9b6c artifacts 2026-04-29 13:31:22 -03:00
Sergio Chouhy
72756e8622 Merge branch 'main' into schouhy/generalize-npk-to-multiple-accounts 2026-04-29 12:28:05 -03:00
Pravdyvy
06a6983ef3 fix: tests updated 2026-04-29 17:24:00 +03:00
Moudy
c327f592bf merge: resolve Cargo.lock conflict with main 2026-04-29 15:38:09 +02:00
Moudy
27649560bc fix: bump testcontainers to resolve RUSTSEC-2026-0112 and RUSTSEC-2026-0113 2026-04-29 15:34:45 +02:00
Petar Radovic
9f6468682a clippy 2026-04-29 15:26:11 +02:00
Moudy
460d7e7fd2 fix: fmt 2026-04-29 14:54:24 +02:00
Petar Radovic
fede4977b7 clippy 2026-04-29 14:38:20 +02:00
Petar Radovic
798b89839c checkpoints 2026-04-29 14:05:23 +02:00
Petar Radovic
7aecc4faba cargo deny 2026-04-29 13:23:13 +02:00
Petar Radovic
c27e30fe57 tests pass 2026-04-29 13:20:29 +02:00
Pravdyvy
113a68c22c fix: correct free 2026-04-29 13:55:44 +03:00
Petar Radovic
d24313d999 new image 2026-04-29 12:52:38 +02:00
Petar Radovic
7944a77251 use new docker image 2026-04-29 12:50:17 +02:00
Petar Radovic
81a9ec401c indexer stream 2026-04-29 11:06:05 +02:00
Petar Radovic
6bcbe058ed merge master 2026-04-29 10:33:07 +02:00
Moudy
198eac1cf1 refactor: address PR #449 review feedback 2026-04-29 10:11:37 +02:00
Moudy
55a4a1d83b ci: fix fmt-rs, deny advisory, and rebake artifacts 2026-04-29 08:54:19 +02:00
Moudy
f7349656c7 refactor: strong-type PrivacyPreservingCircuitInput with per-account enum 2026-04-29 07:37:30 +02:00
Pravdyvy
1338dec951 Merge branch 'Pravdyvy/indexer-ffi-spawns-rpc-for-communication' into Pravdyvy/indexer-query-api 2026-04-28 19:45:05 +03:00
Pravdyvy
a201fc646c fix: main merge 2026-04-28 19:42:58 +03:00
Pravdyvy
89ea884207 feat: working queries 2026-04-28 19:36:23 +03:00
Sergio Chouhy
ad2b4b66e4 expand docs 2026-04-28 00:28:39 -03:00
Sergio Chouhy
a23e44a8df fmt and clippy 2026-04-28 00:18:57 -03:00
Sergio Chouhy
6738d8ef28 update docs 2026-04-28 00:14:55 -03:00
Sergio Chouhy
06681ef39d add KAT 2026-04-28 00:09:28 -03:00
Sergio Chouhy
aea397565d add test. Remove private sync skip when no private accounts 2026-04-28 00:04:42 -03:00
Sergio Chouhy
f512a3bf0f refactor wallet config to use identifiers instead of the redundant account_id field 2026-04-27 23:12:30 -03:00
Sergio Chouhy
f0b89f8acb use vec<identifiers> in persistentaccountdataprivate to avoid the hacky workaround with identifier=0 for unused accounts 2026-04-27 21:09:33 -03:00
Sergio Chouhy
c3f47f6685 use option<identifier> for all wallet commands 2026-04-27 19:57:25 -03:00
Sergio Chouhy
eb3d3d8a8d simplify insert account logic 2026-04-27 18:48:28 -03:00
Sergio Chouhy
924b30650c change pda reserved identifier 2026-04-27 18:45:19 -03:00
Daniil Polyakov
cf3639d825
Merge pull request #458 from logos-blockchain/arjentix/fix-clock-tx-in-indexer
Fix clock transaction validation in Indexer
2026-04-27 18:48:00 +03:00
Pravdyvy
478ba4c2f2
Merge pull request #427 from logos-blockchain/Pravdyvy/indexer-ffi-spawns-rpc-for-communication
Simple indexer FFI
2026-04-27 17:18:03 +03:00
Pravdyvy
be8f5a6db2 fix: comments 2 2026-04-27 15:44:46 +03:00
Pravdyvy
37f59281c0 fix: all types added 2026-04-27 15:38:06 +03:00
Moudy
9927e6e690 fix: rebuild artifacts 2026-04-27 14:37:15 +02:00
Daniil Polyakov
88102d6964 fix: skip check on state for clock transaction in indexer storage 2026-04-27 15:34:21 +03:00
Pravdyvy
02949e961a
Merge branch 'main' into Pravdyvy/indexer-ffi-spawns-rpc-for-communication 2026-04-27 13:58:10 +03:00
Moudy
636fc9dd30 fix: rebuild artifacts 2026-04-27 02:45:06 +02:00
Moudy
5b9cf95c47 feat: add group PDA test program, unit tests, and integration test 2026-04-27 02:44:16 +02:00
Moudy
48f95b1b7a feat: add GroupKeyHolder storage and PrivateGroupPda wallet variant 2026-04-27 02:43:51 +02:00
Moudy
f3215606fb feat: add GroupKeyHolder with per-PDA derivation, epoch ratchet, and seal/unseal 2026-04-27 02:43:26 +02:00
Sergio Chouhy
85a6763490 artifacts 2026-04-24 19:31:13 -03:00
Sergio Chouhy
e09cb6284e enforce reserved identifier for private pda 2026-04-24 18:00:54 -03:00
Sergio Chouhy
52992a124a fix identifier for pda 2026-04-24 17:04:40 -03:00
Pravdyvy
b736b229ef fix: types halfway done 2026-04-24 17:37:09 +03:00
Pravdyvy
063ad8e476 Merge branch 'main' into Pravdyvy/indexer-query-api 2026-04-24 16:32:06 +03:00
Sergio Chouhy
7c45b5af3c Merge branch 'main' into schouhy/generalize-npk-to-multiple-accounts 2026-04-24 01:04:55 -03:00
Sergio Chouhy
584bfb2052 clippy 2026-04-24 00:42:54 -03:00
Sergio Chouhy
6f9c3b2af3 fmt 2026-04-24 00:37:36 -03:00
Sergio Chouhy
9c90a6d182 remove unused impl 2026-04-24 00:36:25 -03:00
Sergio Chouhy
e19c9ff20a return impl iterator 2026-04-24 00:04:22 -03:00
Sergio Chouhy
4719b1265a replace typedef with struct 2026-04-23 23:44:31 -03:00
Sergio Chouhy
a5565e0875 make identifier random by default for wallet cli send commands 2026-04-23 23:31:21 -03:00
Pravdyvy
9fc2e39c10 feat: first query api 2026-04-23 17:07:19 +03:00
Moudy
00d3140490
Merge pull request #446 from logos-blockchain/moudy/feat-private-pdas
feat: private PDA support in the privacy circuit
2026-04-22 23:07:18 +02:00
Pravdyvy
9880a46bdc feat: indexer client added to ffi 2026-04-22 17:39:27 +03:00
Moudy
86ff3670c0 fix: bump rustls-webpki to 0.103.13 for RUSTSEC-2026-0104
Upstream advisory, reachable panic in certificate revocation list
parsing via `BorrowedCertRevocationList::from_der` /
`OwnedCertRevocationList::from_der`. Unrelated to this PR, dropped
into the advisory DB since the last green CI run and broke the `deny`
job. Fix is the recommended version bump.
2026-04-22 16:02:56 +02:00
Moudy
e5b77a27d5 refactor: localize private_pda_npk_by_position and extract authorization helper
Addresses the following review comments from @Arjentix:

- "I think we can move this into `derive_from_outputs()`"
  (on the position → npk map construction in main())
  I moved the construction inside ExecutionState::derive_from_outputs
  and stored the map as a field of ExecutionState. derive_from_outputs
  now takes `private_account_keys` directly and builds the map as part
  of state initialization. main() no longer owns the intermediate
  structure. validate_and_sync_states reads the npk through
  self.private_pda_npk_by_position.

- "Let's move this whole `is_authorized` computation into a separate
  function. This became really bulky"
  I extracted the caller-seeds resolution, family-binding recording,
  and is_authorized computation into a free function
  `resolve_authorization_and_record_bindings`. It takes the three
  field borrows it needs (`&mut pda_family_binding`, `&mut
  private_pda_bound_positions`, `&private_pda_npk_by_position`), same
  shape as `assert_family_binding`. A method would have conflicted
  with the `&mut self.post_states` borrow held by the Occupied match
  arm; the free function lets rustc split-borrow the self fields.
2026-04-22 15:55:35 +02:00
Moudy
22aa5ef70b refactor: simplify PDA API docs and rename compute_authorized_pdas
Addresses the following review comments from @Arjentix:

- "I think there are too many internal implementation information
  exposed here. This structure is used by our users, program devs. And
  they should not care about distinction between private or public pda
  or different masks"
  (on ChainedCall.pda_seeds, same feedback repeated on Claim::Pda)
  I rewrote both docstrings to drop internal details (visibility masks,
  per-form derivation names, npk handling). Program devs see only that
  they emit a seed and the `AccountId` is derived from
  `(program_id, seed)` regardless of whether the account is public or
  private.

- "Let's reflect the new nuance in the name"
  (on compute_authorized_pdas returning public-form derivations only)
  I renamed the function to `compute_public_authorized_pdas`. After
  the PR #446 rework the function only returns public-form
  derivations, the private-form authorization lives in the circuit
  guest. Updated the call site in nssa/src/validated_state_diff.rs
  and the two unit tests.
2026-04-22 15:34:15 +02:00
Pravdyvy
ad6a55c55d fix: lint fix 1 2026-04-22 07:42:02 +03:00
Sergio Chouhy
9d2abc76a1 fix tests 2026-04-21 22:39:14 -03:00
Sergio Chouhy
145198a078 fix test 2026-04-21 21:32:47 -03:00
Sergio Chouhy
b4d883e275 fix test 2026-04-21 19:34:08 -03:00
Sergio Chouhy
670527c2f1 Merge branch 'main' into schouhy/generalize-npk-to-multiple-accounts 2026-04-21 18:53:24 -03:00
Pravdyvy
33557b122f fix: comments fix 1 2026-04-21 17:46:16 +03:00
Moudy
0183eac5cc refactor: unify PDA AccountId construction via AccountId::for_{public,private}_pda
Addresses the following review comment:

- "I think this should be a constructor `AccountId::for_private_pda`.
  Consider also removing the existing `impl From<(ProgramId, Seed)> for
  AccountId` for public pdas in favor of a `AccountId::for_public_pda`
  to have a unified way of constructing pdas"

I replaced `impl From<(&ProgramId, &PdaSeed)> for AccountId` with
`AccountId::for_public_pda(program_id: &ProgramId, seed: &PdaSeed) ->
Self` and replaced the free function `private_pda_account_id(...)`
with `AccountId::for_private_pda(program_id: &ProgramId, seed:
&PdaSeed, npk: &NullifierPublicKey) -> Self`. Both live in an inherent
`impl AccountId` block in nssa/core/src/program.rs next to the PDA
derivation logic. Migrated all call sites across nssa/core,
nssa/src/state.rs, nssa/src/validated_state_diff.rs,
program_methods/guest/src/bin/privacy_preserving_circuit.rs,
programs/amm/core, programs/associated_token_account/core, the example
tail-call binary, and the ATA tutorial doc. Test function names that
referenced the old free function were also renamed
(private_pda_account_id_* to for_private_pda_*).
2026-04-21 12:35:19 +02:00
Sergio Chouhy
3ec166ff7c bring back new private account command for simplicity 2026-04-21 02:35:50 -03:00
Sergio Chouhy
42842dfbb1 clippy 2026-04-20 22:07:03 -03:00
Moudy
68d43d7f2b test: exercise callee authorization in private-PDA delegation tests
Addresses the following review comments:

- "Shouldn't we use a program that checks authorization in this test as
  callee? If not, I'm not sure if we are fully testing what the test
  docs describe (namely, that the callee got the input account with
  is_authorized=true). Maybe add a variant of the noop that checks the
  input account is authorized."
  I added test_program_methods/guest/src/bin/auth_asserting_noop.rs:
  same shape as noop.rs except it asserts pre.is_authorized == true for
  every pre_state before echoing the post_states. Any unauthorized
  pre_state panics the guest, failing the whole circuit proof. I added
  Program::auth_asserting_noop() as the matching helper. In
  caller_pda_seeds_authorize_private_pda_for_callee and
  caller_pda_seeds_with_wrong_seed_rejects_private_pda_for_callee, I
  swapped Program::noop() for Program::auth_asserting_noop() as the
  callee. The positive test now proves the callee actually sees
  is_authorized=true, not just that the circuit's consistency check did
  not reject. The negative test doubles its evidence, both the
  circuit's authorization reconciliation and the callee guest would now
  reject a wrong-seed delegation.

- "This branching logic is only correct because we are not supporting
  non-authorized private accounts with non-default values. Likely to be
  changed in the future. I'm sure there's use cases for this. For
  example the multisig program if ran completely private it would need
  a private non-default and non-authorized input account."
  Agreed. Supporting this needs wallet-supplied `(seed, owner)` side
  input so the npk-to-account_id binding can be re-verified for an
  existing private PDA without a fresh Claim::Pda or a caller
  pda_seeds match. I handled this in the second PR. I added a
  TODO(private-pdas-pr-2/3) marker on the `else` branch in
  privacy_preserving_circuit.rs:3 => { ... } so the constraint is
  visible to future maintainers, along with a comment noting the
  multisig use case.
2026-04-21 02:08:02 +02:00
Moudy
d22c142a37 fix: rebuild artifacts 2026-04-21 01:44:29 +02:00
Moudy
e8b17eef27 refactor: rename mask3 to private_pda in tests and circuit
Addresses the following review comments:

- "I'd rename all mask_3 references in test names and variables to a
  private pda wording. If in the future we change the mask number for
  the private pda, this naming will silently get outdated."
  I renamed all tests and the local variable mask3_account to
  private_pda_account.

- "Let's use more descriptive names. `mask3` is not very meaningful."
  I renamed all `mask3` into `private_pda`. Panic messages and .expect
  strings updated to match. Doc comments that factually describe the
  encoding (e.g. "mask-3 account" meaning "an account whose visibility
  mask is 3") are left as-is since they are accurate and remain stable
  until the mask value itself changes.

- "..._panics" to "..._fails"
  Covered above. The tests assert Err(CircuitProvingError), so
  execute_and_prove returns an Err, the test process itself never
  panics.

- "we can return `Some((*seed, true, caller))` to avoid having to unwrap
  the `caller_program_id` again in line 290"
  I changed matched_caller_seed from Option<(PdaSeed, bool)> to
  Option<(PdaSeed, bool, ProgramId)>, return the `caller` captured by
  the enclosing and_then from each match arm, and dropped the .expect
  at the consumer site. Bundled with the rename since both touch the
  same branch and a single guest ELF rebuild covers them.
2026-04-21 01:43:57 +02:00
Moudy
00cae12d41 docs: drop "wallet" references from nssa crate
Addresses the following review comments:

- "I'd keep this crate independent of wallet references"
  I replaced all with "supplied npk".

- Rename request on locals attested_keys/wallet_keys
  In mask_3_wallet_npk_mismatch_panics the two key sets play distinct
  roles, one produces the pre_state's account_id (the registered pair)
  and the other is supplied in private_account_keys as the mismatched
  npk. Collapsing both to `keys` would be misleading. I renamed to
  keys_a and keys_b with an inline comment noting which one is the
  registered one and which one is mismatched.
2026-04-21 01:02:22 +02:00
Moudy
34f8b6cac8 docs: split miscoupled private-PDA test docs and clean phrasing
Addresses the following review comments:

- "Isn't two_mask_3_claims_under_same_seed_are_rejected already checking
  that there's a mechanism protecting against this exploit scenario?"
  The doc block at nssa/src/state.rs:2488-2504 mixes three paragraphs,
  one about reuse, one TODO about wallet side input, one exploit pin,
  all attached to two_mask_3_claims_under_same_seed_are_rejected. The
  reuse test below it had no doc at all. I split as follows: the
  exploit-pin paragraph stays on two_mask_3_claims_..., the reuse
  paragraph moves to a fresh docstring on
  mask_3_reuse_across_txs_currently_unsupported.

- "I don't understand this. I think this should fail because ... the
  input pre_state which is marked with is_authorized=true will make
  things fail."
  The reuse test's new docstring cites the actual reject site, the
  post-loop private_pda_bound_positions assertion in
  privacy_preserving_circuit.rs:185-192. At top level the Entry::Vacant
  arm accepts is_authorized=true unconditionally, the rejection comes
  from the bound-positions check firing because noop emits no Claim::Pda
  and there is no caller ChainedCall.pda_seeds.

- "let's dont have this TODO as part of the doc"
  The block is moved out into regular // comments immediately above
  mask_3_reuse_across_txs_currently_unsupported.

- "let's not add implementation details to docs"
  In caller_pda_seeds_authorize_mask_3_private_pda_for_callee's
  docstring, I dropped the parenthetical "(Occupied branch)" and the
  trailing sentence about which validate_and_sync_states code path gets
  exercised.

- "what does \`Claim::Pda(seed)\` / \`pda_seeds\` mean?"
  I rewrote the pda_family_binding docstring at
  privacy_preserving_circuit.rs:33-39: replaced the ambiguous
  "Claim::PrivatePda and ChainedCall's private seeds into plain
  Claim::Pda(seed) / pda_seeds" phrase with "a Claim::Pda(seed) in a
  program's post_state or a caller's ChainedCall.pda_seeds entry".

- Suggestion on nssa/src/validated_state_diff.rs:226 rewriting
  "The public-execution path only sees mask-0 accounts" to
  "The public-execution path only sees public accounts".
  Applied: "The public-execution path only sees public accounts".

- Clarification requested on the private_pda_bound_positions field:
  I expanded the docstring at privacy_preserving_circuit.rs:26-31 to
  state that binding is an idempotent property, not an event, and to
  enumerate the two proof paths that populate it (a Claim::Pda on a
  mask-3 pre_state, or a caller's pda_seeds matching under the private
  derivation).
2026-04-21 00:37:06 +02:00
Sergio Chouhy
7ccd6ae331 wip 2026-04-20 11:27:15 -03:00
Sergio Chouhy
6316f59777 fmt 2026-04-19 23:13:51 -03:00
Sergio Chouhy
c43418721e remove unused methods 2026-04-19 23:13:33 -03:00
Sergio Chouhy
20a068a9e3 update shell completions 2026-04-19 21:16:18 -03:00
Sergio Chouhy
c30d435155 update nonce init formula to depend on account id instead of just npk 2026-04-19 19:28:10 -03:00
Sergio Chouhy
3dfbea9b66 update token transfer tutorial 2026-04-17 19:45:42 -03:00
Sergio Chouhy
a42144cb3c minor refactor. update ffi 2026-04-17 19:45:30 -03:00
Sergio Chouhy
b34e301023 rollback and remove identifier arguments in new private account wallet command 2026-04-17 14:26:50 -03:00
Sergio Chouhy
f28ac0f092 return account id on new private account creation 2026-04-17 12:52:07 -03:00
Pravdyvy
cb2a0ab4ee fix: deny fix 1 2026-04-17 17:58:04 +03:00
Moudy
9e4e546c9e ci: reduce debug info in dev/test profiles to avoid LLD OOM 2026-04-17 16:10:20 +02:00
Moudy
d5f97c3de4 fix: rebuild artifacts 2026-04-17 15:38:50 +02:00
Moudy
d3577f02bc fix: reject multiple family members under same (program, seed) in one tx 2026-04-17 15:36:20 +02:00
Pravdyvy
57831443e2 fix: nix update 2 2026-04-17 14:38:08 +03:00
Pravdyvy
173f7ef58a fix: nix update 1 2026-04-17 14:33:58 +03:00
Moudy
f9a5a7635e refactor: make programs privacy-agnostic in the privacy circuit 2026-04-17 07:29:40 +02:00
Sergio Chouhy
a4e8b53817 fix wallet storage bug 2026-04-17 00:09:32 -03:00
Sergio Chouhy
3cf7972425 add identifier to ciphertext and use it on sync mechanism 2026-04-16 23:22:40 -03:00
Moudy
48478ca21e fix: rebuild artifacts 2026-04-16 20:12:39 +02:00
Moudy
ca5f421188 fix: add private_pda_seeds to ChainedCall literals in example guests 2026-04-16 19:41:26 +02:00
Moudy
4b09aae852 fix: doc backticks and rebuild artifacts 2026-04-16 19:26:27 +02:00
Moudy
390bf59660 fix: nightly fmt 2026-04-16 19:11:46 +02:00
Moudy
d1fd6fe945 fix: clippy issue 2026-04-16 19:07:27 +02:00
Moudy
bbb0aae17a fix: rebuild artifacts 2026-04-16 19:02:54 +02:00
Moudy
ba93ec6f3e fix: clippy issue 2026-04-16 19:00:11 +02:00
Moudy
8da04ac898 fix: nightly fmt 2026-04-16 18:45:19 +02:00
Moudy
93c6921eaf Merge remote-tracking branch 'origin/main' into moudy/feat-private-pdas
# Conflicts:
#	artifacts/program_methods/amm.bin
#	artifacts/program_methods/associated_token_account.bin
#	artifacts/program_methods/authenticated_transfer.bin
#	artifacts/program_methods/clock.bin
#	artifacts/program_methods/pinata.bin
#	artifacts/program_methods/pinata_token.bin
#	artifacts/program_methods/privacy_preserving_circuit.bin
#	artifacts/program_methods/token.bin
#	artifacts/test_program_methods/burner.bin
#	artifacts/test_program_methods/chain_caller.bin
#	artifacts/test_program_methods/changer_claimer.bin
#	artifacts/test_program_methods/claimer.bin
#	artifacts/test_program_methods/clock_chain_caller.bin
#	artifacts/test_program_methods/data_changer.bin
#	artifacts/test_program_methods/extra_output.bin
#	artifacts/test_program_methods/flash_swap_callback.bin
#	artifacts/test_program_methods/flash_swap_initiator.bin
#	artifacts/test_program_methods/malicious_authorization_changer.bin
#	artifacts/test_program_methods/malicious_caller_program_id.bin
#	artifacts/test_program_methods/malicious_self_program_id.bin
#	artifacts/test_program_methods/minter.bin
#	artifacts/test_program_methods/missing_output.bin
#	artifacts/test_program_methods/modified_transfer.bin
#	artifacts/test_program_methods/nonce_changer.bin
#	artifacts/test_program_methods/noop.bin
#	artifacts/test_program_methods/pinata_cooldown.bin
#	artifacts/test_program_methods/program_owner_changer.bin
#	artifacts/test_program_methods/simple_balance_transfer.bin
#	artifacts/test_program_methods/time_locked_transfer.bin
#	artifacts/test_program_methods/validity_window.bin
#	artifacts/test_program_methods/validity_window_chain_caller.bin
#	nssa/core/src/program.rs
#	nssa/src/state.rs
2026-04-16 18:25:57 +02:00
Moudy
ac09e785a9 fix: rebuild artifacts 2026-04-16 18:07:46 +02:00
Moudy
526c3cd978 test: add private PDA circuit tests and two guest programs 2026-04-16 18:07:32 +02:00
Moudy
f1b2c04f3d fix: rebuild artifacts 2026-04-16 17:38:41 +02:00
Moudy
1fd4e4e8d9 test: pin private_pda_account_id formula against hardcoded value 2026-04-16 17:15:34 +02:00
Moudy
661ef7c4e9 fix: rebuild artifacts 2026-04-16 16:54:02 +02:00
Moudy
bda21fb5c5 refactor: move private PDA npk into proven ChainedCall and Claim 2026-04-16 16:53:54 +02:00
Sergio Chouhy
4c050f35dd refactor private key tree to store a vec<(Identifier, AccountId)> 2026-04-16 03:03:13 -03:00
Sergio Chouhy
0ecec7016e refactor key trees 2026-04-15 23:34:49 -03:00
Daniil Polyakov
c6f8890473
Merge pull request #433 from logos-blockchain/arjentix/descriptive-invalid-program-behavior-error
feat: introduce more descriptive error messages for public execution
2026-04-16 02:51:21 +03:00
Sergio Chouhy
3a3358e389 adapt wallet ffi 2026-04-15 19:35:48 -03:00
Moudy
e08c8f93b4 fix: rebuild artifacts 2026-04-16 00:23:08 +02:00
Sergio Chouhy
0fd2682d2e add identifier arguments to cli commands 2026-04-15 19:01:58 -03:00
Moudy
b1553109ae fix: validate no duplicate entries in private_pda_info 2026-04-16 00:01:25 +02:00
Moudy
8b9cc5accf fix: rebuild artifacts 2026-04-15 23:29:13 +02:00
Sergio Chouhy
4ab8696d85 update facades to receive identifiers 2026-04-15 17:21:16 -03:00
Moudy
a27da19a45 fix: nightly fmt 2026-04-15 22:10:02 +02:00
Moudy
40a1227871 fix: clippy lint issues 2026-04-15 22:08:26 +02:00
Sergio Chouhy
8fd25bc4bf add identifier to PrivacyPreservingAccount to allow passing different identifiers 2026-04-15 16:51:20 -03:00
Moudy
4bdb1e7a22 fix: rebuild artifacts for CI 2026-04-15 21:31:33 +02:00
Moudy
47843eaa3e fix: nightly fmt and clippy issues (item ordering, doc backticks, integer suffix) 2026-04-15 21:10:22 +02:00
Moudy
b0c10ee5a2 fix: cargo fmt, add #[must_use] to private_pda_account_id, rebuild artifacts 2026-04-15 21:10:22 +02:00
Moudy
7e63f9ddcd test: add unit tests for private PDA AccountId derivation and compute_authorized_pdas 2026-04-15 21:10:21 +02:00
Moudy
cee9608ea7 feat: enable PDA claim verification for private PDA accounts 2026-04-15 21:10:21 +02:00
Moudy
308e1ebebf feat: add mask 3 branch in compute_circuit_output for private PDAs 2026-04-15 21:10:21 +02:00
Moudy
10b26ca223 feat: thread private_pda_info through the privacy circuit and extend compute_authorized_pdas 2026-04-15 21:10:21 +02:00
Moudy
3b78462e2d feat: add private_pda_info field to PrivacyPreservingCircuitInput 2026-04-15 21:10:21 +02:00
Moudy
ac98fba1b1 feat: add private PDA AccountId derivation function 2026-04-15 21:10:21 +02:00
Sergio Chouhy
985f610cea wip 2026-04-15 15:42:04 -03:00
Moudy
3bc371fbc0
Merge pull request #448 from logos-blockchain/schouhy/fix-deny
Fix deny by updating `rustls-webpki` and `multihash`
2026-04-15 20:38:58 +02:00
Sergio Chouhy
db46dcc481 ignore rand deny 2026-04-15 14:25:19 -03:00
Sergio Chouhy
a6194ad447 fix deny by updating rustls-webpki and multihash 2026-04-15 14:08:35 -03:00
Sergio Chouhy
a4af8da13b replace npk for account id in commitment and init nullifier formulas 2026-04-14 23:45:34 -03:00
Sergio Chouhy
12b8c0ad31 add identifier to account id formula 2026-04-14 22:10:52 -03:00
Sergio Chouhy
dae617c673 wip: add dummy identifier 2026-04-14 18:02:38 -03:00
Pravdyvy
e11d4968d0 fix: postfixes 2026-04-14 16:01:33 +03:00
Pravdyvy
b864ff22d4 Merge branch 'main' into Pravdyvy/indexer-ffi-spawns-rpc-for-communication 2026-04-14 10:58:18 +03:00
Pravdyvy
dd3ac54318 fix: all ffi tests added 2026-04-14 10:51:57 +03:00
Daniil Polyakov
699e91363e feat: introduce more descriptive error messages for public execution 2026-04-13 21:25:18 +03:00
Daniil Polyakov
190c811f10
Merge pull request #439 from logos-blockchain/pr/enable-indexer-tests
chore(ci): enable indexer integration tests with new docker image
2026-04-13 21:00:42 +03:00
Pravdyvy
a921d63750 Merge branch 'main' into Pravdyvy/indexer-ffi-spawns-rpc-for-communication 2026-04-13 16:01:14 +03:00
Pravdyvy
5fc397c2ee fix: ci fix 2026-04-13 15:53:31 +03:00
Petar Radovic
910d23821a rm cfgsync config 2026-04-13 12:56:45 +02:00
Petar Radovic
c15c6f09de settings 2026-04-13 12:49:37 +02:00
Petar Radovic
6cb2713356 port mappingS 2026-04-13 12:41:14 +02:00
Pravdyvy
5f86e597d5 fix: tests running 2026-04-13 13:34:01 +03:00
Petar Radovic
e9878b5e9d user config 2026-04-13 12:30:55 +02:00
Petar Radovic
4d71492b4a user config 2026-04-13 12:30:40 +02:00
Petar Radovic
97653a938a sed fix 2026-04-13 12:16:29 +02:00
Petar Radovic
e6ebad31f9 config file 2026-04-13 11:47:33 +02:00
Petar Radovic
078c4726e4 simplify run script 2026-04-13 11:46:29 +02:00
Moudy
7a29b45cf4
Merge pull request #440 from logos-blockchain/schouhy/fix-main 2026-04-10 23:14:14 +02:00
Sergio Chouhy
9d2c37ff08 fix function parameters 2026-04-10 17:15:23 -03:00
Sergio Chouhy
a0cba55035
Merge pull request #435 from logos-blockchain/schouhy/fix-genesis-nullifiers
fix: Add init nullifiers for preconfigured accounts on genesis
2026-04-10 16:37:25 -03:00
Petar Radovic
9dc5c32354 clear debug stuff 2026-04-09 19:43:33 +02:00
Petar Radovic
7266433d51 debug 2026-04-09 18:07:04 +02:00
Petar Radovic
427e1cea45 debug 2026-04-09 17:10:40 +02:00
Petar Radovic
d96e7aed68 debug 2026-04-09 15:45:54 +02:00
Petar Radovic
35b7712b12 debugging 2026-04-09 15:04:15 +02:00
Petar Radovic
e6ad1d3eea resubmit interval 2026-04-09 14:35:51 +02:00
Petar Radovic
a8202603b6 reduce coefficient 2026-04-09 14:29:36 +02:00
Petar Radovic
0eb389b62c increase timeout 2026-04-09 13:51:53 +02:00
Petar Radovic
b7ba2daa97 use newer image 2026-04-09 13:18:54 +02:00
Petar Radovic
359619ad14 new version 2026-04-08 23:33:12 +02:00
Petar Radovic
7af703f198 debug 2026-04-08 22:30:21 +02:00
Petar Radovic
74039f5cca debug 2026-04-08 21:59:15 +02:00
Petar Radovic
4d9ed77e14 debug 2026-04-08 21:07:03 +02:00
Petar Radovic
817cf35fff debug 2026-04-08 20:37:51 +02:00
Petar Radovic
ab40aedf34 debug logs 2026-04-08 20:10:09 +02:00
Petar Radovic
5ba939d156 print logs 2026-04-08 19:40:26 +02:00
Petar Radovic
c2690236e9 dump logs 2026-04-08 19:12:04 +02:00
Petar Radovic
63206faeea tag commits 2026-04-08 18:39:47 +02:00
Petar Radovic
a2f752ac2f chore(ci): enable indexer integration tests 2026-04-08 18:33:35 +02:00
fryorcraken
7473c2f7a9
Merge pull request #392 from logos-blockchain/account-label
feat: add --account-label as alternative to --account-id
2026-04-08 22:39:34 +10:00
fryorcraken
c0a3bee924
clippy 2026-04-08 15:01:22 +10:00
fryorcraken
45c3834da9
format 2026-04-08 14:54:31 +10:00
fryorcraken
bb461fac2a
revert some changes 2026-04-08 14:54:31 +10:00
fryorcraken
ba3c72ae50
revert changes and add tests 2026-04-08 14:54:31 +10:00
fryorcraken
9f292f9b78
fix tests 2026-04-08 14:54:31 +10:00
fryorcraken
0dc8409efc
feat: add --account-label as alternative to --account-id across all wallet subcommands
Allow users to identify accounts by their human-readable label instead of the
full `Privacy/base58` account ID. This makes the CLI much more ergonomic for
users who have labeled their accounts.

- [x] Add `resolve_account_label()` in `helperfunctions.rs` that looks up a label,
  determines account privacy (public/private), and returns the full `Privacy/id` string
- [x] Add `--account-label` (or `--from-label`, `--to-label`, `--definition-label`,
  `--holder-label`, `--user-holding-*-label`) as mutually exclusive alternative to
  every `--account-id`-style flag across all subcommands:
  - `account get`, `account label`
  - `auth-transfer init`, `auth-transfer send`
  - `token new`, `token send`, `token burn`, `token mint`
  - `pinata claim`
  - `amm new`, `amm swap`, `amm add-liquidity`, `amm remove-liquidity`
- [x] Update zsh completion script with `_wallet_account_labels()` helper
- [x] Add bash completion script with `_wallet_get_account_labels()` helper

1. Start a local sequencer
2. Create accounts and label them: `wallet account new public --label alice`
3. Use labels in commands: `wallet account get --account-label alice`
4. Verify mutual exclusivity: `wallet account get --account-id <id> --account-label alice` should error
5. Test shell completions: `wallet account get --account-label <TAB>` should list labels

None

None

- [x] Complete PR description
- [x] Implement the core functionality
- [ ] Add/update tests
- [x] Add/update documentation and inline comments

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 14:53:27 +10:00
Sergio Chouhy
5c3ee030a6 add test. Add --all-features to CI tests so that tests under the mock feature are also run 2026-04-07 21:26:45 -03:00
Sergio Chouhy
3ed6288a65
Merge pull request #431 from logos-blockchain/schouhy/fix-slow-tests-because-of-program-id-computation
Avoid computing program ids to speed up tests
2026-04-07 21:10:02 -03:00
Daniil Polyakov
35d8df0d03
Merge pull request #438 from logos-blockchain/arjentix/fix-docker-builds-on-tags2
fix: use just sha without ref_name
2026-04-08 00:07:28 +03:00
Daniil Polyakov
e4200e4b56 fix: use just sha without ref_name 2026-04-07 23:59:36 +03:00
Daniil Polyakov
7c1e5503c1
Merge pull request #437 from logos-blockchain/arjentix/fix-docker-builds-on-tags
fix: fix docker builds on tags
2026-04-07 23:48:12 +03:00
Sergio Chouhy
99bedb970d Merge branch 'main' into schouhy/fix-slow-tests-because-of-program-id-computation 2026-04-07 17:39:01 -03:00
Daniil Polyakov
ac427bd517 fix: fix docker builds on tags 2026-04-07 23:34:51 +03:00
Sergio Chouhy
ccd8869205 add program id test 2026-04-07 17:33:35 -03:00
Daniil Polyakov
8700e404da
Merge pull request #428 from logos-blockchain/moudy/feat-caller-program-id-and-flash-swap
Add caller_program_id to ProgramInput and flash swap demo
2026-04-07 22:48:56 +03:00
Daniil Polyakov
951b06c0fd
Merge pull request #405 from logos-blockchain/Pravdyvy/db-structural-updates
DB structural updates
2026-04-07 22:32:58 +03:00
Sergio Chouhy
4b0a68889c Merge remote-tracking branch 'origin/main' into schouhy/fix-genesis-nullifiers 2026-04-07 16:05:42 -03:00
Moudy
7e1268f53f fix: rebuild artifacts 2026-04-07 20:57:38 +02:00
Moudy
a87e8d93dc fix: cargo fmt 2026-04-07 20:16:42 +02:00
Moudy
27fbd10d92 fix: post-merge test fixes for caller_program_id and genesis_timestamp 2026-04-07 20:15:17 +02:00
Moudy
56ee93b5b7 merge main into feat-caller-program-id-and-flash-swap 2026-04-07 19:44:55 +02:00
Moudy
b22a989fbc merge main into feat-caller-program-id-and-flash-swap 2026-04-07 19:27:27 +02:00
Moudy
7d465dded7 fix: verify caller_program_id in program output 2026-04-07 19:03:06 +02:00
Sergio Chouhy
2d1896013b handle comments 2026-04-07 13:35:13 -03:00
Moudy
495680e2ea update test guest programs with caller_program_id in ProgramOutput 2026-04-07 17:54:59 +02:00
Sergio Chouhy
98e98c6214
Merge pull request #403 from logos-blockchain/schouhy/add-block-context-system-accounts
Add block context system accounts
2026-04-07 09:09:14 -03:00
Sergio Chouhy
50d402880c add nullifiers for preconfigured accounts on genesis 2026-04-06 21:44:31 -03:00
Sergio Chouhy
1ba80f7631 count mandatory clock tx in max_block_size 2026-04-06 21:34:03 -03:00
Sergio Chouhy
d9ddd5e3f6 fix docs. refactor sequencer logic to check size before executing 2026-04-06 21:07:55 -03:00
Sergio Chouhy
3c014c721e avoid computing program ids to speed up tests 2026-04-06 15:03:00 -03:00
Sergio Chouhy
ed1926b38a use Timestamp instead of u64 2026-04-06 13:21:39 -03:00
Sergio Chouhy
015999b3a5 add clock usage example programs 2026-04-04 00:23:43 -03:00
Moudy
5273c9e076 fix: rebuild artifacts 2026-04-04 00:44:50 +02:00
Moudy
cc8e82278b fix: add backticks in callback doc comment 2026-04-04 00:20:42 +02:00
Moudy
324750e618 fix: remove unfulfilled large_enum_variant expect, add backticks in docs, rebuild artifacts 2026-04-04 00:04:40 +02:00
Sergio Chouhy
34de497d4d add test 2026-04-03 18:44:28 -03:00
Moudy
dca3d1a18d fix: rebuild artifacts 2026-04-03 23:36:36 +02:00
Moudy
032f6b8906 fix: use AccountId::new instead of From<ProgramId>, apply formatting 2026-04-03 23:24:13 +02:00
Sergio Chouhy
4dd2f98fd4 Merge branch 'schouhy/add-block-context-system-accounts' of github.com:logos-blockchain/lssa into schouhy/add-block-context-system-accounts 2026-04-03 18:21:17 -03:00
Sergio Chouhy
deae71b09f handle comments 2026-04-03 18:13:24 -03:00
moudyellaz
1d0c93e9cf test: verify malicious self_program_id is rejected in public execution 2026-04-03 22:13:58 +02:00
Moudy
3cfc74695b fix: compute intermediate states inside flash swap programs 2026-04-03 22:05:49 +02:00
Moudy
c85f19fe85 fix: clippy lints in flash swap guest programs 2026-04-03 20:16:59 +02:00
Moudy
5b42d8ed9c fix: move flash swap types before all functions in test module 2026-04-03 18:14:49 +02:00
Sergio Chouhy
2aa585b847
Update programs/clock/core/src/lib.rs
Co-authored-by: Daniil Polyakov <arjentix@gmail.com>
2026-04-03 11:54:19 -03:00
Moudy
324593c4c7 fix: format expect attribute 2026-04-03 16:08:08 +02:00
Moudy
eefaf64b6d fix: move flash swap types before tests, use expect instead of allow 2026-04-03 16:04:49 +02:00
Moudy
9fb55a75d6 fix: ordering of items and allow large_enum_variant in test module 2026-04-03 15:40:55 +02:00
Moudy
bc0583368d fix: set is_authorized on PDA accounts in flash swap chained calls 2026-04-03 15:18:23 +02:00
Pravdyvy
1ae7192c7a fix: trying to run tests 2026-04-03 15:50:24 +03:00
Moudy
02e336b240 fix: add missing caller_program_id argument in test 2026-04-03 11:47:34 +02:00
Sergio Chouhy
55c75c55ae fix clippy 2026-04-02 21:44:33 -03:00
Sergio Chouhy
7c1f8f4d68 clippy 2026-04-02 21:23:30 -03:00
Moudy
74e16db68f fix: apply formatting and rebuild artifacts 2026-04-03 01:17:42 +02:00
Moudy
88e3b368c3 fix: rebuild artifacts 2026-04-03 00:58:11 +02:00
moudyellaz
af81719414 feat: add flash swap integration tests 2026-04-03 00:58:11 +02:00
moudyellaz
599724b72f feat: register flash_swap programs as test programs 2026-04-03 00:58:11 +02:00
moudyellaz
38ea2a01fa feat: add flash_swap_initiator and flash_swap_callback guest programs 2026-04-03 00:58:11 +02:00
moudyellaz
087baebcca feat: add caller_program_id to ProgramInput 2026-04-03 00:58:11 +02:00
Moudy
d105a51c04 fix: rebuild artifacts 2026-04-03 00:58:11 +02:00
Moudy
65166e8fcc fix: rebuild artifacts 2026-04-03 00:58:11 +02:00
Sergio Chouhy
4d5010f044 Merge branch 'main' into schouhy/add-block-context-system-accounts 2026-04-02 19:48:55 -03:00
Sergio Chouhy
b525447e2d refactor so that indexer checks clock constraints 2026-04-02 18:30:10 -03:00
Sergio Chouhy
6a467da3b1 fmt and clippy 2026-04-02 17:48:02 -03:00
Sergio Chouhy
29d66d2c2d small refactor 2026-04-02 17:40:58 -03:00
Moudy
42a2f04cd5
Merge pull request #426 from logos-blockchain/moudy/feat-self-program-id
add self_program_id
2026-04-02 21:15:32 +02:00
Moudy
67bb1809de fix: rebuild artifacts 2026-04-02 20:38:02 +02:00
Moudy
c9aa4d48c7 fix: add self_program_id check for public execution 2026-04-02 20:30:52 +02:00
Moudy
531381e023 Update program_methods/guest/src/bin/privacy_preserving_circuit.rs
Co-authored-by: Daniil Polyakov <arjentix@gmail.com>
2026-04-02 20:30:52 +02:00
Moudy
702ef4a46f fix: cargo fmt 2026-04-02 20:30:27 +02:00
moudyellaz
7def0c4664 refactor: pass self_program_id to ProgramOutput in test and example guest programs 2026-04-02 20:30:16 +02:00
moudyellaz
58b72dd77c refactor: pass self_program_id to ProgramOutput in production guest programs 2026-04-02 20:30:16 +02:00
moudyellaz
85cc323649 feat: verify self_program_id in privacy circuit 2026-04-02 20:30:16 +02:00
moudyellaz
eafc2969be feat: add self_program_id to ProgramOutput struct 2026-04-02 20:30:16 +02:00
Moudy
59d3d38448 fix: serialize write_inputs fields separately to match guest deserialization 2026-04-02 20:29:12 +02:00
moudyellaz
9ecf186851 refactor: update all guest programs to handle self_program_id field 2026-04-02 20:29:10 +02:00
moudyellaz
d5cb3e0454 feat: inject self_program_id in write_inputs and execute 2026-04-02 20:29:10 +02:00
moudyellaz
27299e75cc feat: add self_program_id to ProgramInput and read_nssa_inputs 2026-04-02 20:29:10 +02:00
Sergio Chouhy
fbdfd8f7ef
Merge pull request #417 from ygd58/fix/ci-fork-pr-permissions
fix: add explicit permissions to allow CI on fork PRs
2026-04-02 12:27:32 -03:00
Pravdyvy
a2904c130c fix: suggestion fix 3 2026-04-02 17:45:49 +03:00
Andrea Franz
7d75eb2d59 chore(programs/amm): rename Swap to SwapExactInput 2026-04-02 16:10:12 +02:00
Andrea Franz
9a6ec0018b feat(programs/amm): add swap exact output functionality 2026-04-02 16:10:12 +02:00
Pravdyvy
2cf7f5d724 fix: machete fix 2026-04-02 15:58:26 +03:00
Pravdyvy
cf420291e3 feat: indexer ffi added 2026-04-02 15:49:42 +03:00
Sergio Chouhy
aa157bfbe7 add tests 2026-04-02 03:55:38 -03:00
Sergio Chouhy
9915f09d6d refactor to enforce validation of state diff through constructors 2026-04-02 02:39:37 -03:00
Sergio Chouhy
40c7b308a9 add validated state diff 2026-04-02 01:09:58 -03:00
r4bbit
3e24ae2736 fix(wallet): use cryptographically secure entropy for mnemonic generation
The mnemonic/wallet generation was using a constant zero-byte array for entropy ([0u8; 32]), making all wallets deterministic based
solely on the password. This commit introduces proper random entropy using OsRng and enables users to back up their recovery phrase.

Changes:

- SeedHolder::new_mnemonic() now uses OsRng for 256-bit random entropy and returns the generated mnemonic
- Added SeedHolder::from_mnemonic() to recover a wallet from an existing mnemonic phrase
- WalletChainStore::new_storage() returns the mnemonic for user backup
- Added WalletChainStore::restore_storage() for recovery from a mnemonic
- WalletCore::new_init_storage() now returns the mnemonic
- Renamed reset_storage to restore_storage, which accepts a mnemonic for recovery
- CLI displays the recovery phrase when a new wallet is created
- RestoreKeys command now prompts for the mnemonic phrase via read_mnemonic_from_stdin()

Note: The password parameter is retained for future storage encryption but is no longer used in seed derivation (empty passphrase is used
 instead). This means the mnemonic alone is sufficient to recover accounts.

Usage:

On first wallet initialization, users will see:
IMPORTANT: Write down your recovery phrase and store it securely.
This is the only way to recover your wallet if you lose access.

Recovery phrase:
  word1 word2 word3 ... word24

To restore keys:
wallet restore-keys --depth 5
Input recovery phrase: <24 words>
Input password: <password>
2026-04-01 16:04:47 +02:00
ygd58
e71a0b9375 fix: use pull_request.head.sha for fork PR checkout
github.head_ref is empty for fork PRs, causing checkout to fail.
Use github.event.pull_request.head.sha as primary ref with
github.head_ref as fallback for branch pushes.
2026-04-01 12:56:29 +02:00
ygd58
ef83bb2e91 chore: remove redundant comment from permissions block 2026-04-01 12:54:26 +02:00
ygd58
868781f992 fix: add explicit permissions to allow CI on fork PRs
Fixes #278
2026-04-01 12:54:26 +02:00
Pravdyvy
40f8750278 fix: suggestions 2 2026-04-01 08:53:45 +03:00
Sergio Chouhy
fa2fd857a9 minor refactor 2026-04-01 00:01:11 -03:00
Sergio Chouhy
3c5a1c9d0a Merge branch 'main' into schouhy/add-block-context-system-accounts 2026-03-31 20:53:10 -03:00
Sergio Chouhy
9fa541f3d1
Merge pull request #404 from logos-blockchain/feature/validity-window-timestamps
feat: extend ValidityWindow with Unix timestamp bounds
2026-03-31 16:46:58 -03:00
Moudy
a560562874 fix: use force_insert_account in pinata PDA test
The pda_mechanism_with_pinata_token_program test was broken after
PR #414 introduced Claim::Authorized for token account claiming.
Set up token accounts directly to focus the test on the PDA mechanism
in the pinata program's chained call.
2026-03-31 18:51:37 +02:00
Moudy
e3edabcaa2 rebuild artifacts after ValidityWindow struct change 2026-03-31 15:47:43 +02:00
Moudy
5f0f8a78d3 fix: use infallible .into() instead of .try_into() for ValidityWindow conversion
Clippy flagged unnecessary fallible conversion since RangeFrom<u64> to
ValidityWindow<u64> cannot fail.
2026-03-31 15:34:52 +02:00
Moudy
103198f981 remove .unwrap() 2026-03-31 14:58:52 +02:00
Moudy
12bdc256d4 rebuild artifacts 2026-03-31 14:55:37 +02:00
Moudy
d4fb326f66 fix: remove stale validity_window ref and add missing timestamp args 2026-03-31 14:38:05 +02:00
Moudy
5113b212d6
Update nssa/core/src/circuit_io.rs
Co-authored-by: Daniil Polyakov <arjentix@gmail.com>
2026-03-31 14:10:43 +02:00
Moudy
df375ad04a fix: add missing nssa_core dependency to common crate 2026-03-31 14:08:12 +02:00
Moudy
be2b2db1f5 rebuild artifacts after rebase onto main 2026-03-31 13:58:53 +02:00
Sergio Chouhy
eb14b8bf98 clippy 2026-03-31 13:51:12 +02:00
Sergio Chouhy
f627910468 fmt 2026-03-31 13:51:12 +02:00
Sergio Chouhy
f1a42c6f97 minor refactor 2026-03-31 13:50:47 +02:00
Sergio Chouhy
8bfaf9ef4a remove blockid from common 2026-03-31 13:50:47 +02:00
Sergio Chouhy
9d830411a1 rename timestamp_ms to timestamp 2026-03-31 13:50:30 +02:00
Sergio Chouhy
77c5032527 remove common TimeStamp 2026-03-31 13:50:30 +02:00
Sergio Chouhy
35273aa02f use curr_time from new block 2026-03-31 13:50:07 +02:00
Sergio Chouhy
99f0ed03dc add type aliases 2026-03-31 13:50:06 +02:00
Sergio Chouhy
27b0ba7592 add tests for timestamp validity windows 2026-03-31 13:49:37 +02:00
Sergio Chouhy
9aa7caf3bf refactor validity window with generic 2026-03-31 13:49:12 +02:00
Moudy
6d690a8d25 fix: add backticks to doc comment for clippy doc_markdown 2026-03-31 13:47:21 +02:00
Moudy
d861f2018c rebuild artifacts with validity_window field in ProgramOutput 2026-03-31 13:47:03 +02:00
moudyellaz
c9d9d77316 fix: remove unused self from next_block_timestamp_ms 2026-03-31 13:46:43 +02:00
moudyellaz
0823461012 style: apply nightly cargo fmt 2026-03-31 13:46:43 +02:00
moudyellaz
c4567d163d style: apply cargo fmt 2026-03-31 13:46:25 +02:00
moudyellaz
5c592312f9 feat: extend ValidityWindow with Unix timestamp bounds 2026-03-31 13:46:08 +02:00
Sergio Chouhy
52f1c337ec add test 2026-03-31 08:35:37 -03:00
Daniil Polyakov
b8bb6913be
Merge pull request #414 from logos-blockchain/arjentix/protect-from-pda-griefing-attack
feat: protect from public pda griefing attacks
2026-03-31 13:56:10 +03:00
Pravdyvy
f59bcde78a fix: suggestions fix 2026-03-31 09:08:40 +03:00
Sergio Chouhy
867d8dac90 artifacts 2026-03-31 01:57:57 -03:00
Sergio Chouhy
67baefeaee fmt and clippy 2026-03-31 01:45:07 -03:00
Sergio Chouhy
d8ffa22b81 add more clock accounts 2026-03-31 01:39:02 -03:00
Sergio Chouhy
d6a92f443e add tests 2026-03-31 00:24:49 -03:00
Sergio Chouhy
7078e403ed add timestamp to clock 2026-03-30 23:50:54 -03:00
jonesmarvin8
ffcbc15972
Issue 258 - remove secp256k1 for k256 (#399)
* initialize branch

* fix signatures to use k256

* fixed error

* lint fix

* lint fix

* clippy fixes

* clippy
2026-03-30 17:15:25 -04:00
Daniil Polyakov
6780f1c9a4 feat: protect from public pda griefing attacks 2026-03-28 01:23:57 +03:00
Sergio Chouhy
085ca69e42
Merge pull request #400 from logos-blockchain/schouhy/add-block-context
Add validity windows to program output
2026-03-27 10:07:38 -03:00
Sergio Chouhy
99071a4ef9 Merge branch 'main' into schouhy/add-block-context 2026-03-26 21:23:36 -03:00
Sergio Chouhy
73fdc1d70c artifacts 2026-03-26 18:30:03 -03:00
Sergio Chouhy
287b196569 remove redundant docstring 2026-03-26 18:16:18 -03:00
Sergio Chouhy
e618e08bdc use validity window as intstruction type in test programs 2026-03-26 18:10:31 -03:00
Sergio Chouhy
25a86d4bac move standard derives to beginning 2026-03-26 15:27:37 -03:00
Sergio Chouhy
57b83f4e1a move to impl display 2026-03-26 15:21:29 -03:00
Pravdyvy
9c23c5fd8a
Merge pull request #393 from logos-blockchain/Pravdyvy/hardcoded-initial-state
feat: general initial state
2026-03-26 19:17:20 +02:00
Pravdyvy
1430909c8b Merge branch 'main' into Pravdyvy/db-structural-updates 2026-03-26 18:28:00 +02:00
Pravdyvy
9d5eb3e3bd
Merge pull request #390 from logos-blockchain/Pravdyvy/indexer-db-batching
Indexer db batching
2026-03-26 18:27:02 +02:00
Sergio Chouhy
acf609ecc8 Merge branch 'main' into schouhy/add-block-context 2026-03-26 13:24:46 -03:00
Pravdyvy
8a806899e4 Merge branch 'main' into Pravdyvy/hardcoded-initial-state 2026-03-26 17:53:05 +02:00
Pravdyvy
b8a754449a Merge branch 'main' into Pravdyvy/indexer-db-batching 2026-03-26 17:51:03 +02:00
Sergio Chouhy
9e6820f2a4
Merge pull request #412 from logos-blockchain/schouhy/remove-indexer-ci-tests
Temporarily remove integration tests indexer
2026-03-26 12:41:23 -03:00
Sergio Chouhy
1ad3070662 fix deny 2026-03-26 11:54:24 -03:00
Sergio Chouhy
867b2ceb0b remove integration tests indexer 2026-03-26 11:01:36 -03:00
r4bbit
0ed91e869e feat(programs): add Associated Token Account program with wallet CLI and tutorial
Introduce the ATA program, which derives deterministic per-token holding
accounts from (owner, token_definition) via SHA256, eliminating the need
to manually create and track holding account IDs.

Program (programs/associated_token_account/):
- Create, Transfer, and Burn instructions with PDA-based authorization
- Deterministic address derivation: SHA256(owner || definition) → seed → AccountId
- Idempotent Create (no-op if ATA already exists)

Wallet CLI (`wallet ata`):
- `address` — derive ATA address locally (no network call)
- `create`  — initialize an ATA on-chain
- `send`    — transfer tokens from owner's ATA to a recipient
- `burn`    — burn tokens from owner's ATA
- `list`    — query ATAs across multiple token definitions

Usage:
  wallet deploy-program artifacts/program_methods/associated_token_account.bin
  wallet ata address --owner <ID> --token-definition <DEF_ID>
  wallet ata create --owner Public/<ID> --token-definition <DEF_ID>
  wallet ata send --from Public/<ID> --token-definition <DEF_ID> --to <RECIPIENT> --amount 100
  wallet ata burn --holder Public/<ID> --token-definition <DEF_ID> --amount 50
  wallet ata list --owner <ID> --token-definition <DEF1> <DEF2>

Includes tutorial: docs/LEZ testnet v0.1 tutorials/associated-token-accounts.md
2026-03-26 13:19:29 +01:00
Sergio Chouhy
fc34ecd3ca fix deny 2026-03-26 09:19:00 -03:00
Pravdyvy
392f9cf179 feat: various cells 2026-03-26 13:10:31 +02:00
Sergio Chouhy
c5cbfa375b artifacts 2026-03-25 20:58:55 -03:00
Sergio Chouhy
d7b0c42255 clippy 2026-03-25 20:26:04 -03:00
Sergio Chouhy
3324bcf391 add sequencer checks 2026-03-25 20:02:13 -03:00
Sergio Chouhy
1b555d6623 update astral tokio tar 2026-03-25 18:03:07 -03:00
Sergio Chouhy
356a84132a build artifacts 2026-03-25 18:02:38 -03:00
Sergio Chouhy
52f254d4ed fix RUSTSEC-2026-0066 and RUSTSEC-2026-0049 2026-03-25 17:50:49 -03:00
Sergio Chouhy
70ccb1befa add constructors from ranges 2026-03-25 17:42:17 -03:00
Sergio Chouhy
953a1dacd3 clippy 2026-03-25 17:35:10 -03:00
Sergio Chouhy
79d70b3a66 add test for empty intersection in circuit 2026-03-25 17:33:27 -03:00
Sergio Chouhy
abc30c0ce0 remove old program output constructors 2026-03-25 16:56:04 -03:00
Sergio Chouhy
3356aef291 handle comments 2026-03-25 16:03:39 -03:00
Sergio Chouhy
1193d31f76 implement convert constructor 2026-03-25 15:47:50 -03:00
Sergio Chouhy
4d5d767249 add tests. minor refactor 2026-03-25 14:55:23 -03:00
Pravdyvy
927f6de9bc fix: structural update 2026-03-25 16:14:33 +02:00
Pravdyvy
901d0701a8 Merge branch 'Pravdyvy/indexer-db-batching' into Pravdyvy/db-structural-updates 2026-03-24 17:26:47 +02:00
Pravdyvy
ce136348e7 fix: deny fix 2026-03-23 19:22:56 +02:00
Pravdyvy
c3723776c6 fix: deny fix 2026-03-23 19:21:42 +02:00
Pravdyvy
df831bbf6f Merge branch 'main' into Pravdyvy/hardcoded-initial-state 2026-03-23 18:20:03 +02:00
Pravdyvy
1eae20ba3f fix: merge fixes 2026-03-23 18:16:53 +02:00
Pravdyvy
f59874dc1e Merge branch 'main' into Pravdyvy/indexer-db-batching 2026-03-23 18:16:45 +02:00
Daniil Polyakov
fb083ce91e
Merge pull request #402 from logos-blockchain/arjentix/fix-docker-permissions
Use docker volumes and cache docker Rust builds
2026-03-23 16:59:58 +03:00
Sergio Chouhy
90f20a7040 wip 2026-03-20 18:05:48 -03:00
Sergio Chouhy
414abe32ba artifacts 2026-03-20 15:53:05 -03:00
Sergio Chouhy
66e9881f38 Merge branch 'main' into schouhy/add-block-context 2026-03-20 14:14:11 -03:00
Sergio Chouhy
a7779f7816 artifacts 2026-03-20 14:11:09 -03:00
Sergio Chouhy
3257440448 enforce valid window construction 2026-03-20 13:49:17 -03:00
Daniil Polyakov
f9071d492c feat: cache rust builds in docker to speed up subsequent builds 2026-03-20 18:39:58 +03:00
Daniil Polyakov
c5950bd08a feat: use docker volumes instend of mounts for runtime data directories 2026-03-20 17:40:59 +03:00
jonesmarvin8
6f77c75b9c
Issue 257 - update ds and add ds to commitment (#397)
* initialize branch

* minor errors

* update artifacts and lint

* lint fix
2026-03-20 10:39:04 -04:00
Pravdyvy
9c90930bd6 Merge branch 'main' into Pravdyvy/hardcoded-initial-state 2026-03-20 14:55:42 +02:00
Sergio Chouhy
607a34058d Merge branch 'main' into schouhy/add-block-context 2026-03-20 09:52:16 -03:00
Pravdyvy
425a10516b Merge branch 'main' into Pravdyvy/indexer-db-batching 2026-03-20 14:39:30 +02:00
Sergio Chouhy
646fcc5390 push artifacts 2026-03-19 20:14:07 -03:00
Daniil Polyakov
fe368f2b48
Merge pull request #394 from logos-blockchain/arjentix/sequencer-rpc-server-refactor
Replace manual Sequencer RPC implementation with jsonrpsee
2026-03-20 01:53:28 +03:00
Sergio Chouhy
d9a1e56983 add docs 2026-03-19 19:41:02 -03:00
Daniil Polyakov
3913446cd2 fix: some wallet default config fixes after rebase 2026-03-20 01:21:50 +03:00
Sergio Chouhy
aeddb56978 fmt 2026-03-19 18:55:19 -03:00
Sergio Chouhy
0d46f0798a clippy 2026-03-19 18:55:02 -03:00
Daniil Polyakov
7b50c68550 chore: move nssa dependency of amm to dev-dependencies 2026-03-20 00:48:04 +03:00
Daniil Polyakov
05c2c3a56d fix: encode transactions with borsh in sequencer rpc 2026-03-20 00:48:04 +03:00
Daniil Polyakov
7b20a83379 fix: fixes after rebase & address comments 2026-03-20 00:48:04 +03:00
Daniil Polyakov
325960d696 feat: remove override_rust_log from wallet config 2026-03-20 00:48:04 +03:00
Daniil Polyakov
b631ef02c6 fix: final fixes & polishing 2026-03-20 00:47:37 +03:00
Daniil Polyakov
9d87e3b046 fix: fix lints 2026-03-20 00:41:05 +03:00
Daniil Polyakov
b254ebb185 feat: refactor sequencer RPC client-side 2026-03-20 00:41:05 +03:00
Daniil Polyakov
be94e133fa feat: refactor sequencer RPC server-side 2026-03-20 00:41:05 +03:00
Daniil Polyakov
bffc711470 refactor: move sequencer_ directories into sequencer 2026-03-20 00:36:07 +03:00
Sergio Chouhy
a069004451 add validity window checks on privacy preserving transactions 2026-03-19 18:32:54 -03:00
Sergio Chouhy
7bbd2dd5d7 add public validity window checks 2026-03-19 15:03:45 -03:00
Pravdyvy
e6cb4ecf94 fix: machete fix 2026-03-19 19:47:47 +02:00
Pravdyvy
dc3650edd7 Merge branch 'main' into Pravdyvy/hardcoded-initial-state 2026-03-19 19:02:19 +02:00
Pravdyvy
afa0a63c95 fix: suggestions fix 2 2026-03-19 18:01:15 +02:00
jonesmarvin8
666353d7df
Merge pull request #356 from logos-blockchain/marvin/bip-32-comp
Key protocol compatibility with BIP-032/Keycard
2026-03-19 11:59:01 -04:00
Sergio Chouhy
895dd942cf add validity range to program output 2026-03-19 12:10:02 -03:00
Daniil Polyakov
c40c62a484 fix: satisfy clippy 2026-03-19 18:03:23 +03:00
Pravdyvy
2c9361c6b5 fix: suggestions 1 2026-03-19 13:01:43 +02:00
Pravdyvy
a740f7494c feat: added one meta field with trait 2026-03-19 12:47:49 +02:00
jonesmarvin8
83ef789002 lint issues 2026-03-18 18:44:07 -04:00
jonesmarvin8
4fdefd3557 fix typo 2026-03-18 16:53:07 -04:00
jonesmarvin8
ce729f112d Merge branch 'main' into marvin/bip-32-comp 2026-03-18 16:47:39 -04:00
jonesmarvin8
be4f6c0c78
Merge pull request #334 from logos-blockchain/marvin/nonce
update nonce mechanism
2026-03-18 14:36:47 -04:00
jonesmarvin8
2cecf71c93 additional lint fixes 2026-03-18 14:06:56 -04:00
jonesmarvin8
b81b725bd1 fmt 2026-03-18 13:47:21 -04:00
jonesmarvin8
5d9980cf63 lint fixes 2026-03-18 13:10:36 -04:00
Pravdyvy
a0ab6ad92d fix: lint fix 2026-03-18 18:32:05 +02:00
Pravdyvy
5ab7d40727 Merge branch 'main' into Pravdyvy/hardcoded-initial-state 2026-03-18 18:18:52 +02:00
Petar Radovic
fee8d1623f lints 2026-03-18 16:57:13 +01:00
Pravdyvy
5b99f0dabd fix: lint fix 2026-03-18 17:52:28 +02:00
Petar Radovic
5191370360 feat(sequencer): use logos-blockchain zone-sdk 2026-03-18 16:24:22 +01:00
jonesmarvin8
0bcb626adc lint fixes 2026-03-18 10:28:52 -04:00
Moudy
0586e98c61
Merge pull request #388 from logos-blockchain/moudy/justfile 2026-03-18 15:12:19 +01:00
Moudy
82fb5781fe
Merge pull request #395 from logos-blockchain/moudyellaz-patch-3 2026-03-18 14:57:48 +01:00
Daniil Polyakov
e42c62fc21
Merge pull request #396 from logos-blockchain/arjentix/versioning
Add versioning docs & publish images on every version
2026-03-18 16:55:52 +03:00
jonesmarvin8
a83725512d Merge branch 'main' into marvin/nonce 2026-03-18 07:59:55 -04:00
Pravdyvy
9554faa778 Merge branch 'main' into Pravdyvy/indexer-db-batching 2026-03-18 10:22:37 +02:00
Pravdyvy
821cb891d5
Merge pull request #374 from logos-blockchain/Pravdyvy/indexer-final-state
Final state caching
2026-03-18 09:49:28 +02:00
Pravdyvy
de3d17c7f8 fix: fmt 2026-03-18 09:16:36 +02:00
Pravdyvy
94faaa6d64 Merge branch 'main' into Pravdyvy/indexer-final-state 2026-03-18 09:15:45 +02:00
Pravdyvy
b94b58ceb1
Merge pull request #370 from logos-blockchain/Pravdyvy/matched-retry-on-inscription
Branched retry on inscriptions
2026-03-18 08:58:07 +02:00
Pravdyvy
d71805abb6 fix: merge updates 2026-03-18 08:24:39 +02:00
Pravdyvy
b3b5337b92 Merge branch 'main' into Pravdyvy/matched-retry-on-inscription 2026-03-18 08:12:30 +02:00
jonesmarvin8
82e8e8601d
Merge pull request #338 from logos-blockchain/marvin/refactor-amm-state-tests
Refactor AMM state tests
2026-03-17 19:24:42 -04:00
jonesmarvin8
bd79e982fd fix additional main merge errors 2026-03-17 19:23:27 -04:00
jonesmarvin8
ba8fdf4f29 fix conflicts from merging main 2026-03-17 19:16:09 -04:00
jonesmarvin8
1035893fd9 fix lint 2026-03-17 19:09:25 -04:00
jonesmarvin8
a12d1e5912 fixed merge issue with main 2026-03-17 18:01:00 -04:00
jonesmarvin8
8dd5037e28 Merge branch 'main' into marvin/nonce 2026-03-17 16:45:08 -04:00
jonesmarvin8
7481b54cf8 fixed lint and artifacts 2026-03-17 16:20:32 -04:00
Daniil Polyakov
2dbacdaff1 doc: add versioning documentation 2026-03-17 23:08:48 +03:00
Daniil Polyakov
dbc2e7c327 feat: run publish_images workflow on release tags 2026-03-17 23:08:33 +03:00
jonesmarvin8
f5ba922b70 Merge branch 'main' into marvin/refactor-amm-state-tests 2026-03-17 15:59:12 -04:00
jonesmarvin8
b544b919a6 combine test files 2026-03-17 15:51:25 -04:00
Daniil Polyakov
a304f3ad13
Merge pull request #391 from logos-blockchain/arjentix/new-clippy-lints
New clippy lints, dependecies & rust bump
2026-03-17 22:11:52 +03:00
jonesmarvin8
66d501e4d7 fixed tests 2026-03-17 14:50:21 -04:00
Daniil Polyakov
d79ac3f9a8 fix: formatting 2026-03-17 21:25:30 +03:00
Daniil Polyakov
65ce23b9e1 chore: rebuild artifacts 2026-03-17 21:25:30 +03:00
Daniil Polyakov
252848a145 feat: update rust to 1.94.0 2026-03-17 21:25:30 +03:00
Daniil Polyakov
3b0e842a42 feat: update dependencies 2026-03-17 21:25:30 +03:00
Daniil Polyakov
147fff957a feat: add cargo clippy lints 2026-03-17 21:25:30 +03:00
Daniil Polyakov
6e79ce0941 feat: add style clippy lints 2026-03-17 21:25:30 +03:00
Daniil Polyakov
90412f9590 feat: add suspicious clippy lints 2026-03-17 21:25:30 +03:00
Daniil Polyakov
5bf4495709 feat: add perf clippy lints 2026-03-17 21:25:30 +03:00
Daniil Polyakov
3e1cc2e58e feat: add complexity clippy lints 2026-03-17 21:25:30 +03:00
Daniil Polyakov
222e68ac0b feat: add correctness clippy lints 2026-03-17 21:25:30 +03:00
Daniil Polyakov
aa462b66eb feat: add nursery clippy lints 2026-03-17 21:25:30 +03:00
Daniil Polyakov
e3b93b6e9a feat: add restriction clippy lints 2026-03-17 21:25:30 +03:00
Daniil Polyakov
efe8393ba0 feat: add pedantic clippy lints 2026-03-17 21:25:30 +03:00
Moudy
3998a2562c
Clarify privacy handling in programmability section 2026-03-17 14:59:40 +01:00
Pravdyvy
dfb9c70941 fix: fmt 2026-03-17 15:44:36 +02:00
Pravdyvy
a00018e64e fix: suggestions 1 2026-03-17 15:40:55 +02:00
jonesmarvin8
b44e066e84 fix constructor 2026-03-17 09:29:00 -04:00
Pravdyvy
6e7a6f8703 fix: fmt 2026-03-17 15:23:41 +02:00
Pravdyvy
adfb1156b1
Update storage/src/indexer/write_batch.rs
Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com>
2026-03-17 15:20:59 +02:00
Pravdyvy
2fb8c2c87f fix: suggestions fix 2 2026-03-17 15:18:12 +02:00
Pravdyvy
8a8c7f722b fix: suggestions fix 2026-03-17 15:10:12 +02:00
Daniil Polyakov
756f2f4135 feat: add workspace lints to every crate 2026-03-17 15:13:44 +03:00
jonesmarvin8
dd64f0b1f8 Merge branch 'main' into marvin/nonce 2026-03-16 11:22:24 -04:00
Pravdyvy
b5afcba4c4 fix: moved constants into consts 2026-03-16 17:02:22 +02:00
Pravdyvy
6b6b3a0fe8 fix: dep fix 2026-03-16 15:56:26 +02:00
jonesmarvin8
d665540495
Merge pull request #357 from logos-blockchain/marvin/issue_295
[Issue 295] ensure! in public transaction
2026-03-16 09:49:52 -04:00
jonesmarvin8
620dd3ad10 Merge branch 'main' into marvin/refactor-amm-state-tests 2026-03-16 09:37:24 -04:00
Pravdyvy
6e756ac371 Merge branch 'main' into Pravdyvy/hardcoded-initial-state 2026-03-16 15:20:48 +02:00
Pravdyvy
a9a0c386ad fix: integartion tests fix 2026-03-16 15:15:35 +02:00
jonesmarvin8
6e74cca512 fixed unit test 2026-03-16 09:12:29 -04:00
fryorcraken
d941fd3b8b
Merge pull request #365 from logos-blockchain/hex
fix: display HashType as hex string in Debug output
2026-03-16 16:21:16 +11:00
fryorcraken
55c930d784
update artifacts 2026-03-16 14:35:29 +11:00
Pravdyvy
02953999aa feat: general initial state 2026-03-13 18:23:39 +02:00
Pravdyvy
7b20d70169 fix: header storing fix 2026-03-13 14:27:54 +02:00
fryorcraken
e20860ea97
fix: use base58 encoding for program_owner instead of hex
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 14:04:17 +11:00
fryorcraken
546c3d0986
fix: remove unused bytemuck dep and apply nightly fmt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 13:46:20 +11:00
fryorcraken
339c0dc048
Prefer hex string to vector of number for stdout 2026-03-13 13:46:19 +11:00
fryorcraken
5f2d33f90e
fix: display HashType as hex string in Debug output
The derived Debug impl printed HashType as a raw byte array, while
Display (and the sequencer logs) used hex encoding. Replace the derived
Debug with a custom impl identical to Display so both representations
are consistent.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 13:46:19 +11:00
Pravdyvy
247094ca61 fix: batching usage in indexer service 2026-03-12 16:21:04 +02:00
Pravdyvy
d8542e089f fix: various fixes and cleanup 2026-03-12 15:53:38 +02:00
Pravdyvy
b57c2ac33d feat: batch writes 2026-03-11 18:37:54 +02:00
Daniil Polyakov
3464d15d89
Merge pull request #364 from logos-blockchain/bash-completion
Bash completion for `wallet`
2026-03-10 22:55:35 +03:00
Daniil Polyakov
3256e3d470
Merge pull request #381 from logos-blockchain/weboko/pinata-auth-transfer
feat: ensure pinata recipient initialized
2026-03-10 22:29:40 +03:00
Daniil Polyakov
0b66903cb9
Merge pull request #376 from logos-blockchain/moudyellaz-explorer
Revise README for sequencer and node setup
2026-03-10 22:29:26 +03:00
jonesmarvin8
b5d0d2d0c1 fmt and lint 2026-03-09 13:05:29 -04:00
jonesmarvin8
b1747548b2 fix nullifer typo 2026-03-09 12:23:57 -04:00
jonesmarvin8
d029def7c7 add ensure test 2026-03-09 12:21:44 -04:00
jonesmarvin8
a5119dc3f3 fix wallet configs 2026-03-09 11:50:37 -04:00
Pravdyvy
53b26064eb feat: multi gets 2026-03-09 16:35:03 +02:00
jonesmarvin8
1cfc710024 Merge branch 'main' into marvin/bip-32-comp 2026-03-09 05:23:24 -04:00
Pravdyvy
3426f1f8a9 fix: minor restructurization 2026-03-09 11:15:57 +02:00
Moudy
2d04946a64 added remove rocksdb from main 2026-03-07 17:00:54 +01:00
Sergio Chouhy
1e3b60feae
Merge pull request #385 from logos-blockchain/arjentix/fix-unrecognized-commitment-set-digest-error
Fix unrecognized commitment set digest error
2026-03-07 11:51:27 -03:00
Daniil Polyakov
7f0d41104d fix: replace old keys with new ones in configurations 2026-03-07 15:41:47 +03:00
Daniil Polyakov
7a9d02af31 feat: adjust just clean command 2026-03-06 22:41:27 +03:00
Daniil Polyakov
39d8c89c17
Merge pull request #384 from logos-blockchain/Pravdyvy/shared-secret-receiver-usage-fix
Shared secret receiver usage fix
2026-03-05 21:59:16 +03:00
Daniil Polyakov
0aec6028f6 fix: remove unused serde_json dev-dependency from key_protocol 2026-03-05 21:14:25 +03:00
Pravdyvy
4950c1277c fix: suggestions fix 2026-03-05 17:25:53 +02:00
Pravdyvy
fa406e7a86 fix: removed redundant logging 2026-03-05 15:20:22 +02:00
Pravdyvy
cf92157728 fix: artifacts updates 2026-03-05 14:41:52 +02:00
Daniil Polyakov
a7607b4525
Merge pull request #383 from logos-blockchain/arjentix/explorer-blocks-right-order
feat: sort blocks from latest to oldest in explorer
2026-03-05 15:24:57 +03:00
Pravdyvy
cb57baaa36 fix: integartion test on claiming path for token updated 2026-03-05 14:13:13 +02:00
Pravdyvy
e3764e1911 fix: shared secret receiver fix 2026-03-05 12:35:18 +02:00
Daniil Polyakov
ec1018deac feat: sort blocks from latest to oldest in explorer 2026-03-04 21:54:36 +03:00
Daniil Polyakov
db5d1e5cf7
Merge pull request #382 from logos-blockchain/Pravdyvy/pinata-buff
Pinata Buff
2026-03-04 21:36:29 +03:00
Pravdyvy
06665160c6 fix: updated pinata balance 2026-03-04 17:47:38 +02:00
Pravdyvy
44a11bd3f1 fix: state interactions fix 2026-03-04 14:12:39 +02:00
Sasha
ddecae8c40
feat: ensure pinata recipient initialized 2026-03-03 23:21:18 +01:00
Pravdyvy
9df121704f
Merge pull request #380 from logos-blockchain/Pravdyvy/block-sorting-while-resubmit
feat: block ordering while resubmit
2026-03-03 10:13:13 +02:00
Moudy
9d7cf58f6f
Revise README for sequencer and node setup
Updated instructions for running the explorer
2026-03-03 01:29:42 +01:00
jonesmarvin8
7daf32611a add necessary typo back in 2026-03-02 16:42:32 -05:00
jonesmarvin8
6000d61bf2 artifacts update 2026-03-02 16:27:11 -05:00
jonesmarvin8
181d43e6d2 test fixing 2026-03-02 16:13:23 -05:00
jonesmarvin8
84abe02573 add serialize/deserialize impls 2026-03-02 11:54:41 -05:00
Pravdyvy
ed299932c5 fear: final state field 2026-03-02 18:25:22 +02:00
jonesmarvin8
71d07ccdf7 fmt 2026-03-02 10:51:30 -05:00
jonesmarvin8
d863b763d1 Merge branch 'main' into marvin/nonce 2026-03-02 10:49:07 -05:00
jonesmarvin8
227f006179 add clone property and shift feature restriction 2026-03-02 10:15:55 -05:00
Pravdyvy
416f16bfc6 feat: block ordering while resubmit 2026-03-02 14:22:11 +02:00
Pravdyvy
2e5a1d29ad fix: branched retry on inscriptions 2026-03-02 09:30:13 +02:00
jonesmarvin8
ab41bff5cc update config files 2026-02-27 18:30:54 -05:00
Daniil Polyakov
226ebe3e0e
Merge pull request #369 from logos-blockchain/arjentix/fix-indexer-docker-startup
fix: fix docker builds and runs
2026-02-27 22:20:43 +03:00
Daniil Polyakov
7d4409fdf9 fix: fix docker builds and runs 2026-02-27 21:20:22 +03:00
Daniil Polyakov
4e112bc520
Merge pull request #368 from logos-blockchain/Pravdyvy/http-retries-in-seq-client
HTTP retries in sequencer client
2026-02-27 21:19:34 +03:00
Pravdyvy
9dc6025537 fix: http retries 2026-02-27 19:11:26 +02:00
jonesmarvin8
5a0ef060fd Merge branch 'main' into marvin/bip-32-comp 2026-02-27 11:32:30 -05:00
jonesmarvin8
157495ad59 fix lint 2026-02-27 10:49:00 -05:00
jonesmarvin8
87c251b6e2 incorporate lip_supply_init to this refactoring 2026-02-27 10:24:47 -05:00
Moudy
bdeca0185f
Merge pull request #367 from logos-blockchain/schouhy/fix-sequencer-config-npk
Update sequencer config npks
2026-02-27 16:08:03 +01:00
jonesmarvin8
3c677ce78c Merge branch 'main' into marvin/refactor-amm-state-tests 2026-02-27 09:34:13 -05:00
Andrea Franz
71de2f7754
Merge pull request #359 from logos-blockchain/fix/amm-initial-lp
chore(programs/amm): fix initial LP supply calculations
2026-02-27 14:33:52 +01:00
Sergio Chouhy
d5c75cbd98 update public account ids 2026-02-27 10:25:07 -03:00
Sergio Chouhy
99069c84b1 update sequencer config npks 2026-02-27 10:06:54 -03:00
Daniel Sanchez
10770bd99e
Merge pull request #366 from logos-blockchain/dsq/nix-circuits-deps
chore(nix): Add circuits dependency
2026-02-27 12:18:09 +00:00
danielSanchezQ
6887131364 Add circuits dependency 2026-02-27 12:13:25 +00:00
Andrea Franz
fee1ab30e9 chore(programs/amm): fix initial LP supply calculations
ift-ts:sc:logos:2026q1-amm-core-improvements:fix-initial-lp-calc
2026-02-27 09:21:43 +01:00
fryorcraken
d9b2b0c824
update zsh completion 2026-02-27 15:26:57 +11:00
fryorcraken
e2175132f5
Add bash completion script 2026-02-27 15:23:03 +11:00
jonesmarvin8
b9324bf03b Merge branch 'main' into marvin/bip-32-comp 2026-02-26 12:41:50 -05:00
jonesmarvin8
3c10263023 Merge branch 'main' into marvin/issue_295 2026-02-26 12:41:00 -05:00
jonesmarvin8
3ed6ce3f1c fix wallet json 2026-02-25 19:00:01 -05:00
jonesmarvin8
6209317cd4 fmt and artifacts 2026-02-25 17:37:49 -05:00
jonesmarvin8
d58df166e7 initialize branch 2026-02-25 15:32:31 -05:00
jonesmarvin8
85dd5fae86 initialize bip-032 changes 2026-02-25 15:18:27 -05:00
jonesmarvin8
82966e652d fix nssa import issue with programs::amm 2026-02-24 18:18:23 -05:00
jonesmarvin8
abfb707900 fixed test 2026-02-19 20:07:55 -05:00
jonesmarvin8
8095408e1b initialize branch 2026-02-19 19:15:04 -05:00
jonesmarvin8
c0d4a98f3c fixed unit test 2026-02-19 19:09:19 -05:00
jonesmarvin8
b3e2162d71 fix machete and unit test 2026-02-17 19:56:35 -05:00
jonesmarvin8
946a45b8a3 added test for new nonce mechanism 2026-02-17 19:10:35 -05:00
jonesmarvin8
fa6a99192e Merge branch 'main' into marvin/nonce 2026-02-17 18:26:48 -05:00
jonesmarvin8
bf8c47b9b7 format fixes 2026-02-16 20:50:14 -05:00
jonesmarvin8
a821f04fb8 Merge branch 'main' into marvin/nonce 2026-02-16 19:56:44 -05:00
jonesmarvin8
889326d2ad clean up and fixed tests 2026-02-16 19:53:32 -05:00
jonesmarvin8
d965b7c25d Merge branch 'main' into marvin/nonce 2026-02-16 09:27:34 -05:00
jonesmarvin8
77f2fb6994 update nonce mechanism 2026-02-12 19:22:03 -05:00
460 changed files with 43703 additions and 20954 deletions

View File

@ -13,6 +13,9 @@ ignore = [
{ id = "RUSTSEC-2025-0055", reason = "`tracing-subscriber` v0.2.25 pulled in by ark-relations v0.4.0 - will be addressed before mainnet" }, { id = "RUSTSEC-2025-0055", reason = "`tracing-subscriber` v0.2.25 pulled in by ark-relations v0.4.0 - will be addressed before mainnet" },
{ id = "RUSTSEC-2025-0141", reason = "`bincode` is unmaintained but continuing to use it." }, { id = "RUSTSEC-2025-0141", reason = "`bincode` is unmaintained but continuing to use it." },
{ id = "RUSTSEC-2023-0089", reason = "atomic-polyfill is pulled transitively via risc0-zkvm; waiting on upstream fix (see https://github.com/risc0/risc0/issues/3453)" }, { id = "RUSTSEC-2023-0089", reason = "atomic-polyfill is pulled transitively via risc0-zkvm; waiting on upstream fix (see https://github.com/risc0/risc0/issues/3453)" },
{ id = "RUSTSEC-2026-0097", reason = "`rand` v0.8.5 is present transitively from logos crates, modification may break integration" },
{ id = "RUSTSEC-2026-0118", reason = "`hickory-proto` v0.25.0-alpha.5 is present transitively from logos crates, modification may break integration" },
{ id = "RUSTSEC-2026-0119", reason = "`hickory-proto` v0.25.0-alpha.5 is present transitively from logos crates, modification may break integration" },
] ]
yanked = "deny" yanked = "deny"
unused-ignored-advisory = "deny" unused-ignored-advisory = "deny"

View File

@ -26,11 +26,20 @@ Thumbs.db
ci_scripts/ ci_scripts/
# Documentation # Documentation
docs/
*.md *.md
!README.md !README.md
# Configs (copy selectively if needed) # Non-build project files
completions/
configs/ configs/
Justfile
# License clippy.toml
rustfmt.toml
flake.nix
flake.lock
LICENSE LICENSE
# Docker compose files (not needed inside build)
docker-compose*.yml
**/docker-compose*.yml

44
.github/workflows/bench-regression.yml vendored Normal file
View File

@ -0,0 +1,44 @@
on:
pull_request:
paths:
- "tools/crypto_primitives_bench/**"
- "key_protocol/**"
- "nssa/core/**"
- ".github/workflows/bench-regression.yml"
permissions:
contents: read
pull-requests: write
name: bench-regression
jobs:
crypto-primitives:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v5
with:
ref: ${{ github.event.pull_request.head.sha || github.head_ref }}
# criterion-compare-action checks out the base branch in a second
# working tree, so we need the full history.
fetch-depth: 0
- uses: ./.github/actions/install-system-deps
- uses: ./.github/actions/install-risc0
- uses: ./.github/actions/install-logos-blockchain-circuits
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Install active toolchain
run: rustup install
- name: Run criterion-compare against base branch
uses: boa-dev/criterion-compare-action@v3
with:
branchName: ${{ github.base_ref }}
cwd: tools/crypto_primitives_bench
benchName: primitives
token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -11,6 +11,10 @@ on:
- "**.md" - "**.md"
- "!.github/workflows/*.yml" - "!.github/workflows/*.yml"
permissions:
contents: read
pull-requests: read
name: General name: General
jobs: jobs:
@ -19,7 +23,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
with: with:
ref: ${{ github.head_ref }} ref: ${{ github.event.pull_request.head.sha || github.head_ref }}
- name: Install nightly toolchain for rustfmt - name: Install nightly toolchain for rustfmt
run: rustup install nightly --profile minimal --component rustfmt run: rustup install nightly --profile minimal --component rustfmt
@ -32,7 +36,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
with: with:
ref: ${{ github.head_ref }} ref: ${{ github.event.pull_request.head.sha || github.head_ref }}
- name: Install taplo-cli - name: Install taplo-cli
run: cargo install --locked taplo-cli run: cargo install --locked taplo-cli
@ -45,7 +49,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
with: with:
ref: ${{ github.head_ref }} ref: ${{ github.event.pull_request.head.sha || github.head_ref }}
- name: Install active toolchain - name: Install active toolchain
run: rustup install run: rustup install
@ -61,7 +65,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
with: with:
ref: ${{ github.head_ref }} ref: ${{ github.event.pull_request.head.sha || github.head_ref }}
- name: Install cargo-deny - name: Install cargo-deny
run: cargo install --locked cargo-deny run: cargo install --locked cargo-deny
@ -77,7 +81,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
with: with:
ref: ${{ github.head_ref }} ref: ${{ github.event.pull_request.head.sha || github.head_ref }}
- uses: ./.github/actions/install-system-deps - uses: ./.github/actions/install-system-deps
@ -90,6 +94,12 @@ jobs:
- name: Install active toolchain - name: Install active toolchain
run: rustup install run: rustup install
- name: Restore Rust cache
uses: Swatinem/rust-cache@v2
with:
shared-key: ci-rust-cache
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Lint workspace - name: Lint workspace
env: env:
RISC0_SKIP_BUILD: "1" RISC0_SKIP_BUILD: "1"
@ -106,7 +116,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
with: with:
ref: ${{ github.head_ref }} ref: ${{ github.event.pull_request.head.sha || github.head_ref }}
- uses: ./.github/actions/install-system-deps - uses: ./.github/actions/install-system-deps
@ -119,6 +129,12 @@ jobs:
- name: Install active toolchain - name: Install active toolchain
run: rustup install run: rustup install
- name: Restore Rust cache
uses: Swatinem/rust-cache@v2
with:
shared-key: ci-rust-cache
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Install nextest - name: Install nextest
run: cargo install --locked cargo-nextest run: cargo install --locked cargo-nextest
@ -126,15 +142,80 @@ jobs:
env: env:
RISC0_DEV_MODE: "1" RISC0_DEV_MODE: "1"
RUST_LOG: "info" RUST_LOG: "info"
run: cargo nextest run --workspace --exclude integration_tests run: cargo nextest run --workspace --exclude integration_tests --all-features
integration-tests-prebuild:
runs-on: ubuntu-latest
outputs:
targets: ${{ steps.discover-targets.outputs.targets }}
steps:
- uses: actions/checkout@v5
with:
ref: ${{ github.event.pull_request.head.sha || github.head_ref }}
- uses: ./.github/actions/install-system-deps
- uses: ./.github/actions/install-risc0
- uses: ./.github/actions/install-logos-blockchain-circuits
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Install active toolchain
run: rustup install
- name: Restore Rust cache
uses: Swatinem/rust-cache@v2
with:
shared-key: ci-rust-cache
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Install nextest
run: cargo install --locked cargo-nextest
- name: Build integration test archive
env:
RISC0_DEV_MODE: "1"
run: cargo nextest archive -p integration_tests --archive-file integration-tests.tar.zst --no-pager
- name: Upload integration test archive
uses: actions/upload-artifact@v4
with:
name: integration-tests-archive
path: integration-tests.tar.zst
- name: Discover integration test targets from archive
id: discover-targets
run: |
cargo nextest list \
--archive-file integration-tests.tar.zst \
--list-type binaries-only \
--message-format json \
--no-pager > integration-tests-binaries.json
targets_json="$(jq -c '[."rust-binaries" | to_entries[] | select(.value.kind == "test" and .value."binary-name" != "tps") | .value."binary-name"] | sort | unique' integration-tests-binaries.json)"
if [[ "$targets_json" == "[]" ]]; then
echo "No integration test targets were discovered." >&2
exit 1
fi
echo "targets=$targets_json" >> "$GITHUB_OUTPUT"
echo "Discovered integration targets: $targets_json"
integration-tests: integration-tests:
needs: integration-tests-prebuild
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 60 timeout-minutes: 90
strategy:
fail-fast: false
matrix:
target: ${{ fromJson(needs.integration-tests-prebuild.outputs.targets) }}
name: integration-tests (${{ matrix.target }})
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
with: with:
ref: ${{ github.head_ref }} ref: ${{ github.event.pull_request.head.sha || github.head_ref }}
- uses: ./.github/actions/install-system-deps - uses: ./.github/actions/install-system-deps
@ -147,33 +228,10 @@ jobs:
- name: Install active toolchain - name: Install active toolchain
run: rustup install run: rustup install
- name: Install nextest - name: Download integration test archive
run: cargo install --locked cargo-nextest uses: actions/download-artifact@v4
- name: Run tests
env:
RISC0_DEV_MODE: "1"
RUST_LOG: "info"
run: cargo nextest run -p integration_tests -- --skip tps_test --skip indexer
integration-tests-indexer:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v5
with: with:
ref: ${{ github.head_ref }} name: integration-tests-archive
- uses: ./.github/actions/install-system-deps
- uses: ./.github/actions/install-risc0
- uses: ./.github/actions/install-logos-blockchain-circuits
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Install active toolchain
run: rustup install
- name: Install nextest - name: Install nextest
run: cargo install --locked cargo-nextest run: cargo install --locked cargo-nextest
@ -182,15 +240,15 @@ jobs:
env: env:
RISC0_DEV_MODE: "1" RISC0_DEV_MODE: "1"
RUST_LOG: "info" RUST_LOG: "info"
run: cargo nextest run -p integration_tests indexer -- --skip tps_test run: cargo nextest run --archive-file integration-tests.tar.zst -E "binary(${{ matrix.target }})"
valid-proof-test: valid-proof-test:
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 60 timeout-minutes: 90
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
with: with:
ref: ${{ github.head_ref }} ref: ${{ github.event.pull_request.head.sha || github.head_ref }}
- uses: ./.github/actions/install-system-deps - uses: ./.github/actions/install-system-deps
@ -203,6 +261,12 @@ jobs:
- name: Install active toolchain - name: Install active toolchain
run: rustup install run: rustup install
- name: Restore Rust cache
uses: Swatinem/rust-cache@v2
with:
shared-key: ci-rust-cache
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Test valid proof - name: Test valid proof
env: env:
RUST_LOG: "info" RUST_LOG: "info"
@ -216,12 +280,18 @@ jobs:
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
with: with:
ref: ${{ github.head_ref }} ref: ${{ github.event.pull_request.head.sha || github.head_ref }}
- uses: ./.github/actions/install-risc0 - uses: ./.github/actions/install-risc0
- name: Restore Rust cache
uses: Swatinem/rust-cache@v2
with:
shared-key: ci-rust-cache
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Install just - name: Install just
run: cargo install just run: cargo install --locked just
- name: Build artifacts - name: Build artifacts
run: just build-artifacts run: just build-artifacts

View File

@ -2,6 +2,9 @@ name: Publish Docker Images
on: on:
workflow_dispatch: workflow_dispatch:
push:
tags:
- "v*"
jobs: jobs:
publish: publish:
@ -9,12 +12,12 @@ jobs:
strategy: strategy:
matrix: matrix:
include: include:
- name: sequencer_runner - name: sequencer_service
dockerfile: ./sequencer_runner/Dockerfile dockerfile: ./sequencer/service/Dockerfile
build_args: | build_args: |
STANDALONE=false STANDALONE=false
- name: sequencer_runner-standalone - name: sequencer_service-standalone
dockerfile: ./sequencer_runner/Dockerfile dockerfile: ./sequencer/service/Dockerfile
build_args: | build_args: |
STANDALONE=true STANDALONE=true
- name: indexer_service - name: indexer_service
@ -42,11 +45,12 @@ jobs:
with: with:
images: ${{ secrets.DOCKER_REGISTRY }}/${{ github.repository }}/${{ matrix.name }} images: ${{ secrets.DOCKER_REGISTRY }}/${{ github.repository }}/${{ matrix.name }}
tags: | tags: |
type=ref,event=tag
type=ref,event=branch type=ref,event=branch
type=ref,event=pr type=ref,event=pr
type=semver,pattern={{version}} type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}}.{{minor}}
type=sha,prefix={{branch}}- type=sha,prefix=sha-
type=raw,value=latest,enable={{is_default_branch}} type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push Docker image - name: Build and push Docker image

2
.gitignore vendored
View File

@ -6,7 +6,7 @@ data/
.idea/ .idea/
.vscode/ .vscode/
rocksdb rocksdb
sequencer_runner/data/ sequencer/service/data/
storage.json storage.json
result result
wallet-ffi/wallet_ffi.h wallet-ffi/wallet_ffi.h

84
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,84 @@
# Contributing
We're glad you're interested in contributing to Logos Execution Zone!
This document describes the guidelines for contributing to the project. We will be updating it as we grow and we figure out what works best for us.
If you have any questions, come say hi to our [Discord](https://discord.gg/tGJwgGrSPN)!
## Commit title format
We use [Conventional Commits](https://www.conventionalcommits.org/).
Use:
- `type(scope): description`
- `type(scope)!: description` for breaking changes
Allowed `type` values:
- `feat`
- `fix`
- `chore`
- `docs`
- `test`
- `refactor`
- `perf`
- `build`
- `ci`
- `revert`
Examples:
- `feat(nssa): add private PDA support`
- `fix(wallet): correct fee calculation`
- `feat(nssa)!: rename AccountId::from((prog, seed)) to AccountId::for_public_pda`
Breaking changes:
- Mark with `!` in the title.
`CHANGELOG.md` is generated from these markers on every `v*` tag via `git-cliff`, and GitHub Releases are created from the same content.
## Pull requests
PR titles should follow the same Conventional Commits format:
- `type(scope): description`
- `type(scope)!: description` for breaking changes
Before marking a PR as ready for review:
- Fill out the PR template.
Breaking changes in PRs:
- Optionally add a `BREAKING CHANGE:` footer in the PR body with migration notes.
Before merging a PR, consider squashing non-meaningful commits. E.g.:
```
- refactor(wallet): move user keys to a separate module
- revert(wallet): revert "refactor(wallet): move user keys to a separate module"
```
Could be squashed to an empty commit if they belong to the same PR.
## Branch workflow
When bringing your feature branch up to date, prefer rebasing on top of `main`.
- Preferred: `git rebase main`
- Avoid: `git merge main` in feature branches
This keeps commit history cleaner and makes reviews easier.
## Useful commands
We have [`Justfile`](./Justfile) which contains some useful utilities which may help you.
To list all of them run the command: `just`.
Any change to our core crates may invalidate our RISC0 [`artifacts`](./artifacts/), in that case you're required to run `just build-artifacts` to update them.
## AI-assisted contributions
AI tools are allowed for drafting code, docs, tests, and review suggestions.
Requirements:
- A human author is fully responsible for all submitted code and text.
- The person opening the PR must review, verify, and be able to explain every change.
- Do not open PRs automatically via AI agents or bots. Automatic AI-created PRs are not allowed.

3664
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -15,11 +15,18 @@ members = [
"nssa/core", "nssa/core",
"programs/amm/core", "programs/amm/core",
"programs/amm", "programs/amm",
"programs/clock/core",
"programs/token/core", "programs/token/core",
"programs/token", "programs/token",
"sequencer_core", "programs/associated_token_account/core",
"sequencer_rpc", "programs/associated_token_account",
"sequencer_runner", "programs/authenticated_transfer/core",
"programs/faucet/core",
"programs/vault/core",
"sequencer/core",
"sequencer/service",
"sequencer/service/protocol",
"sequencer/service/rpc",
"indexer/core", "indexer/core",
"indexer/service", "indexer/service",
"indexer/service/protocol", "indexer/service/protocol",
@ -32,7 +39,13 @@ members = [
"examples/program_deployment", "examples/program_deployment",
"examples/program_deployment/methods", "examples/program_deployment/methods",
"examples/program_deployment/methods/guest", "examples/program_deployment/methods/guest",
"bedrock_client", "testnet_initial_state",
"indexer/ffi",
"keycard_wallet",
"test_fixtures",
"tools/cycle_bench",
"tools/crypto_primitives_bench",
"tools/integration_bench",
] ]
[workspace.dependencies] [workspace.dependencies]
@ -42,32 +55,43 @@ common = { path = "common" }
mempool = { path = "mempool" } mempool = { path = "mempool" }
storage = { path = "storage" } storage = { path = "storage" }
key_protocol = { path = "key_protocol" } key_protocol = { path = "key_protocol" }
sequencer_core = { path = "sequencer_core" } sequencer_core = { path = "sequencer/core" }
sequencer_rpc = { path = "sequencer_rpc" } sequencer_service_protocol = { path = "sequencer/service/protocol" }
sequencer_runner = { path = "sequencer_runner" } sequencer_service_rpc = { path = "sequencer/service/rpc" }
sequencer_service = { path = "sequencer/service" }
indexer_core = { path = "indexer/core" } indexer_core = { path = "indexer/core" }
indexer_service = { path = "indexer/service" } indexer_service = { path = "indexer/service" }
indexer_service_protocol = { path = "indexer/service/protocol" } indexer_service_protocol = { path = "indexer/service/protocol" }
indexer_service_rpc = { path = "indexer/service/rpc" } indexer_service_rpc = { path = "indexer/service/rpc" }
wallet = { path = "wallet" } wallet = { path = "wallet" }
wallet-ffi = { path = "wallet-ffi", default-features = false } wallet-ffi = { path = "wallet-ffi", default-features = false }
indexer_ffi = { path = "indexer/ffi" }
clock_core = { path = "programs/clock/core" }
token_core = { path = "programs/token/core" } token_core = { path = "programs/token/core" }
token_program = { path = "programs/token" } token_program = { path = "programs/token" }
amm_core = { path = "programs/amm/core" } amm_core = { path = "programs/amm/core" }
amm_program = { path = "programs/amm" } amm_program = { path = "programs/amm" }
ata_core = { path = "programs/associated_token_account/core" }
ata_program = { path = "programs/associated_token_account" }
authenticated_transfer_core = { path = "programs/authenticated_transfer/core" }
faucet_core = { path = "programs/faucet/core" }
vault_core = { path = "programs/vault/core" }
test_program_methods = { path = "test_program_methods" } test_program_methods = { path = "test_program_methods" }
bedrock_client = { path = "bedrock_client" } testnet_initial_state = { path = "testnet_initial_state" }
keycard_wallet = { path = "keycard_wallet" }
test_fixtures = { path = "test_fixtures" }
tokio = { version = "1.28.2", features = [ tokio = { version = "1.50", features = [
"net", "net",
"rt-multi-thread", "rt-multi-thread",
"sync", "sync",
"fs", "fs",
] } ] }
tokio-util = "0.7.18" tokio-util = "0.7.18"
risc0-zkvm = { version = "3.0.5", features = ['std'] } risc0-zkvm = { version = "3.0.5", default-features = false, features = ['std'] }
risc0-build = "3.0.5" risc0-build = "3.0.5"
anyhow = "1.0.98" anyhow = "1.0.98"
derive_more = "2.1.1"
num_cpus = "1.13.1" num_cpus = "1.13.1"
openssl = { version = "0.10", features = ["vendored"] } openssl = { version = "0.10", features = ["vendored"] }
openssl-probe = { version = "0.1.2" } openssl-probe = { version = "0.1.2" }
@ -75,15 +99,15 @@ serde = { version = "1.0.60", default-features = false, features = ["derive"] }
serde_json = "1.0.81" serde_json = "1.0.81"
serde_with = "3.16.1" serde_with = "3.16.1"
actix = "0.13.0" actix = "0.13.0"
actix-cors = "0.6.1" actix-cors = "0.7.1"
jsonrpsee = "0.26.0" jsonrpsee = "0.26.0"
futures = "0.3" futures = "0.3"
actix-rt = "*" actix-rt = "*"
lazy_static = "1.5.0" lazy_static = "1.5.0"
env_logger = "0.10" env_logger = "0.11"
log = "0.4.28" log = "0.4.28"
lru = "0.7.8" lru = "0.16.3"
thiserror = "2.0.12" thiserror = "2.0"
sha2 = "0.10.8" sha2 = "0.10.8"
hex = "0.4.3" hex = "0.4.3"
bytemuck = "1.24.0" bytemuck = "1.24.0"
@ -91,7 +115,7 @@ bytesize = { version = "2.3.1", features = ["serde"] }
humantime-serde = "1.1" humantime-serde = "1.1"
humantime = "2.1" humantime = "2.1"
aes-gcm = "0.10.3" aes-gcm = "0.10.3"
toml = "0.7.4" toml = "0.9.8"
bincode = "1.3.3" bincode = "1.3.3"
tempfile = "3.14.0" tempfile = "3.14.0"
light-poseidon = "0.3.0" light-poseidon = "0.3.0"
@ -107,14 +131,16 @@ base58 = "0.2.0"
itertools = "0.14.0" itertools = "0.14.0"
url = { version = "2.5.4", features = ["serde"] } url = { version = "2.5.4", features = ["serde"] }
tokio-retry = "0.3.0" tokio-retry = "0.3.0"
schemars = "1.2.0" schemars = "1.2"
async-stream = "0.3.6" async-stream = "0.3.6"
criterion = { version = "0.8", features = ["html_reports"] }
logos-blockchain-common-http-client = { git = "https://github.com/logos-blockchain/logos-blockchain.git" } logos-blockchain-common-http-client = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "ee281a447d95a951752461ee0a6e88eb4a0f17cf" }
logos-blockchain-key-management-system-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git" } logos-blockchain-key-management-system-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "ee281a447d95a951752461ee0a6e88eb4a0f17cf" }
logos-blockchain-core = { git = "https://github.com/logos-blockchain/logos-blockchain.git" } logos-blockchain-core = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "ee281a447d95a951752461ee0a6e88eb4a0f17cf" }
logos-blockchain-chain-broadcast-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git" } logos-blockchain-chain-broadcast-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "ee281a447d95a951752461ee0a6e88eb4a0f17cf" }
logos-blockchain-chain-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git" } logos-blockchain-chain-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "ee281a447d95a951752461ee0a6e88eb4a0f17cf" }
logos-blockchain-zone-sdk = { git = "https://github.com/logos-blockchain/logos-blockchain.git", rev = "ee281a447d95a951752461ee0a6e88eb4a0f17cf" }
rocksdb = { version = "0.24.0", default-features = false, features = [ rocksdb = { version = "0.24.0", default-features = false, features = [
"snappy", "snappy",
@ -129,11 +155,12 @@ k256 = { version = "0.13.3", features = [
"pem", "pem",
] } ] }
elliptic-curve = { version = "0.13.8", features = ["arithmetic"] } elliptic-curve = { version = "0.13.8", features = ["arithmetic"] }
actix-web = { version = "=4.1.0", default-features = false, features = [ actix-web = { version = "4.13.0", default-features = false, features = [
"macros", "macros",
] } ] }
clap = { version = "4.5.42", features = ["derive", "env"] } clap = { version = "4.5.42", features = ["derive", "env"] }
reqwest = { version = "0.12", features = ["json", "rustls-tls", "stream"] } reqwest = { version = "0.12", features = ["json", "rustls-tls", "stream"] }
pyo3 = { version = "0.24", features = ["auto-initialize"] }
# Profile for leptos WASM release builds # Profile for leptos WASM release builds
[profile.wasm-release] [profile.wasm-release]
@ -141,3 +168,150 @@ inherits = "release"
opt-level = 'z' opt-level = 'z'
lto = true lto = true
codegen-units = 1 codegen-units = 1
# Keep backtraces but drop full DWARF type info to avoid LLD OOM/SIGBUS when
# linking large integration-test binaries on resource-constrained CI runners.
[profile.dev]
debug = "line-tables-only"
[profile.test]
debug = "line-tables-only"
[workspace.lints.rust]
warnings = "deny"
[workspace.lints]
clippy.all = { level = "deny", priority = -1 }
# Pedantic
clippy.pedantic = { level = "deny", priority = -1 }
# Reason: documenting every function returning Result is too verbose and doesn't add much value when you have good error types.
clippy.missing-errors-doc = "allow"
# Reason: most of the panics are internal and not part of the public API, so documenting them is not necessary.
clippy.missing-panics-doc = "allow"
# Reason: this isn't always bad and actually works well for our financial and cryptography code.
clippy.similar-names = "allow"
# Reason: this lint is too strict and hard to fix.
clippy.too-many-lines = "allow"
# Reason: std hasher is fine for us in public functions.
clippy.implicit-hasher = "allow"
# Restriction
clippy.restriction = { level = "deny", priority = -1 }
# Reason: we deny the whole `restriction` group but we allow things that don't make sense for us.
# That way we can still benefit from new lints added to the `restriction` group without having to
# explicitly allow them.
# As a downside our contributors don't know if some lint was enabled intentionally or just no one
# else faced it before to allow it but we can handle this during code reviews.
clippy.blanket-clippy-restriction-lints = "allow"
# Reason: we can't avoid using unwrap for now.
clippy.unwrap-used = "allow"
# Reason: we can't avoid using expect for now.
clippy.expect-used = "allow"
# Reason: unreachable is good in many cases.
clippy.unreachable = "allow"
# Reason: this is ridiculous strict in our codebase and doesn't add any value.
clippy.single-call-fn = "allow"
# Reason: we use panic in some places and it's okay.
clippy.panic = "allow"
# Reason: shadowing is good most of the times.
clippy.shadow-reuse = "allow"
# Reason: implicit return is good.
clippy.implicit-return = "allow"
# Reason: std is fine for us, we don't need to use core.
clippy.std-instead-of-core = "allow"
# Reason: std is fine for us, we don't need to use alloc.
clippy.std-instead-of-alloc = "allow"
# Reason: default methods are good most of the time.
clippy.missing-trait-methods = "allow"
# Reason: this is too verbose and doesn't help much if you have rust analyzer.
clippy.pattern-type-mismatch = "allow"
# Reason: decreases readability.
clippy.assertions-on-result-states = "allow"
# Reason: documenting every assert is too verbose.
clippy.missing-assert-message = "allow"
# Reason: documenting private items is too verbose and doesn't add much value.
clippy.missing-docs-in-private-items = "allow"
# Reason: we use separated suffix style.
clippy.separated_literal_suffix = "allow"
# Reason: sometimes absolute paths are more readable.
clippy.absolute-paths = "allow"
# Reason: sometimes it's as readable as full variable naming.
clippy.min-ident-chars = "allow"
# Reason: it's very common and handy.
clippy.indexing-slicing = "allow"
# Reason: we use little endian style.
clippy.little-endian-bytes = "allow"
# Reason: we use this style of pub visibility.
clippy.pub-with-shorthand = "allow"
# Reason: question mark operator is very cool.
clippy.question-mark-used = "allow"
# Reason: it's fine to panic in tests and some functions where it makes sense.
clippy.panic-in-result-fn = "allow"
# Reason: we don't care that much about inlining and LTO should take care of it.
clippy.missing_inline_in_public_items = "allow"
# Reason: it's okay for us.
clippy.default-numeric-fallback = "allow"
# Reason: this is fine for us.
clippy.exhaustive-enums = "allow"
# Reason: this is fine for us.
clippy.exhaustive-structs = "allow"
# Reason: this helps readability when item is imported in other modules.
clippy.module-name-repetitions = "allow"
# Reason: mostly historical reasons, maybe we'll address this in future.
clippy.mod-module-files = "allow"
# Reason: named module files is our preferred way.
clippy.self-named-module-files = "allow"
# Reason: this is actually quite handy.
clippy.impl-trait-in-params = "allow"
# Reason: this is often useful.
clippy.use-debug = "allow"
# Reason: this is sometimes useful.
clippy.field-scoped-visibility-modifiers = "allow"
# Reason: `pub use` is good for re-exports and hiding unnecessary details.
clippy.pub-use = "allow"
# Reason: we prefer semicolons inside blocks.
clippy.semicolon-outside-block = "allow"
# Reason: we don't do it blindly, this is mostly internal constraints checks.
clippy.unwrap-in-result = "allow"
# Reason: we don't see any problems with that.
clippy.shadow-same = "allow"
# Reason: this lint is too verbose.
clippy.let-underscore-untyped = "allow"
# Reason: this lint is actually bad as it forces to use wildcard `..` instead of
# field-by-field `_` which may lead to subtle bugs when new fields are added to the struct.
clippy.unneeded-field-pattern = "allow"
# Nursery
clippy.nursery = { level = "deny", priority = -1 }
# Reason: this is okay if it compiles.
clippy.future-not-send = "allow"
# Reason: this is actually a good lint, but currently it gives a lot of false-positives.
clippy.significant-drop-tightening = "allow"
# Correctness
clippy.correctness = { level = "deny", priority = -1 }
# Complexity
clippy.complexity = { level = "deny", priority = -1 }
# Perf
clippy.perf = { level = "deny", priority = -1 }
# Suspicious
clippy.suspicious = { level = "deny", priority = -1 }
# Style
clippy.style = { level = "deny", priority = -1 }
# Cargo
clippy.cargo = { level = "deny", priority = -1 }
# Reason: we're not at this stage yet and it will be a pain to create a new crate.
clippy.cargo-common-metadata = "allow"
# Reason: hard to address right now and mostly comes from dependencies
# so the fix would be just a long list of exceptions.
clippy.multiple-crate-versions = "allow"

View File

@ -23,6 +23,12 @@ test:
@echo "🧪 Running tests" @echo "🧪 Running tests"
RISC0_DEV_MODE=1 cargo nextest run --no-fail-fast RISC0_DEV_MODE=1 cargo nextest run --no-fail-fast
# Run criterion benches: fast crypto primitives, then the slow PPE verify (real proving setup).
bench:
@echo "📊 Running criterion benches"
cargo bench -p crypto_primitives_bench --bench primitives
cargo bench -p cycle_bench --features ppe --bench verify
# Run Bedrock node in docker # Run Bedrock node in docker
[working-directory: 'bedrock'] [working-directory: 'bedrock']
run-bedrock: run-bedrock:
@ -30,16 +36,22 @@ run-bedrock:
docker compose up docker compose up
# Run Sequencer # Run Sequencer
[working-directory: 'sequencer_runner'] [working-directory: 'sequencer/service']
run-sequencer: run-sequencer:
@echo "🧠 Running 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 # Run Indexer
[working-directory: 'indexer/service'] [working-directory: 'indexer/service']
run-indexer: run-indexer mock="":
@echo "🔍 Running indexer" @echo "🔍 Running indexer"
RUST_LOG=info RISC0_DEV_MODE=1 cargo run --release -p indexer_service configs/indexer_config.json @if [ "{{mock}}" = "mock" ]; then \
echo "🧪 Using mock data"; \
RUST_LOG=info RISC0_DEV_MODE=1 cargo run --release --features mock-responses -p indexer_service configs/indexer_config.json; \
else \
echo "🚀 Using real data"; \
RUST_LOG=info RISC0_DEV_MODE=1 cargo run --release -p indexer_service configs/indexer_config.json; \
fi
# Run Explorer # Run Explorer
[working-directory: 'explorer_service'] [working-directory: 'explorer_service']
@ -56,6 +68,9 @@ run-wallet +args:
# Clean runtime data # Clean runtime data
clean: clean:
@echo "🧹 Cleaning run artifacts" @echo "🧹 Cleaning run artifacts"
rm -rf sequencer_runner/bedrock_signing_key rm -rf sequencer/service/bedrock_signing_key
rm -rf sequencer_runner/rocksdb rm -rf sequencer/service/rocksdb
rm -rf indexer/service/rocksdb
rm -rf wallet/configs/debug/storage.json rm -rf wallet/configs/debug/storage.json
rm -rf rocksdb
cd bedrock && docker compose down -v

View File

@ -12,7 +12,7 @@ Public accounts are stored on-chain as a visible map from IDs to account states,
### Programmability and selective privacy ### 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 programs 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 programs 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. 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.
@ -71,6 +71,17 @@ This design keeps public transactions as fast as any RISC-Vbased 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 dependencies
### Install build dependencies ### Install build dependencies
@ -130,36 +141,38 @@ RUST_LOG=info RISC0_DEV_MODE=1 cargo run $(pwd)/configs/debug all
``` ```
# Run the sequencer and node # Run the sequencer and node
## Running Manually ## Running Manually
### Normal mode ### Normal mode
The sequencer and logos blockchain node can be run locally: The sequencer and logos blockchain node can be run locally:
1. On one terminal go to the `logos-blockchain/logos-blockchain` repo and run a local logos blockchain node: 1. On one terminal go to the `logos-blockchain/logos-blockchain` repo and run a local logos blockchain node:
- `git checkout master; git pull` - `git checkout master; git pull`
- `cargo clean` - `cargo clean`
- `rm -r ~/.logos-blockchain-circuits` - `rm -r ~/.logos-blockchain-circuits`
- `./scripts/setup-logos-blockchain-circuits.sh` - `./scripts/setup-logos-blockchain-circuits.sh`
- `cargo build --all-features` - `cargo build --all-features`
- `./target/debug/logos-blockchain-node --deployment nodes/node/standalone-deployment-config.yaml nodes/node/standalone-node-config.yaml` - `./target/debug/logos-blockchain-node --deployment nodes/node/standalone-deployment-config.yaml nodes/node/standalone-node-config.yaml`
2. Alternatively (WARNING: This node is outdated) go to ``logos-blockchain/lssa/` repo and run the node from docker: - Alternatively (WARNING: This node is outdated) go to `logos-blockchain/lssa/` repo and run the node from docker:
- `cd bedrock` - `cd bedrock`
- Change line 14 of `docker-compose.yml` from `"0:18080/tcp"` into `"8080:18080/tcp"` - Change line 14 of `docker-compose.yml` from `"0:18080/tcp"` into `"8080:18080/tcp"`
- `docker compose up` - `docker compose up`
3. On another terminal go to the `logos-blockchain/lssa` repo and run indexer service: 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` - `RUST_LOG=info cargo run -p indexer_service indexer/service/configs/indexer_config.json`
4. On another terminal go to the `logos-blockchain/lssa` repo and run the sequencer: 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`
- `cargo leptos serve --release`
### Notes on cleanup ### Notes on cleanup
After stopping services above you need to remove 3 folders to start cleanly: 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) 1. In the `logos-blockchain/logos-blockchain` folder `state` (not needed in case of docker setup)
2. In the `lssa` folder `sequencer_runner/rocksdb` 2. In the `lssa` folder `sequencer/service/rocksdb`
3. In the `lssa` file `sequencer_runner/bedrock_signing_key` 3. In the `lssa` file `sequencer/service/bedrock_signing_key`
4. In the `lssa` folder `indexer/service/rocksdb` 4. In the `lssa` folder `indexer/service/rocksdb`
### Normal mode (`just` commands) ### Normal mode (`just` commands)
@ -207,7 +220,7 @@ This will use a wallet binary built from this repo and not the one installed in
### Standalone mode ### Standalone mode
The sequencer can be run in standalone mode with: The sequencer can be run in standalone mode with:
```bash ```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 ## 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.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,12 +0,0 @@
port: 4400
n_hosts: 4
timeout: 10
# Tracing
tracing_settings:
logger: Stdout
tracing: None
filter: None
metrics: None
console: None
level: DEBUG

View File

@ -0,0 +1,82 @@
blend:
common:
num_blend_layers: 3
minimum_network_size: 30
protocol_name: /blend/integration-tests
data_replication_factor: 0
core:
scheduler:
cover:
message_frequency_per_round: 1.0
intervals_for_safety_buffer: 100
delayer:
maximum_release_delay_in_rounds: 3
minimum_messages_coefficient: 1
normalization_constant: 1.03
activity_threshold_sensitivity: 1
network:
kademlia_protocol_name: /integration/logos-blockchain/kad/1.0.0
identify_protocol_name: /integration/logos-blockchain/identify/1.0.0
chain_sync_protocol_name: /integration/logos-blockchain/chainsync/1.0.0
cryptarchia:
epoch_config:
epoch_stake_distribution_stabilization: 3
epoch_period_nonce_buffer: 3
epoch_period_nonce_stabilization: 4
security_param: 10
slot_activation_coeff:
numerator: 1
denominator: 2
learning_rate: 0.1
sdp_config:
service_params:
BN:
lock_period: 10
inactivity_period: 1
retention_period: 1
timestamp: 0
min_stake:
threshold: 1
timestamp: 0
gossipsub_protocol: /integration/logos-blockchain/cryptarchia/proto/1.0.0
genesis_block:
header:
version: Bedrock
parent_block: '0000000000000000000000000000000000000000000000000000000000000000'
slot: 0
block_root: b5f8787ac23674822414c70eea15d842da38f2e806ede1a73cf7b5cf0277da07
proof_of_leadership:
proof: '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
entropy_contribution: '0000000000000000000000000000000000000000000000000000000000000000'
leader_key: '0000000000000000000000000000000000000000000000000000000000000000'
voucher_cm: '0000000000000000000000000000000000000000000000000000000000000000'
signature: '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
transactions:
- mantle_tx:
ops:
- opcode: 0
payload:
inputs: []
outputs:
- value: 1
pk: d204000000000000000000000000000000000000000000000000000000000000
- value: 100
pk: '2e03b2eff5a45478e7e79668d2a146cf2c5c7925bce927f2b1c67f2ab4fc0d26'
- value: 1
pk: ed266e6e887b9b97059dc1aa1b7b2e19b934291753c6336a163fe4ebaa28e717
- opcode: 17
payload:
channel_id: '0000000000000000000000000000000000000000000000000000000000000000'
inscription: '67656e65736973'
parent: '0000000000000000000000000000000000000000000000000000000000000000'
signer: '0000000000000000000000000000000000000000000000000000000000000000'
execution_gas_price: 0
storage_gas_price: 0
ops_proofs:
- !Ed25519Sig '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
- !Ed25519Sig '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
time:
slot_duration: '1.0'
chain_start_time: PLACEHOLDER_CHAIN_START_TIME
mempool:
pubsub_topic: mantle_e2e_tests

View File

@ -1,46 +1,12 @@
services: services:
cfgsync:
image: ghcr.io/logos-blockchain/logos-blockchain@sha256:000982e751dfd346ca5346b8025c685fc3abc585079c59cde3bde7fd63100657
volumes:
- ./scripts:/etc/logos-blockchain/scripts
- ./cfgsync.yaml:/etc/logos-blockchain/cfgsync.yaml:z
entrypoint: /etc/logos-blockchain/scripts/run_cfgsync.sh
logos-blockchain-node-0: logos-blockchain-node-0:
image: ghcr.io/logos-blockchain/logos-blockchain@sha256:000982e751dfd346ca5346b8025c685fc3abc585079c59cde3bde7fd63100657 image: ghcr.io/logos-blockchain/logos-blockchain@sha256:9f1829dea335c56f6ff68ae37ea872ed5313b96b69e8ffe143c02b7217de85fc
ports: ports:
- "${PORT:-8080}:18080/tcp" - "${PORT:-8080}:18080/tcp"
volumes: volumes:
- ./scripts:/etc/logos-blockchain/scripts - ./scripts:/etc/logos-blockchain/scripts
- ./kzgrs_test_params:/kzgrs_test_params:z - ./kzgrs_test_params:/kzgrs_test_params:z
depends_on: - ./node-config.yaml:/etc/logos-blockchain/node-config.yaml:z
- cfgsync - ./deployment-settings.yaml:/etc/logos-blockchain/deployment-settings.yaml:z
entrypoint: /etc/logos-blockchain/scripts/run_logos_blockchain_node.sh
logos-blockchain-node-1:
image: ghcr.io/logos-blockchain/logos-blockchain@sha256:000982e751dfd346ca5346b8025c685fc3abc585079c59cde3bde7fd63100657
volumes:
- ./scripts:/etc/logos-blockchain/scripts
- ./kzgrs_test_params:/kzgrs_test_params:z
depends_on:
- cfgsync
entrypoint: /etc/logos-blockchain/scripts/run_logos_blockchain_node.sh
logos-blockchain-node-2:
image: ghcr.io/logos-blockchain/logos-blockchain@sha256:000982e751dfd346ca5346b8025c685fc3abc585079c59cde3bde7fd63100657
volumes:
- ./scripts:/etc/logos-blockchain/scripts
- ./kzgrs_test_params:/kzgrs_test_params:z
depends_on:
- cfgsync
entrypoint: /etc/logos-blockchain/scripts/run_logos_blockchain_node.sh
logos-blockchain-node-3:
image: ghcr.io/logos-blockchain/logos-blockchain@sha256:000982e751dfd346ca5346b8025c685fc3abc585079c59cde3bde7fd63100657
volumes:
- ./scripts:/etc/logos-blockchain/scripts
- ./kzgrs_test_params:/kzgrs_test_params:z
depends_on:
- cfgsync
entrypoint: /etc/logos-blockchain/scripts/run_logos_blockchain_node.sh entrypoint: /etc/logos-blockchain/scripts/run_logos_blockchain_node.sh

54
bedrock/node-config.yaml Normal file
View File

@ -0,0 +1,54 @@
blend:
non_ephemeral_signing_key_id: 86c8519f00178e9eb1fe5f4247e4bed77d4c9f6da2fb10e8a1fdd7ba6bc79fa0
core:
zk:
secret_key_kms_id: 64249c75c2cb813578b75d05b215fc95f67cea5862fff047228183f22e63460e
cryptarchia:
service:
bootstrap:
prolonged_bootstrap_period: '1.000000000'
network:
network:
max_connected_peers_to_try_download: 16
max_discovered_peers_to_try_download: 16
leader:
wallet:
funding_pk: "2e03b2eff5a45478e7e79668d2a146cf2c5c7925bce927f2b1c67f2ab4fc0d26"
sdp:
wallet:
funding_pk: ed266e6e887b9b97059dc1aa1b7b2e19b934291753c6336a163fe4ebaa28e717
kms:
backend:
keys:
2e03b2eff5a45478e7e79668d2a146cf2c5c7925bce927f2b1c67f2ab4fc0d26: !Zk 6c645cd4636d9c4c36a37a9aeabcaa3300000000000000000000000000000000
64249c75c2cb813578b75d05b215fc95f67cea5862fff047228183f22e63460e: !Zk 83c851cf4436e8d2fdac33d56d2b235f66431be97e2a20bf241d431713dc720f
ed266e6e887b9b97059dc1aa1b7b2e19b934291753c6336a163fe4ebaa28e717: !Zk 7364705cd4636d9c4c36a37a9aeabcaa00000000000000000000000000000000
86c8519f00178e9eb1fe5f4247e4bed77d4c9f6da2fb10e8a1fdd7ba6bc79fa0: !Ed25519 5cd4636d9c4c36a37a9aeabcaa332c3ec796226af0af48a0b2e70167205af749
wallet:
known_keys:
2e03b2eff5a45478e7e79668d2a146cf2c5c7925bce927f2b1c67f2ab4fc0d26: "2e03b2eff5a45478e7e79668d2a146cf2c5c7925bce927f2b1c67f2ab4fc0d26"
ed266e6e887b9b97059dc1aa1b7b2e19b934291753c6336a163fe4ebaa28e717: "ed266e6e887b9b97059dc1aa1b7b2e19b934291753c6336a163fe4ebaa28e717"
voucher_master_key_id: 2e03b2eff5a45478e7e79668d2a146cf2c5c7925bce927f2b1c67f2ab4fc0d26
api:
backend:
listen_address: 0.0.0.0:18080
cors_origins: []
timeout: 30
max_body_size: 10485760
max_concurrent_requests: 500
tracing:
logger:
stdout: true
stderr: false
file:
directory: "./state/logs"
otlp: null
loki: null
gelf: null
tracing: None
filter: None
metrics: None
console: None
level: "INFO"

View File

@ -2,12 +2,19 @@
set -e set -e
export CFG_FILE_PATH="/config.yaml" \ export POL_PROOF_DEV_MODE=true
CFG_SERVER_ADDR="http://cfgsync:4400" \
CFG_HOST_IP=$(hostname -i) \
CFG_HOST_IDENTIFIER="validator-$(hostname -i)" \
LOG_LEVEL="INFO" \
POL_PROOF_DEV_MODE=true
/usr/bin/logos-blockchain-cfgsync-client && \ # Use static configs mounted from host. Both node-config.yaml and
exec /usr/bin/logos-blockchain-node /config.yaml # deployment-settings.yaml have matching validator keys so the node
# can produce blocks as a single-validator network.
# Copy deployment-settings to a writable path because sed -i can't
# rename on a bind-mounted file.
cp /etc/logos-blockchain/deployment-settings.yaml /deployment-settings.yaml
# Set chain_start_time to "now" so the chain starts immediately.
sed -i "s/PLACEHOLDER_CHAIN_START_TIME/$(date -u '+%Y-%m-%d %H:%M:%S.000000 +00:00:00')/" \
/deployment-settings.yaml
exec /usr/bin/logos-blockchain-node \
/etc/logos-blockchain/node-config.yaml \
--deployment /deployment-settings.yaml

View File

@ -1,20 +0,0 @@
[package]
name = "bedrock_client"
version = "0.1.0"
edition = "2024"
license = { workspace = true }
[dependencies]
common.workspace = true
reqwest.workspace = true
anyhow.workspace = true
tokio-retry.workspace = true
futures.workspace = true
log.workspace = true
serde.workspace = true
humantime-serde.workspace = true
logos-blockchain-common-http-client.workspace = true
logos-blockchain-core.workspace = true
logos-blockchain-chain-broadcast-service.workspace = true
logos-blockchain-chain-service.workspace = true

View File

@ -1,104 +0,0 @@
use std::time::Duration;
use anyhow::{Context as _, Result};
use common::config::BasicAuth;
use futures::{Stream, TryFutureExt};
#[expect(clippy::single_component_path_imports, reason = "Satisfy machete")]
use humantime_serde;
use log::{info, warn};
pub use logos_blockchain_chain_broadcast_service::BlockInfo;
use logos_blockchain_chain_service::CryptarchiaInfo;
pub use logos_blockchain_common_http_client::{CommonHttpClient, Error};
pub use logos_blockchain_core::{block::Block, header::HeaderId, mantle::SignedMantleTx};
use reqwest::{Client, Url};
use serde::{Deserialize, Serialize};
use tokio_retry::Retry;
/// Fibonacci backoff retry strategy configuration
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
pub struct BackoffConfig {
#[serde(with = "humantime_serde")]
pub start_delay: Duration,
pub max_retries: usize,
}
impl Default for BackoffConfig {
fn default() -> Self {
Self {
start_delay: Duration::from_millis(100),
max_retries: 5,
}
}
}
// Simple wrapper
// maybe extend in the future for our purposes
// `Clone` is cheap because `CommonHttpClient` is internally reference counted (`Arc`).
#[derive(Clone)]
pub struct BedrockClient {
http_client: CommonHttpClient,
node_url: Url,
backoff: BackoffConfig,
}
impl BedrockClient {
pub fn new(backoff: BackoffConfig, node_url: Url, auth: Option<BasicAuth>) -> Result<Self> {
info!("Creating Bedrock client with node URL {node_url}");
let client = Client::builder()
//Add more fields if needed
.timeout(std::time::Duration::from_secs(60))
.build()
.context("Failed to build HTTP client")?;
let auth = auth.map(|a| {
logos_blockchain_common_http_client::BasicAuthCredentials::new(a.username, a.password)
});
let http_client = CommonHttpClient::new_with_client(client, auth);
Ok(Self {
http_client,
node_url,
backoff,
})
}
pub async fn post_transaction(&self, tx: SignedMantleTx) -> Result<(), Error> {
Retry::spawn(self.backoff_strategy(), || {
self.http_client
.post_transaction(self.node_url.clone(), tx.clone())
})
.await
}
pub async fn get_lib_stream(&self) -> Result<impl Stream<Item = BlockInfo>, Error> {
self.http_client.get_lib_stream(self.node_url.clone()).await
}
pub async fn get_block_by_id(
&self,
header_id: HeaderId,
) -> Result<Option<Block<SignedMantleTx>>, Error> {
Retry::spawn(self.backoff_strategy(), || {
self.http_client
.get_block_by_id(self.node_url.clone(), header_id)
.inspect_err(|err| warn!("Block fetching failed with error: {err:#}"))
})
.await
}
pub async fn get_consensus_info(&self) -> Result<CryptarchiaInfo, Error> {
Retry::spawn(self.backoff_strategy(), || {
self.http_client
.consensus_info(self.node_url.clone())
.inspect_err(|err| warn!("Block fetching failed with error: {err:#}"))
})
.await
}
fn backoff_strategy(&self) -> impl Iterator<Item = Duration> {
tokio_retry::strategy::FibonacciBackoff::from_millis(
self.backoff.start_delay.as_millis() as u64
)
.take(self.backoff.max_retries)
}
}

54
clippy.toml Normal file
View File

@ -0,0 +1,54 @@
module-item-order-groupings = [
[
"use",
[
"use",
],
],
[
"modules",
[
"extern_crate",
"mod",
"foreign_mod",
],
],
[
"macros",
[
"macro",
],
],
[
"global_asm",
[
"global_asm",
],
],
[
"UPPER_SNAKE_CASE",
[
"static",
"const",
],
],
[
"PascalCase",
[
"ty_alias",
"enum",
"struct",
"union",
"trait",
"trait_alias",
"impl",
],
],
[
"lower_snake_case",
[
"fn",
],
],
]
source-item-ordering = ["module"]

View File

@ -4,21 +4,22 @@ version = "0.1.0"
edition = "2024" edition = "2024"
license = { workspace = true } license = { workspace = true }
[lints]
workspace = true
[dependencies] [dependencies]
nssa.workspace = true nssa.workspace = true
nssa_core.workspace = true nssa_core.workspace = true
authenticated_transfer_core.workspace = true
clock_core.workspace = true
anyhow.workspace = true anyhow.workspace = true
thiserror.workspace = true thiserror.workspace = true
serde_json.workspace = true
serde.workspace = true serde.workspace = true
serde_with.workspace = true serde_with.workspace = true
reqwest.workspace = true base64.workspace = true
sha2.workspace = true sha2.workspace = true
log.workspace = true log.workspace = true
hex.workspace = true hex.workspace = true
borsh.workspace = true borsh.workspace = true
bytesize.workspace = true
base64.workspace = true
url.workspace = true
logos-blockchain-common-http-client.workspace = true logos-blockchain-common-http-client.workspace = true

View File

@ -1,14 +1,12 @@
use borsh::{BorshDeserialize, BorshSerialize}; use borsh::{BorshDeserialize, BorshSerialize};
use nssa::AccountId; use nssa_core::BlockId;
pub use nssa_core::Timestamp;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256, digest::FixedOutput}; use sha2::{Digest as _, Sha256, digest::FixedOutput as _};
use crate::{HashType, transaction::NSSATransaction}; use crate::{HashType, transaction::NSSATransaction};
pub type MantleMsgId = [u8; 32]; pub type MantleMsgId = [u8; 32];
pub type BlockHash = HashType; pub type BlockHash = HashType;
pub type BlockId = u64;
pub type TimeStamp = u64;
#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)] #[derive(Debug, Clone, BorshSerialize, BorshDeserialize)]
pub struct BlockMeta { pub struct BlockMeta {
@ -20,7 +18,7 @@ pub struct BlockMeta {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
/// Our own hasher. /// Our own hasher.
/// Currently it is SHA256 hasher wrapper. May change in a future. /// Currently it is SHA256 hasher wrapper. May change in a future.
pub struct OwnHasher {} pub struct OwnHasher;
impl OwnHasher { impl OwnHasher {
fn hash(data: &[u8]) -> HashType { fn hash(data: &[u8]) -> HashType {
@ -36,7 +34,7 @@ pub struct BlockHeader {
pub block_id: BlockId, pub block_id: BlockId,
pub prev_block_hash: BlockHash, pub prev_block_hash: BlockHash,
pub hash: BlockHash, pub hash: BlockHash,
pub timestamp: TimeStamp, pub timestamp: Timestamp,
pub signature: nssa::Signature, pub signature: nssa::Signature,
} }
@ -60,23 +58,47 @@ pub struct Block {
pub bedrock_parent_id: MantleMsgId, 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)] #[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
pub struct HashableBlockData { pub struct HashableBlockData {
pub block_id: BlockId, pub block_id: BlockId,
pub prev_block_hash: BlockHash, pub prev_block_hash: BlockHash,
pub timestamp: TimeStamp, pub timestamp: Timestamp,
pub transactions: Vec<NSSATransaction>, pub transactions: Vec<NSSATransaction>,
} }
impl HashableBlockData { impl HashableBlockData {
#[must_use]
pub fn into_pending_block( pub fn into_pending_block(
self, self,
signing_key: &nssa::PrivateKey, signing_key: &nssa::PrivateKey,
bedrock_parent_id: MantleMsgId, bedrock_parent_id: MantleMsgId,
) -> Block { ) -> Block {
const PREFIX: &[u8; 32] = b"/LEE/v0.3/Message/Block/\x00\x00\x00\x00\x00\x00\x00\x00";
let data_bytes = borsh::to_vec(&self).unwrap(); let data_bytes = borsh::to_vec(&self).unwrap();
let signature = nssa::Signature::new(signing_key, &data_bytes); let mut bytes = Vec::with_capacity(
let hash = OwnHasher::hash(&data_bytes); PREFIX
.len()
.checked_add(data_bytes.len())
.expect("length overflow"),
);
bytes.extend_from_slice(PREFIX);
bytes.extend_from_slice(&data_bytes);
let hash = OwnHasher::hash(&bytes);
let signature = nssa::Signature::new(signing_key, &hash.0);
Block { Block {
header: BlockHeader { header: BlockHeader {
block_id: self.block_id, block_id: self.block_id,
@ -92,10 +114,6 @@ impl HashableBlockData {
bedrock_parent_id, bedrock_parent_id,
} }
} }
pub fn block_hash(&self) -> BlockHash {
OwnHasher::hash(&borsh::to_vec(&self).unwrap())
}
} }
impl From<Block> for HashableBlockData { impl From<Block> for HashableBlockData {
@ -109,26 +127,12 @@ impl From<Block> for HashableBlockData {
} }
} }
/// Helper struct for account (de-)serialization
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AccountInitialData {
pub account_id: AccountId,
pub balance: u128,
}
/// Helper struct to (de-)serialize initial commitments
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CommitmentsInitialData {
pub npk: nssa_core::NullifierPublicKey,
pub account: nssa_core::account::Account,
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{HashType, block::HashableBlockData, test_utils}; use crate::{HashType, block::HashableBlockData, test_utils};
#[test] #[test]
fn test_encoding_roundtrip() { fn encoding_roundtrip() {
let transactions = vec![test_utils::produce_dummy_empty_transaction()]; let transactions = vec![test_utils::produce_dummy_empty_transaction()];
let block = test_utils::produce_dummy_block(1, Some(HashType([1; 32])), transactions); let block = test_utils::produce_dummy_block(1, Some(HashType([1; 32])), transactions);
let hashable = HashableBlockData::from(block); let hashable = HashableBlockData::from(block);

View 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)
}

View File

@ -42,14 +42,14 @@ impl FromStr for BasicAuth {
})?; })?;
Ok(Self { Ok(Self {
username: username.to_string(), username: username.to_owned(),
password: password.map(|p| p.to_string()), password: password.map(std::string::ToString::to_string),
}) })
} }
} }
impl From<BasicAuth> for BasicAuthCredentials { impl From<BasicAuth> for BasicAuthCredentials {
fn from(value: BasicAuth) -> Self { fn from(value: BasicAuth) -> Self {
BasicAuthCredentials::new(value.username, value.password) Self::new(value.username, value.password)
} }
} }

View File

@ -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 {
SequencerClientError::InternalError(value)
}
}
#[derive(Debug, thiserror::Error)]
pub enum ExecutionFailureKind {
#[error("Failed to get account data from sequencer")]
SequencerError,
#[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),
}

View File

@ -4,10 +4,8 @@ use borsh::{BorshDeserialize, BorshSerialize};
use serde_with::{DeserializeFromStr, SerializeDisplay}; use serde_with::{DeserializeFromStr, SerializeDisplay};
pub mod block; pub mod block;
mod borsh_base64;
pub mod config; pub mod config;
pub mod error;
pub mod rpc_primitives;
pub mod sequencer_client;
pub mod transaction; pub mod transaction;
// Module for tests utility functions // Module for tests utility functions
@ -17,7 +15,6 @@ pub mod test_utils;
pub const PINATA_BASE58: &str = "EfQhKQAkX2FJiwNii2WFQsGndjvF1Mzd7RuVe7QdPLw7"; pub const PINATA_BASE58: &str = "EfQhKQAkX2FJiwNii2WFQsGndjvF1Mzd7RuVe7QdPLw7";
#[derive( #[derive(
Debug,
Default, Default,
Copy, Copy,
Clone, Clone,
@ -37,13 +34,19 @@ impl Display for HashType {
} }
} }
impl std::fmt::Debug for HashType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", hex::encode(self.0))
}
}
impl FromStr for HashType { impl FromStr for HashType {
type Err = hex::FromHexError; type Err = hex::FromHexError;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut bytes = [0u8; 32]; let mut bytes = [0_u8; 32];
hex::decode_to_slice(s, &mut bytes)?; hex::decode_to_slice(s, &mut bytes)?;
Ok(HashType(bytes)) Ok(Self(bytes))
} }
} }
@ -61,7 +64,7 @@ impl From<HashType> for [u8; 32] {
impl From<[u8; 32]> for HashType { impl From<[u8; 32]> for HashType {
fn from(bytes: [u8; 32]) -> Self { fn from(bytes: [u8; 32]) -> Self {
HashType(bytes) Self(bytes)
} }
} }
@ -69,7 +72,7 @@ impl TryFrom<Vec<u8>> for HashType {
type Error = <[u8; 32] as TryFrom<Vec<u8>>>::Error; type Error = <[u8; 32] as TryFrom<Vec<u8>>>::Error;
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> { fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
Ok(HashType(value.try_into()?)) Ok(Self(value.try_into()?))
} }
} }
@ -85,7 +88,7 @@ mod tests {
#[test] #[test]
fn serialization_roundtrip() { fn serialization_roundtrip() {
let original = HashType([1u8; 32]); let original = HashType([1_u8; 32]);
let serialized = original.to_string(); let serialized = original.to_string();
let deserialized = HashType::from_str(&serialized).unwrap(); let deserialized = HashType::from_str(&serialized).unwrap();
assert_eq!(original, deserialized); assert_eq!(original, deserialized);

View File

@ -1,187 +0,0 @@
use std::fmt;
use serde_json::{Value, to_value};
#[derive(serde::Serialize)]
pub struct RpcParseError(pub String);
#[allow(clippy::too_long_first_doc_paragraph)]
/// 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)]
#[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)]
#[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)]
#[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.
pub fn new(code: i64, message: String, data: Option<Value>) -> Self {
RpcError {
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()
)));
}
};
RpcError::new(-32_602, "Invalid params".to_owned(), Some(value))
}
/// Create a server error.
pub fn server_error<E: serde::Serialize>(e: Option<E>) -> Self {
RpcError::new(
-32_000,
"Server error".to_owned(),
e.map(|v| to_value(v).expect("Must be representable in JSON")),
)
}
/// Create a parse error.
pub fn parse_error(e: String) -> Self {
RpcError {
code: -32_700,
message: "Parse error".to_owned(),
data: Some(Value::String(e.clone())),
error_struct: Some(RpcErrorKind::RequestValidationError(
RpcRequestValidationErrorKind::ParseError { error_message: e },
)),
}
}
pub fn serialization_error(e: &str) -> Self {
RpcError::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
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)
}
}
pub fn new_internal_error(error_data: Option<Value>, info: &str) -> Self {
RpcError {
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 {
RpcError {
code: -32_000,
message: "Server error".to_owned(),
data: error_data,
error_struct: Some(RpcErrorKind::HandlerError(error_struct)),
}
}
/// Create a method not found error.
pub fn method_not_found(method: String) -> Self {
RpcError {
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 {
unsafe { core::hint::unreachable_unchecked() }
}
}
impl fmt::Display for ServerError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ServerError::Timeout => write!(f, "ServerError: Timeout"),
ServerError::Closed => write!(f, "ServerError: Closed"),
}
}
}
impl From<ServerError> for RpcError {
fn from(e: ServerError) -> RpcError {
let error_data = match to_value(&e) {
Ok(value) => value,
Err(_err) => {
return RpcError::new_internal_error(None, "Failed to serialize ServerError");
}
};
RpcError::new_internal_error(Some(error_data), e.to_string().as_str())
}
}

View File

@ -1,567 +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, Serializer},
};
use serde_json::{Result as JsonResult, Value};
use super::errors::RpcError;
#[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 {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct VersionVisitor;
#[allow(clippy::needless_lifetimes)]
impl<'de> Visitor<'de> 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)]
#[serde(deny_unknown_fields)]
pub struct Request {
jsonrpc: Version,
pub method: String,
#[serde(default, skip_serializing_if = "Value::is_null")]
pub params: Value,
pub id: Value,
}
impl Request {
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.
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.
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).
#[derive(Debug, Clone, PartialEq)]
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(ref value) => sub.serialize_field("result", value),
Err(ref err) => sub.serialize_field("error", err),
}?;
sub.serialize_field("id", &self.id)?;
sub.end()
}
}
/// 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)
}
/// 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
#[allow(dead_code)]
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(Response {
jsonrpc: Version,
result,
id: wr.id,
})
}
}
/// A notification (doesn't expect an answer).
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq)]
#[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<Message>),
/// 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.
pub fn request(method: String, params: Value) -> Self {
let id = Value::from("dontcare");
Message::Request(Request {
jsonrpc: Version,
method,
params,
id,
})
}
/// Create a top-level error (without an ID).
pub fn error(error: RpcError) -> Self {
Message::Response(Response {
jsonrpc: Version,
result: Err(error),
id: Value::Null,
})
}
/// A constructor for a notification.
pub fn notification(method: String, params: Value) -> Self {
Message::Notification(Notification {
jsonrpc: Version,
method,
params,
})
}
/// A constructor for a response.
pub fn response(id: Value, result: Result<Value, RpcError>) -> Self {
Message::Response(Response {
jsonrpc: Version,
result,
id,
})
}
/// Returns id or Null if there is no id.
pub fn id(&self) -> Value {
match self {
Message::Request(req) => req.id.clone(),
_ => Value::Null,
}
}
}
/// A broken message.
///
/// Protocol-level errors.
#[derive(Debug, Clone, PartialEq, 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.
pub fn reply(&self) -> Message {
match *self {
Broken::Unmatched(_) => Message::error(RpcError::parse_error(
"JSON RPC Request format was expected".to_owned(),
)),
Broken::SyntaxError(ref 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())),
}
}
pub type Parsed = Result<Message, Broken>;
/// 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())
}
impl From<Message> for String {
fn from(val: Message) -> Self {
::serde_json::ser::to_string(&val).unwrap()
}
}
impl From<Message> for Vec<u8> {
fn from(val: Message) -> Self {
::serde_json::ser::to_vec(&val).unwrap()
}
}
#[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]
#[allow(clippy::too_many_lines)]
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]
#[allow(clippy::panic)]
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(r#"{]"#) {
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]
#[allow(clippy::panic)]
#[ignore]
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(ref 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(ref 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(ref 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");
}
}
}

View File

@ -1,55 +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 {
RpcConfig {
addr: "0.0.0.0:3040".to_owned(),
cors_allowed_origins: vec!["*".to_owned()],
limits_config: RpcLimitsConfig::default(),
}
}
}
impl RpcConfig {
pub fn new(addr: &str) -> Self {
RpcConfig {
addr: addr.to_owned(),
..Default::default()
}
}
pub fn with_port(port: u16) -> Self {
RpcConfig {
addr: format!("0.0.0.0:{port}"),
..Default::default()
}
}
}

View File

@ -1,27 +0,0 @@
use serde::de::DeserializeOwned;
use serde_json::Value;
use super::errors::RpcParseError;
pub trait RpcRequest: Sized {
fn parse(value: Option<Value>) -> Result<Self, RpcParseError>;
}
pub fn parse_params<T: DeserializeOwned>(value: Option<Value>) -> Result<T, RpcParseError> {
if let Some(value) = value {
serde_json::from_value(value)
.map_err(|err| RpcParseError(format!("Failed parsing args: {err}")))
} else {
Err(RpcParseError("Require at least one parameter".to_owned()))
}
}
#[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)
}
}
};
}

View File

@ -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};
#[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>>,
}
mod base64_deser {
use base64::{Engine as _, engine::general_purpose};
use serde::{self, Deserialize, Deserializer, Serializer, ser::SerializeSeq as _};
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)
}
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()
}
}
}
#[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,
}

View File

@ -1,351 +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(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_string(), payload);
log::debug!(
"Calling method {method} with payload {request:?} to sequencer at {}",
self.sequencer_addr
);
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 = call_builder.json(&request).send().await?;
let response_vall = call_res.json::<Value>().await?;
#[derive(Debug, Clone, Deserialize)]
#[allow(dead_code)]
pub struct SequencerRpcResponse {
pub jsonrpc: String,
pub result: serde_json::Value,
pub id: u64,
}
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)
}
}

View File

@ -3,49 +3,55 @@ use nssa::AccountId;
use crate::{ use crate::{
HashType, HashType,
block::{Block, HashableBlockData}, block::{Block, HashableBlockData},
transaction::NSSATransaction, transaction::{NSSATransaction, clock_invocation},
}; };
// Helpers // Helpers
#[must_use]
pub fn sequencer_sign_key_for_testing() -> nssa::PrivateKey { pub fn sequencer_sign_key_for_testing() -> nssa::PrivateKey {
nssa::PrivateKey::try_new([37; 32]).unwrap() nssa::PrivateKey::try_new([37; 32]).unwrap()
} }
// Dummy producers // Dummy producers
/// Produce dummy block with /// Produce dummy block with provided transactions + clock transaction an the end.
/// ///
/// `id` - block id, provide zero for genesis /// `id` - block id, provide zero for genesis.
/// ///
/// `prev_hash` - hash of previous block, provide None for genesis /// `prev_hash` - hash of previous block, provide None for genesis.
/// ///
/// `transactions` - vector of `EncodedTransaction` objects /// `transactions` - vector of `EncodedTransaction` objects.
#[must_use]
pub fn produce_dummy_block( pub fn produce_dummy_block(
id: u64, id: u64,
prev_hash: Option<HashType>, prev_hash: Option<HashType>,
transactions: Vec<NSSATransaction>, mut transactions: Vec<NSSATransaction>,
) -> Block { ) -> Block {
transactions.push(NSSATransaction::Public(clock_invocation(
id.saturating_mul(100),
)));
let block_data = HashableBlockData { let block_data = HashableBlockData {
block_id: id, block_id: id,
prev_block_hash: prev_hash.unwrap_or_default(), prev_block_hash: prev_hash.unwrap_or_default(),
timestamp: id * 100, timestamp: id.saturating_mul(100),
transactions, transactions,
}; };
block_data.into_pending_block(&sequencer_sign_key_for_testing(), [0; 32]) block_data.into_pending_block(&sequencer_sign_key_for_testing(), [0; 32])
} }
#[must_use]
pub fn produce_dummy_empty_transaction() -> NSSATransaction { pub fn produce_dummy_empty_transaction() -> NSSATransaction {
let program_id = nssa::program::Program::authenticated_transfer_program().id(); let program_id = nssa::program::Program::authenticated_transfer_program().id();
let account_ids = vec![]; let account_ids = vec![];
let nonces = vec![]; let nonces = vec![];
let instruction_data: u128 = 0;
let message = nssa::public_transaction::Message::try_new( let message = nssa::public_transaction::Message::try_new(
program_id, program_id,
account_ids, account_ids,
nonces, nonces,
instruction_data, authenticated_transfer_core::Instruction::Initialize,
) )
.unwrap(); .unwrap();
let private_key = nssa::PrivateKey::try_new([1; 32]).unwrap(); let private_key = nssa::PrivateKey::try_new([1; 32]).unwrap();
@ -56,24 +62,27 @@ pub fn produce_dummy_empty_transaction() -> NSSATransaction {
NSSATransaction::Public(nssa_tx) NSSATransaction::Public(nssa_tx)
} }
#[must_use]
pub fn create_transaction_native_token_transfer( pub fn create_transaction_native_token_transfer(
from: AccountId, from: AccountId,
nonce: u128, nonce: u128,
to: AccountId, to: AccountId,
balance_to_move: u128, balance_to_move: u128,
signing_key: nssa::PrivateKey, signing_key: &nssa::PrivateKey,
) -> NSSATransaction { ) -> NSSATransaction {
let account_ids = vec![from, to]; 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 program_id = nssa::program::Program::authenticated_transfer_program().id();
let message = nssa::public_transaction::Message::try_new( let message = nssa::public_transaction::Message::try_new(
program_id, program_id,
account_ids, account_ids,
nonces, nonces,
balance_to_move, authenticated_transfer_core::Instruction::Transfer {
amount: balance_to_move,
},
) )
.unwrap(); .unwrap();
let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[&signing_key]); let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]);
let nssa_tx = nssa::PublicTransaction::new(message, witness_set); let nssa_tx = nssa::PublicTransaction::new(message, witness_set);

View File

@ -1,6 +1,7 @@
use borsh::{BorshDeserialize, BorshSerialize}; use borsh::{BorshDeserialize, BorshSerialize};
use log::warn; use log::warn;
use nssa::{AccountId, V02State}; use nssa::{AccountId, V03State, ValidatedStateDiff};
use nssa_core::{BlockId, Timestamp};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::HashType; use crate::HashType;
@ -12,20 +13,34 @@ pub enum NSSATransaction {
ProgramDeployment(nssa::ProgramDeploymentTransaction), 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 { impl NSSATransaction {
#[must_use]
pub fn hash(&self) -> HashType { pub fn hash(&self) -> HashType {
HashType(match self { HashType(match self {
NSSATransaction::Public(tx) => tx.hash(), Self::Public(tx) => tx.hash(),
NSSATransaction::PrivacyPreserving(tx) => tx.hash(), Self::PrivacyPreserving(tx) => tx.hash(),
NSSATransaction::ProgramDeployment(tx) => tx.hash(), Self::ProgramDeployment(tx) => tx.hash(),
}) })
} }
#[must_use]
pub fn affected_public_account_ids(&self) -> Vec<AccountId> { pub fn affected_public_account_ids(&self) -> Vec<AccountId> {
match self { match self {
NSSATransaction::ProgramDeployment(tx) => tx.affected_public_account_ids(), Self::ProgramDeployment(tx) => tx.affected_public_account_ids(),
NSSATransaction::Public(tx) => tx.affected_public_account_ids(), Self::Public(tx) => tx.affected_public_account_ids(),
NSSATransaction::PrivacyPreserving(tx) => tx.affected_public_account_ids(), Self::PrivacyPreserving(tx) => tx.affected_public_account_ids(),
} }
} }
@ -33,39 +48,85 @@ impl NSSATransaction {
pub fn transaction_stateless_check(self) -> Result<Self, TransactionMalformationError> { pub fn transaction_stateless_check(self) -> Result<Self, TransactionMalformationError> {
// Stateless checks here // Stateless checks here
match self { match self {
NSSATransaction::Public(tx) => { Self::Public(tx) => {
if tx.witness_set().is_valid_for(tx.message()) { if tx.witness_set().is_valid_for(tx.message()) {
Ok(NSSATransaction::Public(tx)) Ok(Self::Public(tx))
} else { } else {
Err(TransactionMalformationError::InvalidSignature) Err(TransactionMalformationError::InvalidSignature)
} }
} }
NSSATransaction::PrivacyPreserving(tx) => { Self::PrivacyPreserving(tx) => {
if tx.witness_set().signatures_are_valid_for(tx.message()) { if tx.witness_set().signatures_are_valid_for(tx.message()) {
Ok(NSSATransaction::PrivacyPreserving(tx)) Ok(Self::PrivacyPreserving(tx))
} else { } else {
Err(TransactionMalformationError::InvalidSignature) Err(TransactionMalformationError::InvalidSignature)
} }
} }
NSSATransaction::ProgramDeployment(tx) => Ok(NSSATransaction::ProgramDeployment(tx)), Self::ProgramDeployment(tx) => Ok(Self::ProgramDeployment(tx)),
} }
} }
/// Validates the transaction against the current state and returns the resulting diff
/// without applying it. Rejects transactions that modify clock or faucet system accounts,
/// whether directly or indirectly via chain calls.
///
/// This check is required for all user transactions. Only sequencer transactions may bypass
/// this check.
pub fn validate_on_state(
&self,
state: &V03State,
block_id: BlockId,
timestamp: Timestamp,
) -> Result<ValidatedStateDiff, nssa::error::NssaError> {
let diff = match self {
Self::Public(tx) => {
ValidatedStateDiff::from_public_transaction(tx, state, block_id, timestamp)
}
Self::PrivacyPreserving(tx) => ValidatedStateDiff::from_privacy_preserving_transaction(
tx, state, block_id, timestamp,
),
Self::ProgramDeployment(tx) => {
ValidatedStateDiff::from_program_deployment_transaction(tx, state)
}
}?;
let public_diff = diff.public_diff();
let touches_clock = nssa::CLOCK_PROGRAM_ACCOUNT_IDS.iter().any(|id| {
public_diff
.get(id)
.is_some_and(|post| *post != state.get_account_by_id(*id))
});
if touches_clock {
return Err(nssa::error::NssaError::InvalidInput(
"Transaction modifies system clock accounts".into(),
));
}
let faucet_id = nssa::system_faucet_account_id();
if public_diff
.get(&faucet_id)
.is_some_and(|post| *post != state.get_account_by_id(faucet_id))
{
return Err(nssa::error::NssaError::InvalidInput(
"Transaction modifies system faucet account".into(),
));
}
Ok(diff)
}
/// Validates the transaction against the current state, rejects modifications to clock
/// system accounts, and applies the resulting diff to the state.
pub fn execute_check_on_state( pub fn execute_check_on_state(
self, self,
state: &mut V02State, state: &mut V03State,
block_id: BlockId,
timestamp: Timestamp,
) -> Result<Self, nssa::error::NssaError> { ) -> Result<Self, nssa::error::NssaError> {
match &self { let diff = self
NSSATransaction::Public(tx) => state.transition_from_public_transaction(tx), .validate_on_state(state, block_id, timestamp)
NSSATransaction::PrivacyPreserving(tx) => { .inspect_err(|err| warn!("Error at transition {err:#?}"))?;
state.transition_from_privacy_preserving_transaction(tx) state.apply_state_diff(diff);
}
NSSATransaction::ProgramDeployment(tx) => {
state.transition_from_program_deployment_transaction(tx)
}
}
.inspect_err(|err| warn!("Error at transition {err:#?}"))?;
Ok(self) Ok(self)
} }
} }
@ -89,7 +150,7 @@ impl From<nssa::ProgramDeploymentTransaction> for NSSATransaction {
} }
#[derive( #[derive(
Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, BorshSerialize, BorshDeserialize, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, BorshSerialize, BorshDeserialize,
)] )]
pub enum TxKind { pub enum TxKind {
Public, Public,
@ -97,7 +158,7 @@ pub enum TxKind {
ProgramDeployment, ProgramDeployment,
} }
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, thiserror::Error)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, thiserror::Error)]
pub enum TransactionMalformationError { pub enum TransactionMalformationError {
#[error("Invalid signature(-s)")] #[error("Invalid signature(-s)")]
InvalidSignature, InvalidSignature,
@ -106,3 +167,20 @@ pub enum TransactionMalformationError {
#[error("Transaction size {size} exceeds maximum allowed size of {max} bytes")] #[error("Transaction size {size} exceeds maximum allowed size of {max} bytes")]
TransactionTooLarge { size: usize, max: usize }, TransactionTooLarge { size: usize, max: usize },
} }
/// Returns the canonical Clock Program invocation transaction for the given block timestamp.
/// Every valid block must end with exactly one occurrence of this transaction.
#[must_use]
pub fn clock_invocation(timestamp: clock_core::Instruction) -> nssa::PublicTransaction {
let message = nssa::public_transaction::Message::try_new(
nssa::program::Program::clock().id(),
clock_core::CLOCK_PROGRAM_ACCOUNT_IDS.to_vec(),
vec![],
timestamp,
)
.expect("Clock invocation message should always be constructable");
nssa::PublicTransaction::new(
message,
nssa::public_transaction::WitnessSet::from_raw_parts(vec![]),
)
}

View File

@ -20,8 +20,8 @@ e.g.:
``` ```
▶ wallet account list ▶ wallet account list
Preconfigured Public/Gj1mJy5W7J5pfmLRujmQaLfLMWidNxQ6uwnhb666ZwHw, Preconfigured Public/7wHg9sbJwc6h3NP1S9bekfAzB8CHifEcxKswCKUt3YQo,
Preconfigured Public/BLgCRDXYdQPMMWVHYRFGQZbgeHx9frkipa8GtpG2Syqy, Preconfigured Public/6iArKUXxhUJqS7kCaPNhwMWt3ro71PDyBj7jwAyE2VQV,
Preconfigured Private/3oCG8gqdKLMegw4rRfyaMQvuPHpcASt7xwttsmnZLSkw, Preconfigured Private/3oCG8gqdKLMegw4rRfyaMQvuPHpcASt7xwttsmnZLSkw,
Preconfigured Private/AKTcXgJ1xoynta1Ec7y6Jso1z1JQtHqd7aPQ1h9er6xX, Preconfigured Private/AKTcXgJ1xoynta1Ec7y6Jso1z1JQtHqd7aPQ1h9er6xX,
/ Public/8DstRgMQrB2N9a7ymv98RDDbt8nctrP9ZzaNRSpKDZSu, / Public/8DstRgMQrB2N9a7ymv98RDDbt8nctrP9ZzaNRSpKDZSu,
@ -93,6 +93,83 @@ Only `Public/2gJJjtG9UivBGEhA1Jz6waZQx1cwfYupC5yvKEweHaeH` is used for completio
exec zsh exec zsh
``` ```
> **Note:** After updating the completion script, re-run step 1 to copy the new file, then rebuild the cache:
> ```sh
> cp _wallet ~/.oh-my-zsh/custom/plugins/wallet/
> rm -rf ~/.zcompdump* && exec zsh
> ```
### Requirements
The completion script calls `wallet account list` to dynamically fetch account IDs. Ensure the `wallet` command is in your `$PATH`.
### Usage
```sh
# Main commands
wallet <TAB>
# Account subcommands
wallet account <TAB>
# Options for auth-transfer send
wallet auth-transfer send --<TAB>
# Account types when creating
wallet account new <TAB>
# Shows: public private
# Account IDs (fetched dynamically)
wallet account get --account-id <TAB>
# Shows: Public/... Private/...
```
## Bash
Works with bash 4+. The `bash-completion` package is required for auto-sourcing from
`/etc/bash_completion.d/`; without it, source the file directly from `~/.bashrc` instead.
### Features
- Full completion for all wallet subcommands
- Contextual option completion for each command
- Dynamic account ID completion via `wallet account list`
- Falls back to `Public/` / `Private/` prefixes when no accounts are available
Note that only accounts created by the user auto-complete (same filtering as zsh — see above).
### Installation
#### Option A — source directly from `~/.bashrc` (works everywhere)
```sh
echo "source $(pwd)/completions/bash/wallet" >> ~/.bashrc
exec bash
```
#### Option B — system-wide via `bash-completion`
1. Copy the file:
```sh
cp ./bash/wallet /etc/bash_completion.d/wallet
```
2. Ensure `bash-completion` is initialised in every interactive shell. On many Linux
distributions (e.g. Fedora) it is only sourced for **login** shells via
`/etc/profile.d/bash_completion.sh`. For non-login shells (e.g. a bash session started
inside zsh), add this to `~/.bashrc`:
```sh
[[ -f /usr/share/bash-completion/bash_completion ]] && source /usr/share/bash-completion/bash_completion
```
3. Reload your shell:
```sh
exec bash
```
### Requirements ### Requirements
The completion script calls `wallet account list` to dynamically fetch account IDs. Ensure the `wallet` command is in your `$PATH`. The completion script calls `wallet account list` to dynamically fetch account IDs. Ensure the `wallet` command is in your `$PATH`.
@ -120,14 +197,13 @@ wallet account get --account-id <TAB>
## Troubleshooting ## Troubleshooting
### Completions not appearing ### Zsh completions not appearing
1. Check that `compinit` is called in your `.zshrc` 1. Check that `compinit` is called in your `.zshrc`
2. Rebuild the completion cache: 2. Rebuild the completion cache:
```sh ```sh
rm -f ~/.zcompdump* rm -rf ~/.zcompdump* && exec zsh
exec zsh
``` ```
### Account IDs not completing ### Account IDs not completing

591
completions/bash/wallet Normal file
View File

@ -0,0 +1,591 @@
#!/usr/bin/env bash
# Bash completion script for the wallet CLI
# See instructions in ../README.md
# Helper function to complete account IDs
# Uses `wallet account list` to get available accounts
# Only includes accounts with /N prefix (where N is a number)
_wallet_complete_account_id() {
local cur="$1"
local accounts
if command -v wallet &>/dev/null; then
accounts=$(wallet account list 2>/dev/null | grep '^/[0-9]' | awk '{print $2}' | tr -d ',')
fi
if [[ -n "$accounts" ]]; then
COMPREPLY=($(compgen -W "$accounts" -- "$cur"))
else
COMPREPLY=($(compgen -W "Public/ Private/" -- "$cur"))
compopt -o nospace 2>/dev/null
fi
}
# Helper function to complete account labels
_wallet_complete_account_label() {
local cur="$1"
local labels
if command -v wallet &>/dev/null; then
labels=$(wallet account list 2>/dev/null | grep -o '\[.*\]' | sed 's/^\[//;s/\]$//')
fi
if [[ -n "$labels" ]]; then
COMPREPLY=($(compgen -W "$labels" -- "$cur"))
fi
}
_wallet() {
local cur prev words cword
_init_completion 2>/dev/null || {
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
words=("${COMP_WORDS[@]}")
cword=$COMP_CWORD
}
local commands="auth-transfer chain-info account pinata token amm ata check-health config restore-keys deploy-program help"
# Find the main command and subcommand by scanning words before the cursor.
# Global options that take a value are skipped along with their argument.
local cmd="" subcmd=""
local cmd_idx=0 subcmd_idx=0
local i
for ((i = 1; i < cword; i++)); do
local w="${words[$i]}"
case "$w" in
--auth)
((i++)) # skip the auth value
;;
-c | --continuous-run)
# boolean flag, no value
;;
-*)
# unrecognised option, skip
;;
*)
if [[ -z "$cmd" ]]; then
cmd="$w"
cmd_idx=$i
elif [[ -z "$subcmd" ]]; then
subcmd="$w"
subcmd_idx=$i
fi
;;
esac
done
local config_keys="override_rust_log sequencer_addr seq_poll_timeout seq_tx_poll_max_blocks seq_poll_max_retries seq_block_poll_max_amount initial_accounts basic_auth"
case "$cmd" in
"")
# Completing the main command or a global option
if [[ "$prev" == "--auth" ]]; then
return # completing the --auth value; no suggestions
fi
case "$cur" in
-*)
COMPREPLY=($(compgen -W "-c --continuous-run --auth" -- "$cur"))
;;
*)
COMPREPLY=($(compgen -W "$commands" -- "$cur"))
;;
esac
;;
auth-transfer)
case "$subcmd" in
"")
COMPREPLY=($(compgen -W "init send help" -- "$cur"))
;;
init)
case "$prev" in
--account-id)
_wallet_complete_account_id "$cur"
;;
--account-label)
_wallet_complete_account_label "$cur"
;;
*)
COMPREPLY=($(compgen -W "--account-id --account-label" -- "$cur"))
;;
esac
;;
send)
case "$prev" in
--from)
_wallet_complete_account_id "$cur"
;;
--from-label)
_wallet_complete_account_label "$cur"
;;
--to)
_wallet_complete_account_id "$cur"
;;
--to-label)
_wallet_complete_account_label "$cur"
;;
--to-npk | --to-vpk | --to-identifier | --amount)
;; # no specific completion
*)
COMPREPLY=($(compgen -W "--from --from-label --to --to-label --to-npk --to-vpk --to-identifier --amount" -- "$cur"))
;;
esac
;;
esac
;;
chain-info)
case "$subcmd" in
"")
COMPREPLY=($(compgen -W "current-block-id block transaction help" -- "$cur"))
;;
block)
case "$prev" in
-i | --id)
;; # no specific completion for block ID
*)
COMPREPLY=($(compgen -W "-i --id" -- "$cur"))
;;
esac
;;
transaction)
case "$prev" in
-t | --hash)
;; # no specific completion for tx hash
*)
COMPREPLY=($(compgen -W "-t --hash" -- "$cur"))
;;
esac
;;
esac
;;
account)
case "$subcmd" in
"")
COMPREPLY=($(compgen -W "get new sync-private list ls label help" -- "$cur"))
;;
get)
case "$prev" in
-a | --account-id)
_wallet_complete_account_id "$cur"
;;
--account-label)
_wallet_complete_account_label "$cur"
;;
*)
COMPREPLY=($(compgen -W "-r --raw -k --keys -a --account-id --account-label" -- "$cur"))
;;
esac
;;
list | ls)
COMPREPLY=($(compgen -W "-l --long" -- "$cur"))
;;
sync-private)
;; # no options
new)
# `account new` is itself a subcommand: public | private-accounts-key
local new_subcmd=""
for ((i = subcmd_idx + 1; i < cword; i++)); do
case "${words[$i]}" in
public | private-accounts-key)
new_subcmd="${words[$i]}"
break
;;
esac
done
if [[ -z "$new_subcmd" ]]; then
COMPREPLY=($(compgen -W "public private-accounts-key" -- "$cur"))
else
case "$new_subcmd" in
public)
case "$prev" in
--cci | -l | --label)
;; # no specific completion
*)
COMPREPLY=($(compgen -W "--cci -l --label" -- "$cur"))
;;
esac
;;
private-accounts-key)
case "$prev" in
--cci)
;; # no specific completion
*)
COMPREPLY=($(compgen -W "--cci" -- "$cur"))
;;
esac
;;
esac
fi
;;
label)
case "$prev" in
-a | --account-id)
_wallet_complete_account_id "$cur"
;;
--account-label)
_wallet_complete_account_label "$cur"
;;
-l | --label)
;; # no specific completion for label value
*)
COMPREPLY=($(compgen -W "-a --account-id --account-label -l --label" -- "$cur"))
;;
esac
;;
esac
;;
pinata)
case "$subcmd" in
"")
COMPREPLY=($(compgen -W "claim help" -- "$cur"))
;;
claim)
case "$prev" in
--to)
_wallet_complete_account_id "$cur"
;;
--to-label)
_wallet_complete_account_label "$cur"
;;
*)
COMPREPLY=($(compgen -W "--to --to-label" -- "$cur"))
;;
esac
;;
esac
;;
token)
case "$subcmd" in
"")
COMPREPLY=($(compgen -W "new send burn mint help" -- "$cur"))
;;
new)
case "$prev" in
--definition-account-id)
_wallet_complete_account_id "$cur"
;;
--definition-account-label)
_wallet_complete_account_label "$cur"
;;
--supply-account-id)
_wallet_complete_account_id "$cur"
;;
--supply-account-label)
_wallet_complete_account_label "$cur"
;;
-n | --name | -t | --total-supply)
;; # no specific completion
*)
COMPREPLY=($(compgen -W "--definition-account-id --definition-account-label --supply-account-id --supply-account-label -n --name -t --total-supply" -- "$cur"))
;;
esac
;;
send)
case "$prev" in
--from)
_wallet_complete_account_id "$cur"
;;
--from-label)
_wallet_complete_account_label "$cur"
;;
--to)
_wallet_complete_account_id "$cur"
;;
--to-label)
_wallet_complete_account_label "$cur"
;;
--to-npk | --to-vpk | --to-identifier | --amount)
;; # no specific completion
*)
COMPREPLY=($(compgen -W "--from --from-label --to --to-label --to-npk --to-vpk --to-identifier --amount" -- "$cur"))
;;
esac
;;
burn)
case "$prev" in
--definition)
_wallet_complete_account_id "$cur"
;;
--definition-label)
_wallet_complete_account_label "$cur"
;;
--holder)
_wallet_complete_account_id "$cur"
;;
--holder-label)
_wallet_complete_account_label "$cur"
;;
--amount)
;; # no specific completion
*)
COMPREPLY=($(compgen -W "--definition --definition-label --holder --holder-label --amount" -- "$cur"))
;;
esac
;;
mint)
case "$prev" in
--definition)
_wallet_complete_account_id "$cur"
;;
--definition-label)
_wallet_complete_account_label "$cur"
;;
--holder)
_wallet_complete_account_id "$cur"
;;
--holder-label)
_wallet_complete_account_label "$cur"
;;
--holder-npk | --holder-vpk | --holder-identifier | --amount)
;; # no specific completion
*)
COMPREPLY=($(compgen -W "--definition --definition-label --holder --holder-label --holder-npk --holder-vpk --holder-identifier --amount" -- "$cur"))
;;
esac
;;
esac
;;
amm)
case "$subcmd" in
"")
COMPREPLY=($(compgen -W "new swap-exact-input swap-exact-output add-liquidity remove-liquidity help" -- "$cur"))
;;
new)
case "$prev" in
--user-holding-a)
_wallet_complete_account_id "$cur"
;;
--user-holding-a-label)
_wallet_complete_account_label "$cur"
;;
--user-holding-b)
_wallet_complete_account_id "$cur"
;;
--user-holding-b-label)
_wallet_complete_account_label "$cur"
;;
--user-holding-lp)
_wallet_complete_account_id "$cur"
;;
--user-holding-lp-label)
_wallet_complete_account_label "$cur"
;;
--balance-a | --balance-b)
;; # no specific completion
*)
COMPREPLY=($(compgen -W "--user-holding-a --user-holding-a-label --user-holding-b --user-holding-b-label --user-holding-lp --user-holding-lp-label --balance-a --balance-b" -- "$cur"))
;;
esac
;;
swap-exact-input)
case "$prev" in
--user-holding-a)
_wallet_complete_account_id "$cur"
;;
--user-holding-a-label)
_wallet_complete_account_label "$cur"
;;
--user-holding-b)
_wallet_complete_account_id "$cur"
;;
--user-holding-b-label)
_wallet_complete_account_label "$cur"
;;
--amount-in | --min-amount-out | --token-definition)
;; # no specific completion
*)
COMPREPLY=($(compgen -W "--user-holding-a --user-holding-a-label --user-holding-b --user-holding-b-label --amount-in --min-amount-out --token-definition" -- "$cur"))
;;
esac
;;
swap-exact-output)
case "$prev" in
--user-holding-a | --user-holding-b | --exact-amount-out | --max-amount-in | --token-definition)
;; # no specific completion
*)
COMPREPLY=($(compgen -W "--user-holding-a --user-holding-b --exact-amount-out --max-amount-in --token-definition" -- "$cur"))
;;
esac
;;
add-liquidity)
case "$prev" in
--user-holding-a)
_wallet_complete_account_id "$cur"
;;
--user-holding-a-label)
_wallet_complete_account_label "$cur"
;;
--user-holding-b)
_wallet_complete_account_id "$cur"
;;
--user-holding-b-label)
_wallet_complete_account_label "$cur"
;;
--user-holding-lp)
_wallet_complete_account_id "$cur"
;;
--user-holding-lp-label)
_wallet_complete_account_label "$cur"
;;
--max-amount-a | --max-amount-b | --min-amount-lp)
;; # no specific completion
*)
COMPREPLY=($(compgen -W "--user-holding-a --user-holding-a-label --user-holding-b --user-holding-b-label --user-holding-lp --user-holding-lp-label --max-amount-a --max-amount-b --min-amount-lp" -- "$cur"))
;;
esac
;;
remove-liquidity)
case "$prev" in
--user-holding-a)
_wallet_complete_account_id "$cur"
;;
--user-holding-a-label)
_wallet_complete_account_label "$cur"
;;
--user-holding-b)
_wallet_complete_account_id "$cur"
;;
--user-holding-b-label)
_wallet_complete_account_label "$cur"
;;
--user-holding-lp)
_wallet_complete_account_id "$cur"
;;
--user-holding-lp-label)
_wallet_complete_account_label "$cur"
;;
--balance-lp | --min-amount-a | --min-amount-b)
;; # no specific completion
*)
COMPREPLY=($(compgen -W "--user-holding-a --user-holding-a-label --user-holding-b --user-holding-b-label --user-holding-lp --user-holding-lp-label --balance-lp --min-amount-a --min-amount-b" -- "$cur"))
;;
esac
;;
esac
;;
ata)
case "$subcmd" in
"")
COMPREPLY=($(compgen -W "address create send burn list help" -- "$cur"))
;;
address)
case "$prev" in
--owner | --token-definition)
;; # no specific completion
*)
COMPREPLY=($(compgen -W "--owner --token-definition" -- "$cur"))
;;
esac
;;
create)
case "$prev" in
--owner)
_wallet_complete_account_id "$cur"
;;
--token-definition)
;; # no specific completion
*)
COMPREPLY=($(compgen -W "--owner --token-definition" -- "$cur"))
;;
esac
;;
send)
case "$prev" in
--from)
_wallet_complete_account_id "$cur"
;;
--to | --token-definition | --amount)
;; # no specific completion
*)
COMPREPLY=($(compgen -W "--from --token-definition --to --amount" -- "$cur"))
;;
esac
;;
burn)
case "$prev" in
--holder)
_wallet_complete_account_id "$cur"
;;
--token-definition | --amount)
;; # no specific completion
*)
COMPREPLY=($(compgen -W "--holder --token-definition --amount" -- "$cur"))
;;
esac
;;
list)
case "$prev" in
--owner | --token-definition)
;; # no specific completion
*)
COMPREPLY=($(compgen -W "--owner --token-definition" -- "$cur"))
;;
esac
;;
esac
;;
config)
case "$subcmd" in
"")
COMPREPLY=($(compgen -W "get set description help" -- "$cur"))
;;
get)
# Accepts optional -a/--all flag and an optional positional key
COMPREPLY=($(compgen -W "--all -a $config_keys" -- "$cur"))
;;
set)
# set <key> <value> — only complete the key; no completion for the value
local set_args=0
for ((i = subcmd_idx + 1; i < cword; i++)); do
[[ "${words[$i]}" != -* ]] && ((set_args++))
done
if [[ $set_args -eq 0 ]]; then
COMPREPLY=($(compgen -W "$config_keys" -- "$cur"))
fi
;;
description)
# description <key> — only complete if no key provided yet
local has_key=false
for ((i = subcmd_idx + 1; i < cword; i++)); do
[[ "${words[$i]}" != -* ]] && has_key=true && break
done
if ! $has_key; then
COMPREPLY=($(compgen -W "$config_keys" -- "$cur"))
fi
;;
esac
;;
restore-keys)
case "$prev" in
-d | --depth)
;; # no specific completion for depth value
*)
COMPREPLY=($(compgen -W "-d --depth" -- "$cur"))
;;
esac
;;
deploy-program)
COMPREPLY=($(compgen -f -- "$cur"))
compopt -o filenames 2>/dev/null
;;
help)
COMPREPLY=($(compgen -W "$commands" -- "$cur"))
;;
esac
}
complete -F _wallet wallet

View File

@ -24,6 +24,7 @@ _wallet() {
'pinata:Pinata program interaction subcommand' 'pinata:Pinata program interaction subcommand'
'token:Token program interaction subcommand' 'token:Token program interaction subcommand'
'amm:AMM program interaction subcommand' 'amm:AMM program interaction subcommand'
'ata:Associated Token Account program interaction subcommand'
'check-health:Check the wallet can connect to the node and builtin local programs match the remote versions' 'check-health:Check the wallet can connect to the node and builtin local programs match the remote versions'
'config:Command to setup config, get and set config fields' 'config:Command to setup config, get and set config fields'
'restore-keys:Restoring keys from given password at given depth' 'restore-keys:Restoring keys from given password at given depth'
@ -52,6 +53,9 @@ _wallet() {
amm) amm)
_wallet_amm _wallet_amm
;; ;;
ata)
_wallet_ata
;;
config) config)
_wallet_config _wallet_config
;; ;;
@ -90,14 +94,18 @@ _wallet_auth_transfer() {
case $line[1] in case $line[1] in
init) init)
_arguments \ _arguments \
'--account-id[Account ID to initialize]:account_id:_wallet_account_ids' '--account-id[Account ID to initialize]:account_id:_wallet_account_ids' \
'--account-label[Account label (alternative to --account-id)]:label:'
;; ;;
send) send)
_arguments \ _arguments \
'--from[Source account ID]:from_account:_wallet_account_ids' \ '--from[Source account ID]:from_account:_wallet_account_ids' \
'--from-label[From account label (alternative to --from)]:label:' \
'--to[Destination account ID (for owned accounts)]:to_account:_wallet_account_ids' \ '--to[Destination account ID (for owned accounts)]:to_account:_wallet_account_ids' \
'--to-label[To account label (alternative to --to)]:label:' \
'--to-npk[Destination nullifier public key (for foreign private accounts)]:npk:' \ '--to-npk[Destination nullifier public key (for foreign private accounts)]:npk:' \
'--to-vpk[Destination viewing public key (for foreign private accounts)]:vpk:' \ '--to-vpk[Destination viewing public key (for foreign private accounts)]:vpk:' \
'--to-identifier[Identifier for the recipient private account]:identifier:' \
'--amount[Amount of native tokens to send]:amount:' '--amount[Amount of native tokens to send]:amount:'
;; ;;
esac esac
@ -165,7 +173,8 @@ _wallet_account() {
_arguments \ _arguments \
'(-r --raw)'{-r,--raw}'[Get raw account data]' \ '(-r --raw)'{-r,--raw}'[Get raw account data]' \
'(-k --keys)'{-k,--keys}'[Display keys (pk for public accounts, npk/vpk for private accounts)]' \ '(-k --keys)'{-k,--keys}'[Display keys (pk for public accounts, npk/vpk for private accounts)]' \
'(-a --account-id)'{-a,--account-id}'[Account ID to query]:account_id:_wallet_account_ids' '(-a --account-id)'{-a,--account-id}'[Account ID to query]:account_id:_wallet_account_ids' \
'--account-label[Account label (alternative to --account-id)]:label:'
;; ;;
list|ls) list|ls)
_arguments \ _arguments \
@ -177,17 +186,27 @@ _wallet_account() {
'*:: :->new_args' '*:: :->new_args'
case $state in case $state in
account_type) account_type)
compadd public private compadd public private-accounts-key
;; ;;
new_args) new_args)
_arguments \ case $line[1] in
'--cci[Chain index of a parent node]:chain_index:' public)
_arguments \
'--cci[Chain index of a parent node]:chain_index:' \
'(-l --label)'{-l,--label}'[Label to assign to the new account]:label:'
;;
private-accounts-key)
_arguments \
'--cci[Chain index of a parent node]:chain_index:'
;;
esac
;; ;;
esac esac
;; ;;
label) label)
_arguments \ _arguments \
'(-a --account-id)'{-a,--account-id}'[Account ID to label]:account_id:_wallet_account_ids' \ '(-a --account-id)'{-a,--account-id}'[Account ID to label]:account_id:_wallet_account_ids' \
'--account-label[Account label (alternative to --account-id)]:label:' \
'(-l --label)'{-l,--label}'[The label to assign to the account]:label:' '(-l --label)'{-l,--label}'[The label to assign to the account]:label:'
;; ;;
esac esac
@ -215,7 +234,8 @@ _wallet_pinata() {
case $line[1] in case $line[1] in
claim) claim)
_arguments \ _arguments \
'--to[Destination account ID to receive claimed tokens]:to_account:_wallet_account_ids' '--to[Destination account ID to receive claimed tokens]:to_account:_wallet_account_ids' \
'--to-label[To account label (alternative to --to)]:label:'
;; ;;
esac esac
;; ;;
@ -248,28 +268,38 @@ _wallet_token() {
'--name[Token name]:name:' \ '--name[Token name]:name:' \
'--total-supply[Total supply of tokens to mint]:total_supply:' \ '--total-supply[Total supply of tokens to mint]:total_supply:' \
'--definition-account-id[Account ID for token definition]:definition_account:_wallet_account_ids' \ '--definition-account-id[Account ID for token definition]:definition_account:_wallet_account_ids' \
'--supply-account-id[Account ID to receive initial supply]:supply_account:_wallet_account_ids' '--definition-account-label[Definition account label (alternative to --definition-account-id)]:label:' \
'--supply-account-id[Account ID to receive initial supply]:supply_account:_wallet_account_ids' \
'--supply-account-label[Supply account label (alternative to --supply-account-id)]:label:'
;; ;;
send) send)
_arguments \ _arguments \
'--from[Source holding account ID]:from_account:_wallet_account_ids' \ '--from[Source holding account ID]:from_account:_wallet_account_ids' \
'--from-label[From account label (alternative to --from)]:label:' \
'--to[Destination holding account ID (for owned accounts)]:to_account:_wallet_account_ids' \ '--to[Destination holding account ID (for owned accounts)]:to_account:_wallet_account_ids' \
'--to-label[To account label (alternative to --to)]:label:' \
'--to-npk[Destination nullifier public key (for foreign private accounts)]:npk:' \ '--to-npk[Destination nullifier public key (for foreign private accounts)]:npk:' \
'--to-vpk[Destination viewing public key (for foreign private accounts)]:vpk:' \ '--to-vpk[Destination viewing public key (for foreign private accounts)]:vpk:' \
'--to-identifier[Identifier for the recipient private account]:identifier:' \
'--amount[Amount of tokens to send]:amount:' '--amount[Amount of tokens to send]:amount:'
;; ;;
burn) burn)
_arguments \ _arguments \
'--definition[Definition account ID]:definition_account:_wallet_account_ids' \ '--definition[Definition account ID]:definition_account:_wallet_account_ids' \
'--definition-label[Definition account label (alternative to --definition)]:label:' \
'--holder[Holder account ID]:holder_account:_wallet_account_ids' \ '--holder[Holder account ID]:holder_account:_wallet_account_ids' \
'--holder-label[Holder account label (alternative to --holder)]:label:' \
'--amount[Amount of tokens to burn]:amount:' '--amount[Amount of tokens to burn]:amount:'
;; ;;
mint) mint)
_arguments \ _arguments \
'--definition[Definition account ID]:definition_account:_wallet_account_ids' \ '--definition[Definition account ID]:definition_account:_wallet_account_ids' \
'--definition-label[Definition account label (alternative to --definition)]:label:' \
'--holder[Holder account ID (for owned accounts)]:holder_account:_wallet_account_ids' \ '--holder[Holder account ID (for owned accounts)]:holder_account:_wallet_account_ids' \
'--holder-label[Holder account label (alternative to --holder)]:label:' \
'--holder-npk[Holder nullifier public key (for foreign private accounts)]:npk:' \ '--holder-npk[Holder nullifier public key (for foreign private accounts)]:npk:' \
'--holder-vpk[Holder viewing public key (for foreign private accounts)]:vpk:' \ '--holder-vpk[Holder viewing public key (for foreign private accounts)]:vpk:' \
'--holder-identifier[Identifier for the holder private account]:identifier:' \
'--amount[Amount of tokens to mint]:amount:' '--amount[Amount of tokens to mint]:amount:'
;; ;;
esac esac
@ -289,7 +319,8 @@ _wallet_amm() {
subcommand) subcommand)
subcommands=( subcommands=(
'new:Create a new liquidity pool' 'new:Create a new liquidity pool'
'swap:Swap tokens using the AMM' 'swap-exact-input:Swap specifying exact input amount'
'swap-exact-output:Swap specifying exact output amount'
'add-liquidity:Add liquidity to an existing pool' 'add-liquidity:Add liquidity to an existing pool'
'remove-liquidity:Remove liquidity from a pool' 'remove-liquidity:Remove liquidity from a pool'
'help:Print this message or the help of the given subcommand(s)' 'help:Print this message or the help of the given subcommand(s)'
@ -301,24 +332,40 @@ _wallet_amm() {
new) new)
_arguments \ _arguments \
'--user-holding-a[User token A holding account ID]:holding_a:_wallet_account_ids' \ '--user-holding-a[User token A holding account ID]:holding_a:_wallet_account_ids' \
'--user-holding-a-label[User holding A label (alternative to --user-holding-a)]:label:' \
'--user-holding-b[User token B holding account ID]:holding_b:_wallet_account_ids' \ '--user-holding-b[User token B holding account ID]:holding_b:_wallet_account_ids' \
'--user-holding-b-label[User holding B label (alternative to --user-holding-b)]:label:' \
'--user-holding-lp[User LP token holding account ID]:holding_lp:_wallet_account_ids' \ '--user-holding-lp[User LP token holding account ID]:holding_lp:_wallet_account_ids' \
'--user-holding-lp-label[User holding LP label (alternative to --user-holding-lp)]:label:' \
'--balance-a[Amount of token A to deposit]:balance_a:' \ '--balance-a[Amount of token A to deposit]:balance_a:' \
'--balance-b[Amount of token B to deposit]:balance_b:' '--balance-b[Amount of token B to deposit]:balance_b:'
;; ;;
swap) swap-exact-input)
_arguments \ _arguments \
'--user-holding-a[User token A holding account ID]:holding_a:_wallet_account_ids' \ '--user-holding-a[User token A holding account ID]:holding_a:_wallet_account_ids' \
'--user-holding-a-label[User holding A label (alternative to --user-holding-a)]:label:' \
'--user-holding-b[User token B holding account ID]:holding_b:_wallet_account_ids' \ '--user-holding-b[User token B holding account ID]:holding_b:_wallet_account_ids' \
'--user-holding-b-label[User holding B label (alternative to --user-holding-b)]:label:' \
'--amount-in[Amount of tokens to swap]:amount_in:' \ '--amount-in[Amount of tokens to swap]:amount_in:' \
'--min-amount-out[Minimum tokens expected in return]:min_amount_out:' \ '--min-amount-out[Minimum tokens expected in return]:min_amount_out:' \
'--token-definition[Definition ID of the token being provided]:token_def:' '--token-definition[Definition ID of the token being provided]:token_def:'
;; ;;
swap-exact-output)
_arguments \
'--user-holding-a[User token A holding account ID]:holding_a:' \
'--user-holding-b[User token B holding account ID]:holding_b:' \
'--exact-amount-out[Exact amount of tokens expected out]:exact_amount_out:' \
'--max-amount-in[Maximum tokens to spend]:max_amount_in:' \
'--token-definition[Definition ID of the token being provided]:token_def:'
;;
add-liquidity) add-liquidity)
_arguments \ _arguments \
'--user-holding-a[User token A holding account ID]:holding_a:_wallet_account_ids' \ '--user-holding-a[User token A holding account ID]:holding_a:_wallet_account_ids' \
'--user-holding-a-label[User holding A label (alternative to --user-holding-a)]:label:' \
'--user-holding-b[User token B holding account ID]:holding_b:_wallet_account_ids' \ '--user-holding-b[User token B holding account ID]:holding_b:_wallet_account_ids' \
'--user-holding-b-label[User holding B label (alternative to --user-holding-b)]:label:' \
'--user-holding-lp[User LP token holding account ID]:holding_lp:_wallet_account_ids' \ '--user-holding-lp[User LP token holding account ID]:holding_lp:_wallet_account_ids' \
'--user-holding-lp-label[User holding LP label (alternative to --user-holding-lp)]:label:' \
'--max-amount-a[Maximum amount of token A to deposit]:max_amount_a:' \ '--max-amount-a[Maximum amount of token A to deposit]:max_amount_a:' \
'--max-amount-b[Maximum amount of token B to deposit]:max_amount_b:' \ '--max-amount-b[Maximum amount of token B to deposit]:max_amount_b:' \
'--min-amount-lp[Minimum LP tokens to receive]:min_amount_lp:' '--min-amount-lp[Minimum LP tokens to receive]:min_amount_lp:'
@ -326,8 +373,11 @@ _wallet_amm() {
remove-liquidity) remove-liquidity)
_arguments \ _arguments \
'--user-holding-a[User token A holding account ID]:holding_a:_wallet_account_ids' \ '--user-holding-a[User token A holding account ID]:holding_a:_wallet_account_ids' \
'--user-holding-a-label[User holding A label (alternative to --user-holding-a)]:label:' \
'--user-holding-b[User token B holding account ID]:holding_b:_wallet_account_ids' \ '--user-holding-b[User token B holding account ID]:holding_b:_wallet_account_ids' \
'--user-holding-b-label[User holding B label (alternative to --user-holding-b)]:label:' \
'--user-holding-lp[User LP token holding account ID]:holding_lp:_wallet_account_ids' \ '--user-holding-lp[User LP token holding account ID]:holding_lp:_wallet_account_ids' \
'--user-holding-lp-label[User holding LP label (alternative to --user-holding-lp)]:label:' \
'--balance-lp[Amount of LP tokens to burn]:balance_lp:' \ '--balance-lp[Amount of LP tokens to burn]:balance_lp:' \
'--min-amount-a[Minimum token A to receive]:min_amount_a:' \ '--min-amount-a[Minimum token A to receive]:min_amount_a:' \
'--min-amount-b[Minimum token B to receive]:min_amount_b:' '--min-amount-b[Minimum token B to receive]:min_amount_b:'
@ -337,13 +387,67 @@ _wallet_amm() {
esac esac
} }
# ata subcommand
_wallet_ata() {
local -a subcommands
_arguments -C \
'1: :->subcommand' \
'*:: :->args'
case $state in
subcommand)
subcommands=(
'address:Derive and print the Associated Token Account address (local only)'
'create:Create (or idempotently no-op) the Associated Token Account'
'send:Send tokens from owner ATA to a recipient token holding account'
'burn:Burn tokens from holder ATA'
'list:List all ATAs for a given owner across multiple token definitions'
'help:Print this message or the help of the given subcommand(s)'
)
_describe -t subcommands 'ata subcommands' subcommands
;;
args)
case $line[1] in
address)
_arguments \
'--owner[Owner account (no privacy prefix)]:owner:' \
'--token-definition[Token definition account (no privacy prefix)]:token_def:'
;;
create)
_arguments \
'--owner[Owner account with privacy prefix]:owner:_wallet_account_ids' \
'--token-definition[Token definition account (no privacy prefix)]:token_def:'
;;
send)
_arguments \
'--from[Sender account with privacy prefix]:from:_wallet_account_ids' \
'--token-definition[Token definition account (no privacy prefix)]:token_def:' \
'--to[Recipient account (no privacy prefix)]:to:' \
'--amount[Amount of tokens to send]:amount:'
;;
burn)
_arguments \
'--holder[Holder account with privacy prefix]:holder:_wallet_account_ids' \
'--token-definition[Token definition account (no privacy prefix)]:token_def:' \
'--amount[Amount of tokens to burn]:amount:'
;;
list)
_arguments \
'--owner[Owner account (no privacy prefix)]:owner:' \
'--token-definition[Token definition accounts (no privacy prefix)]:token_def:'
;;
esac
;;
esac
}
# config subcommand # config subcommand
_wallet_config() { _wallet_config() {
local -a subcommands local -a subcommands
local -a config_keys local -a config_keys
config_keys=( config_keys=(
'all'
'override_rust_log' 'override_rust_log'
'sequencer_addr' 'sequencer_addr'
'seq_poll_timeout' 'seq_poll_timeout'
@ -370,7 +474,12 @@ _wallet_config() {
;; ;;
args) args)
case $line[1] in case $line[1] in
get|description) get)
_arguments \
'(-a --all)'{-a,--all}'[Print all config fields]' \
'::key:compadd -a config_keys'
;;
description)
compadd -a config_keys compadd -a config_keys
;; ;;
set) set)
@ -405,6 +514,7 @@ _wallet_help() {
'pinata:Pinata program interaction subcommand' 'pinata:Pinata program interaction subcommand'
'token:Token program interaction subcommand' 'token:Token program interaction subcommand'
'amm:AMM program interaction subcommand' 'amm:AMM program interaction subcommand'
'ata:Associated Token Account program interaction subcommand'
'check-health:Check the wallet can connect to the node' 'check-health:Check the wallet can connect to the node'
'config:Command to setup config, get and set config fields' 'config:Command to setup config, get and set config fields'
'restore-keys:Restoring keys from given password at given depth' 'restore-keys:Restoring keys from given password at given depth'

View File

@ -1,11 +0,0 @@
{
"resubscribe_interval": "1s",
"bedrock_client_config": {
"addr": "http://logos-blockchain-node-0:18080",
"backoff": {
"start_delay": "100ms",
"max_retries": 5
}
},
"channel_id": "0101010101010101010101010101010101010101010101010101010101010101"
}

View File

@ -0,0 +1,8 @@
{
"home": "./indexer/service",
"consensus_info_polling_interval": "1s",
"bedrock_config": {
"addr": "http://logos-blockchain-node-0:18080"
},
"channel_id": "0101010101010101010101010101010101010101010101010101010101010101"
}

View File

@ -1,103 +0,0 @@
{
"home": "/var/lib/sequencer_runner",
"override_rust_log": null,
"genesis_id": 1,
"is_genesis_random": true,
"max_num_tx_in_block": 20,
"max_block_size": "1 MiB",
"mempool_max_size": 10000,
"block_create_timeout": "10s",
"retry_pending_blocks_timeout": "7s",
"port": 3040,
"bedrock_config": {
"backoff": {
"start_delay": "100ms",
"max_retries": 5
},
"channel_id": "0101010101010101010101010101010101010101010101010101010101010101",
"node_url": "http://logos-blockchain-node-0:18080"
},
"indexer_rpc_url": "ws://indexer_service:8779",
"initial_accounts": [
{
"account_id": "BLgCRDXYdQPMMWVHYRFGQZbgeHx9frkipa8GtpG2Syqy",
"balance": 10000
},
{
"account_id": "Gj1mJy5W7J5pfmLRujmQaLfLMWidNxQ6uwnhb666ZwHw",
"balance": 20000
}
],
"initial_commitments": [
{
"npk": [13, 25, 40, 5, 198, 248, 210, 248, 237, 121, 124, 145, 186, 142, 253, 216, 236, 69, 193, 32, 166, 167, 49, 133, 172, 111, 159, 46, 84, 17, 157, 23],
"account": {
"program_owner": [
0,
0,
0,
0,
0,
0,
0,
0
],
"balance": 10000,
"data": [],
"nonce": 0
}
},
{
"npk": [32, 67, 72, 164, 106, 53, 66, 239, 141, 15, 52, 230, 136, 177, 2, 236, 207, 243, 134, 135, 210, 143, 87, 232, 215, 128, 194, 120, 113, 224, 4, 165],
"account": {
"program_owner": [
0,
0,
0,
0,
0,
0,
0,
0
],
"balance": 20000,
"data": [],
"nonce": 0
}
}
],
"signing_key": [
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37
]
}

View File

@ -0,0 +1,77 @@
{
"home": "/var/lib/sequencer_service",
"max_num_tx_in_block": 20,
"max_block_size": "1 MiB",
"mempool_max_size": 10000,
"block_create_timeout": "10s",
"retry_pending_blocks_timeout": "7s",
"bedrock_config": {
"backoff": {
"start_delay": "100ms",
"max_retries": 5
},
"channel_id": "0101010101010101010101010101010101010101010101010101010101010101",
"node_url": "http://logos-blockchain-node-0:18080"
},
"indexer_rpc_url": "ws://indexer_service:8779",
"genesis": [
{
"supply_account": {
"account_id": "6iArKUXxhUJqS7kCaPNhwMWt3ro71PDyBj7jwAyE2VQV",
"balance": 10000
}
},
{
"supply_account": {
"account_id": "7wHg9sbJwc6h3NP1S9bekfAzB8CHifEcxKswCKUt3YQo",
"balance": 20000
}
},
{
"supply_account": {
"account_id": "61EsoYN6gvTLkveh1YSTMG3yJkncpHy5EGmxhSK4ew29",
"balance": 10000
}
},
{
"supply_account": {
"account_id": "3m6HQmCgmAvsxZtxAHPqqEqoBG4335fCG8TzxigyW7rE",
"balance": 20000
}
}
],
"signing_key": [
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37
]
}

View File

@ -7,18 +7,18 @@ services:
environment: environment:
- RUST_LOG=error - RUST_LOG=error
sequencer_runner: sequencer_service:
depends_on: depends_on:
- logos-blockchain-node-0 - logos-blockchain-node-0
- indexer_service - indexer_service
volumes: !override volumes:
- ./configs/docker-all-in-one/sequencer:/etc/sequencer_runner - ./configs/docker-all-in-one/sequencer_config.json:/etc/sequencer_service/sequencer_config.json
indexer_service: indexer_service:
depends_on: depends_on:
- logos-blockchain-node-0 - logos-blockchain-node-0
volumes: 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: explorer_service:
depends_on: depends_on:

View File

@ -6,7 +6,7 @@ include:
- path: - path:
bedrock/docker-compose.yml bedrock/docker-compose.yml
- path: - path:
sequencer_runner/docker-compose.yml sequencer/service/docker-compose.yml
- path: - path:
indexer/service/docker-compose.yml indexer/service/docker-compose.yml
- path: - path:

View File

@ -0,0 +1,369 @@
# Associated Token Accounts (ATAs)
This tutorial covers Associated Token Accounts (ATAs). An ATA lets you derive a unique token holding address from an owner account and a token definition — no need to create and track holding accounts manually. Given the same inputs, anyone can compute the same ATA address without a network call. By the end, you will have practiced:
1. Deriving ATA addresses locally.
2. Creating an ATA.
3. Sending tokens via ATAs.
4. Burning tokens from an ATA.
5. Listing ATAs across multiple token definitions.
6. Creating an ATA with a private owner.
7. Sending tokens from a private owner's ATA.
8. Burning tokens from a private owner's ATA.
> [!Important]
> This tutorial assumes you have completed the [wallet-setup](wallet-setup.md) and [custom-tokens](custom-tokens.md) tutorials. You need a running wallet with accounts and at least one token definition.
## Prerequisites
### Deploy the ATA program
Unlike the Token program (which is built-in), the ATA program must be deployed before you can use it. The pre-built binary is included in the repository:
```bash
wallet deploy-program artifacts/program_methods/associated_token_account.bin
```
> [!Note]
> Program deployment is idempotent — if the ATA program has already been deployed (e.g. by another user on the same network), the command is a no-op.
You can verify the deployment succeeded by running any `wallet ata` command. If the program is not deployed, commands that submit transactions will fail.
The CLI provides commands to work with the ATA program. Run `wallet ata` to see the options:
```bash
Commands:
address Derive and print the Associated Token Account address (local only, no network)
create Create (or idempotently no-op) the Associated Token Account
send Send tokens from owner's ATA to a recipient
burn Burn tokens from holder's ATA
list List all ATAs for a given owner across multiple token definitions
help Print this message or the help of the given subcommand(s)
```
## 1. How ATA addresses work
An ATA address is deterministically derived from two inputs:
1. The **owner** account ID.
2. The **token definition** account ID.
The derivation works as follows:
```
seed = SHA256(owner_id || definition_id)
ata_address = AccountId::for_public_pda(ata_program_id, seed)
```
Because the computation is pure, anyone who knows the owner and definition can reproduce the exact same ATA address — no network call required.
> [!Note]
> All ATA commands that submit transactions accept a privacy prefix on the owner/holder argument — `Public/` for public accounts and `Private/` for private accounts. Using `Private/` generates a zero-knowledge proof locally and submits only the proof to the sequencer, keeping the owner's identity off-chain.
## 2. Deriving an ATA address (`wallet ata address`)
The `address` subcommand computes the ATA address locally without submitting a transaction.
### a. Set up an owner and token definition
If you already have a public account and a token definition from the custom-tokens tutorial, you can reuse them. Otherwise, create them now:
```bash
wallet account new public
# Output:
Generated new account with account_id Public/5FkBei8HYoSUNqh9rWCrJDnSZE5FJfGiWmTvhgBx3qTB
```
```bash
wallet account new public
# Output:
Generated new account with account_id Public/3YpK8RvVzWm6Q4h2nDAbxJfLmuRqkEkFP9C7UwTdGvE4
```
```bash
wallet token new \
--name MYTOKEN \
--total-supply 10000 \
--definition-account-id Public/3YpK8RvVzWm6Q4h2nDAbxJfLmuRqkEkFP9C7UwTdGvE4 \
--supply-account-id Public/5FkBei8HYoSUNqh9rWCrJDnSZE5FJfGiWmTvhgBx3qTB
```
### b. Derive the ATA address
```bash
wallet ata address \
--owner 5FkBei8HYoSUNqh9rWCrJDnSZE5FJfGiWmTvhgBx3qTB \
--token-definition 3YpK8RvVzWm6Q4h2nDAbxJfLmuRqkEkFP9C7UwTdGvE4
# Output:
7a2Bf9cKLm3XpRtH1wDqZs8vYjN4eU6gAoFxW5kMnE2R
```
> [!Note]
> This is a pure computation — no transaction is submitted and no network connection is needed. The same inputs will always produce the same output.
## 3. Creating an ATA (`wallet ata create`)
Before an ATA can hold tokens it must be created on-chain. The `create` subcommand submits a transaction that initializes the ATA. If it already exists, the operation is a no-op.
### a. Create the ATA
```bash
wallet ata create \
--owner Public/5FkBei8HYoSUNqh9rWCrJDnSZE5FJfGiWmTvhgBx3qTB \
--token-definition 3YpK8RvVzWm6Q4h2nDAbxJfLmuRqkEkFP9C7UwTdGvE4
```
### b. Inspect the ATA
Use the ATA address derived in the previous section:
```bash
wallet account get --account-id Public/7a2Bf9cKLm3XpRtH1wDqZs8vYjN4eU6gAoFxW5kMnE2R
# Output:
Holding account owned by ata program
{"account_type":"Token holding","definition_id":"3YpK8RvVzWm6Q4h2nDAbxJfLmuRqkEkFP9C7UwTdGvE4","balance":0}
```
> [!Tip]
> Creation is idempotent — running the same command again is a no-op.
## 4. Sending tokens via ATA (`wallet ata send`)
The `send` subcommand transfers tokens from the owner's ATA to a recipient account.
### a. Fund the ATA
First, move tokens into the ATA from the supply account created earlier:
```bash
wallet token send \
--from Public/5FkBei8HYoSUNqh9rWCrJDnSZE5FJfGiWmTvhgBx3qTB \
--to Public/7a2Bf9cKLm3XpRtH1wDqZs8vYjN4eU6gAoFxW5kMnE2R \
--amount 5000
```
### b. Create a recipient account
```bash
wallet account new public
# Output:
Generated new account with account_id Public/9Ht4Kv8pYmW2rXjN6dFcQsA7bEoLf3gUZx1wDnR5eTi
```
### c. Send tokens from the ATA to the recipient
```bash
wallet ata send \
--from Public/5FkBei8HYoSUNqh9rWCrJDnSZE5FJfGiWmTvhgBx3qTB \
--token-definition 3YpK8RvVzWm6Q4h2nDAbxJfLmuRqkEkFP9C7UwTdGvE4 \
--to 9Ht4Kv8pYmW2rXjN6dFcQsA7bEoLf3gUZx1wDnR5eTi \
--amount 2000
```
### d. Verify balances
```bash
wallet account get --account-id Public/7a2Bf9cKLm3XpRtH1wDqZs8vYjN4eU6gAoFxW5kMnE2R
# Output:
Holding account owned by ata program
{"account_type":"Token holding","definition_id":"3YpK8RvVzWm6Q4h2nDAbxJfLmuRqkEkFP9C7UwTdGvE4","balance":3000}
```
```bash
wallet account get --account-id Public/9Ht4Kv8pYmW2rXjN6dFcQsA7bEoLf3gUZx1wDnR5eTi
# Output:
Holding account owned by token program
{"account_type":"Token holding","definition_id":"3YpK8RvVzWm6Q4h2nDAbxJfLmuRqkEkFP9C7UwTdGvE4","balance":2000}
```
## 5. Burning tokens from an ATA (`wallet ata burn`)
The `burn` subcommand destroys tokens held in the owner's ATA, reducing the token's total supply.
### a. Burn tokens
```bash
wallet ata burn \
--holder Public/5FkBei8HYoSUNqh9rWCrJDnSZE5FJfGiWmTvhgBx3qTB \
--token-definition 3YpK8RvVzWm6Q4h2nDAbxJfLmuRqkEkFP9C7UwTdGvE4 \
--amount 500
```
### b. Verify the reduced balance
```bash
wallet account get --account-id Public/7a2Bf9cKLm3XpRtH1wDqZs8vYjN4eU6gAoFxW5kMnE2R
# Output:
Holding account owned by ata program
{"account_type":"Token holding","definition_id":"3YpK8RvVzWm6Q4h2nDAbxJfLmuRqkEkFP9C7UwTdGvE4","balance":2500}
```
## 6. Listing ATAs (`wallet ata list`)
The `list` subcommand queries ATAs for a given owner across one or more token definitions.
### a. Create a second token and ATA
Create a second token definition so there are multiple ATAs to list:
```bash
wallet account new public
# Output:
Generated new account with account_id Public/BxR3Lm7YkWp9vNs2hD4qJcTfA8eUoZ6gKn1wXjM5rFi
```
```bash
wallet account new public
# Output:
Generated new account with account_id Public/Ck8mVp4YhWn2rXjD6dFsQtA7bEoLf3gUZx1wDnR9eTs
```
```bash
wallet token new \
--name OTHERTOKEN \
--total-supply 5000 \
--definition-account-id Public/BxR3Lm7YkWp9vNs2hD4qJcTfA8eUoZ6gKn1wXjM5rFi \
--supply-account-id Public/Ck8mVp4YhWn2rXjD6dFsQtA7bEoLf3gUZx1wDnR9eTs
```
Create an ATA for the second token:
```bash
wallet ata create \
--owner Public/5FkBei8HYoSUNqh9rWCrJDnSZE5FJfGiWmTvhgBx3qTB \
--token-definition BxR3Lm7YkWp9vNs2hD4qJcTfA8eUoZ6gKn1wXjM5rFi
```
### b. List ATAs for both token definitions
```bash
wallet ata list \
--owner 5FkBei8HYoSUNqh9rWCrJDnSZE5FJfGiWmTvhgBx3qTB \
--token-definition \
3YpK8RvVzWm6Q4h2nDAbxJfLmuRqkEkFP9C7UwTdGvE4 \
BxR3Lm7YkWp9vNs2hD4qJcTfA8eUoZ6gKn1wXjM5rFi
# Output:
ATA 7a2Bf9cKLm3XpRtH1wDqZs8vYjN4eU6gAoFxW5kMnE2R (definition 3YpK8RvVzWm6Q4h2nDAbxJfLmuRqkEkFP9C7UwTdGvE4): balance 2500
ATA 4nPxKd8YmW7rVsH2jDfQcA9bEoLf6gUZx3wTnR1eMs5 (definition BxR3Lm7YkWp9vNs2hD4qJcTfA8eUoZ6gKn1wXjM5rFi): balance 0
```
> [!Note]
> The `list` command derives each ATA address locally and fetches its on-chain state. If an ATA has not been created for a given definition, it prints "No ATA for definition ..." instead.
## 7. Private owner operations
All three ATA operations — `create`, `send`, and `burn` — support private owner accounts. Passing a `Private/` prefix on the owner argument switches the wallet into privacy-preserving mode:
1. The wallet builds the transaction locally.
2. The ATA program is executed inside the RISC0 ZK VM to generate a proof.
3. The proof, the updated ATA state (in plaintext), and an encrypted update for the owner's private account are submitted to the sequencer.
4. The sequencer verifies the proof, writes the ATA state change to the public chain, and records the owner's new commitment in the nullifier set.
The result is that the ATA account and its token balance are **fully public** — anyone can see them. What stays private is the link between the ATA and its owner: the proof demonstrates that someone with the correct private key authorized the operation, but reveals nothing about which account that was.
> [!Note]
> The ATA address is derived from `SHA256(owner_id || definition_id)`. Because SHA256 is one-way, the ATA address does not reveal the owner's identity. However, if the owner's account ID becomes known for any other reason, all of their ATAs across every token definition can be enumerated by anyone.
### a. Create a private account
```bash
wallet account new private
# Output:
Generated new account with account_id Private/HkR7Lm2YnWp4vNs8hD3qJcTfA6eUoZ9gKn5wXjM1rFi
```
### b. Create the ATA for the private owner
Pass `Private/` on `--owner`. The token definition account has no privacy prefix — it is always a public account.
```bash
wallet ata create \
--owner Private/HkR7Lm2YnWp4vNs8hD3qJcTfA6eUoZ9gKn5wXjM1rFi \
--token-definition 3YpK8RvVzWm6Q4h2nDAbxJfLmuRqkEkFP9C7UwTdGvE4
```
> [!Note]
> Proof generation runs locally in the RISC0 ZK VM and can take up to a minute on first run.
### c. Verify the ATA was created
Derive the ATA address using the raw account ID (no privacy prefix):
```bash
wallet ata address \
--owner HkR7Lm2YnWp4vNs8hD3qJcTfA6eUoZ9gKn5wXjM1rFi \
--token-definition 3YpK8RvVzWm6Q4h2nDAbxJfLmuRqkEkFP9C7UwTdGvE4
# Output:
2pQxNf7YkWm3rVsH8jDcQaA4bEoLf9gUZx6wTnR2eMs1
```
```bash
wallet account get --account-id Public/2pQxNf7YkWm3rVsH8jDcQaA4bEoLf9gUZx6wTnR2eMs1
# Output:
Holding account owned by ata program
{"account_type":"Token holding","definition_id":"3YpK8RvVzWm6Q4h2nDAbxJfLmuRqkEkFP9C7UwTdGvE4","balance":0}
```
### d. Fund the ATA
The ATA is a public account. Fund it with a direct token transfer from any public holding account:
```bash
wallet token send \
--from Public/5FkBei8HYoSUNqh9rWCrJDnSZE5FJfGiWmTvhgBx3qTB \
--to Public/2pQxNf7YkWm3rVsH8jDcQaA4bEoLf9gUZx6wTnR2eMs1 \
--amount 500
```
### e. Send tokens from the private owner's ATA
```bash
wallet ata send \
--from Private/HkR7Lm2YnWp4vNs8hD3qJcTfA6eUoZ9gKn5wXjM1rFi \
--token-definition 3YpK8RvVzWm6Q4h2nDAbxJfLmuRqkEkFP9C7UwTdGvE4 \
--to 9Ht4Kv8pYmW2rXjN6dFcQsA7bEoLf3gUZx1wDnR5eTi \
--amount 200
```
Verify the ATA balance decreased:
```bash
wallet account get --account-id Public/2pQxNf7YkWm3rVsH8jDcQaA4bEoLf9gUZx6wTnR2eMs1
# Output:
Holding account owned by ata program
{"account_type":"Token holding","definition_id":"3YpK8RvVzWm6Q4h2nDAbxJfLmuRqkEkFP9C7UwTdGvE4","balance":300}
```
### f. Burn tokens from the private owner's ATA
```bash
wallet ata burn \
--holder Private/HkR7Lm2YnWp4vNs8hD3qJcTfA6eUoZ9gKn5wXjM1rFi \
--token-definition 3YpK8RvVzWm6Q4h2nDAbxJfLmuRqkEkFP9C7UwTdGvE4 \
--amount 100
```
Verify the balance and token supply:
```bash
wallet account get --account-id Public/2pQxNf7YkWm3rVsH8jDcQaA4bEoLf9gUZx6wTnR2eMs1
# Output:
Holding account owned by ata program
{"account_type":"Token holding","definition_id":"3YpK8RvVzWm6Q4h2nDAbxJfLmuRqkEkFP9C7UwTdGvE4","balance":200}
```

View File

@ -0,0 +1,237 @@
This tutorial walks you through using Keycard with Wallet CLI. Keycard is optional hardware that can offer enhance security to a LEZ wallet. A LEZ wallet that utilizes Keycard does not store any secret keys for public accounts (eventually, this will extend to private accounts). Instead, Wallet CLI retrieves the appropriate public keys and signatures from Keycard.
## Keycard Setup
### Required hardware
- Keycard (Blank) - a Keycard, directly, from Keycard.tech cannot (currently) be updated to support LEE.
- Smartcard reader
- Applets (`math.cap` and `LEE_keycard.cap`). Eventually, both of these applets will be available in separate repos.
- `math.cap` is an applet to speed up computations on Keycard; developed by Bitgamma (Keycard-tech team).
- `LEE_keycard.cap` is an applet that contains LEE keycard protocol; developed by Bitgamma (Keycard-tech team)
### Firmware installation
Installation:
1. Install math applet on your keycard; this process only needs to be done once. In the root of repo:
```
sudo apt-get install -y default-jdk
wget https://github.com/martinpaljak/GlobalPlatformPro/releases/download/v25.10.20/gp.jar -P keycard_wallet/keycard_applets
cd keycard_wallet/keycard_applets
java -jar gp.jar --key c212e073ff8b4bbfaff4de8ab655221f --load math.cap
```
2. Install `keycard-desktop` from [github](https://github.com/choppu/keycard-desktop)
- Keycard Desktop is used to install the LEE key protocol to a blank keycard.
- Select (Re)Install Applet and upload the key binary (`keycard_wallet/keycard_applets/LEE_keycard.cap`).
![keycard-desktop.png](keycard-desktop.png)
- **Important:** keycard can only connect with one application at a time; if Keycard-Desktop is using keycard then Wallet CLI cannot access the same keycard, and vice-versa.
## Wallet with Keycard
Keycard functionality is available to Wallet CLI by setting up the following Python virtual environment. The steps below can also be run via `keycard_wallet/wallet_with_keycard.sh`.
```bash
# Install appropriate version of `keycard-py`.
git clone --branch lee-schnorr --single-branch https://github.com/bitgamma/keycard-py.git keycard_wallet/python/keycard-py
# Set up virtual environment.
python3 -m venv venv
source venv/bin/activate
pip install pyscard mnemonic ecdsa pyaes
pip install -e keycard_wallet/python/keycard-py
```
**Important**: Keycard wallet commands only work within the virtual environment.
```bash
# In the root of LEE repo:
source venv/bin/activate
```
## PIN entry
Each Keycard command prompts for a PIN interactively. To avoid re-entering it across multiple commands, export it as an environment variable:
```bash
export KEYCARD_PIN=123456
```
Unset it when done:
```bash
unset KEYCARD_PIN
```
## Keycard Commands
### Keycard
| Command | Description |
|-----------------------------|------------------------------------------------------------|
| `wallet keycard available` | Checks whether a Keycard reader and card are accessible |
| `wallet keycard init` | Initializes a blank Keycard with a PIN and a generated PUK |
| `wallet keycard connect` | Establishes and saves a pairing with the Keycard |
| `wallet keycard disconnect` | Unpairs the Keycard and clears the saved pairing |
| `wallet keycard load` | Loads a mnemonic phrase onto the Keycard |
1. Check keycard availability
```bash
wallet keycard available
# Output:
✅ Keycard is available.
```
2. Initialize a blank Keycard
```bash
wallet keycard init
# Output:
Keycard PIN:
Keycard PUK: 847302916485
Record this PUK and store it somewhere safe. It cannot be recovered.
✅ Keycard initialized successfully.
```
3. Connect (pair and save pairing for subsequent commands)
```bash
wallet keycard connect
# Output:
Keycard PIN:
✅ Keycard paired and ready.
```
4. Load a mnemonic phrase
```bash
# Supply mnemonic via environment variable to avoid interactive prompt
export KEYCARD_MNEMONIC="fashion degree mountain wool question damp current pond grow dolphin chronic then"
wallet keycard load
unset KEYCARD_MNEMONIC
# Output:
Keycard PIN:
✅ Keycard is now connected to wallet.
✅ Mnemonic phrase loaded successfully.
```
5. Disconnect (unpair and clear saved pairing)
```bash
wallet keycard disconnect
# Output:
Keycard PIN:
✅ Keycard unpaired and pairing cleared.
```
### Pinata (testnet)
| Command | Description |
|-----------------------|--------------------------------------------------------------------------|
| `wallet pinata claim` | Claims a testnet pinata reward to a public or private recipient account |
Note: The recipient account must be initialized with `wallet auth-transfer init` before claiming.
`--to` accepts any of:
- A BIP32 key path — uses Keycard (e.g. `m/44'/60'/0'/0/0`)
- An account ID with privacy prefix (e.g. `Public/9bKm...`)
- An account label (e.g. `my-account`)
1. Claim to a Keycard public account
```bash
wallet pinata claim --to "m/44'/60'/0'/0/0"
# Output:
Keycard PIN:
Computing solution for pinata...
Found solution 989106 in 33.739525ms
Transaction hash is fd320c01f5469e62d2486afa1d9d5be39afcca0cd01d1575905b7acd95cf6397
```
2. Claim to a local wallet account by label
```bash
wallet pinata claim --to my-account
# Output:
Transaction hash is 2c8a4f1e903d5b76e80214c5b82e1d46a105e28930ad71bcce48f2d07b49a16f
```
### Authenticated-transfer program
| Command | Description |
|-----------------------------|-------------------------------------------------------------------------------|
| `wallet auth-transfer init` | Registers an account with the auth-transfer program |
| `wallet auth-transfer send` | Sends native tokens between accounts |
`--account-id` (for `init`) and `--from`/`--to` (for `send`) each accept any of:
- A BIP32 key path — uses Keycard (e.g. `m/44'/60'/0'/0/0`)
- An account ID with privacy prefix (e.g. `Public/9bKm...`)
- An account label (e.g. `my-account`)
For `send`, foreign recipient accounts (not in the local wallet and not a Keycard path) do not need to sign — pass their account ID directly via `--to`. Shielded sends to foreign private accounts use `--to-npk`/`--to-vpk`.
1. Initialize a Keycard public account
```bash
wallet auth-transfer init --account-id "m/44'/60'/0'/0/0"
# Output:
Keycard PIN:
Transaction hash is 49c16940493e1618c393645c1211b5c793d405838221c29ac6562a8a4b11c5a7
```
2. Send native tokens between two Keycard accounts
```bash
wallet auth-transfer send \
--from "m/44'/60'/0'/0/0" \
--to "m/44'/60'/0'/0/1" \
--amount 40
# Output:
Keycard PIN:
Transaction hash is 1a9764ab20763dcc1ffb51c6e9badd5a6316a773759032ca48e0eee59caaf488
```
3. Send native tokens from a Keycard account to a foreign account
```bash
wallet auth-transfer send \
--from "m/44'/60'/0'/0/0" \
--to "Public/9bKmZ4n7PqVRxEtY3dWsQjA2cHrFT5LpDoGXM8wJuNv6" \
--amount 20
# Output:
Keycard PIN:
Transaction hash is 3e7b2a91cf804d56fe19084b3c8b25d07e8f243829bc50addf6e2c78b4b09d34
```
4. Send native tokens from a Keycard account to a local wallet account by label
```bash
wallet auth-transfer send \
--from "m/44'/60'/0'/0/0" \
--to my-account \
--amount 20
# Output:
Keycard PIN:
Transaction hash is 7d4c1b8e2f903a56fd19084b3c8b25d07e8f243829bc50addf6e2c78b4b09e45
```
## Testing
Tests for Keycard commands are in `keycard_wallet/tests/keycard_tests.sh`. Run from the repo root with a Keycard connected:
```bash
bash keycard_wallet/tests/keycard_tests.sh
```
## SigningGroups
`SigningGroups` (`wallet/src/signing.rs`) partitions a transaction's signers into two buckets — local accounts and Keycard accounts. This ensures that Python GIL is only used at most once per transaction, regardless of how many Keycard accounts are involved.
Local signers are resolved and signed in pure Rust. Keycard signers store only their BIP32 key path; all of them are signed inside a single Python session (`connect` / `close_session`) when `sign_all` is called. The command calls `needs_pin` to decide whether to prompt for a PIN before signing.
Foreign recipient accounts — those with no local key and no Keycard path — are silently skipped and require neither a signature nor a nonce.
```
SigningGroups {
local: [(AccountId, PrivateKey)], // signed in pure Rust
keycard: [(AccountId, BIP32Path)], // signed via a single Python/Keycard session
}
```

View File

@ -5,6 +5,7 @@ This tutorial walks through native token transfers between public and private ac
4. Private account creation. 4. Private account creation.
5. Native token transfer from a public account to a private account. 5. Native token transfer from a public account to a private account.
6. Native token transfer from a public account to a private account owned by someone else. 6. Native token transfer from a public account to a private account owned by someone else.
7. Sending to a private accounts key from multiple independent senders.
--- ---
@ -142,7 +143,7 @@ Account owned by authenticated-transfer program
> Private accounts are structurally identical to public accounts, but their values are stored off-chain. On-chain, only a 32-byte commitment is recorded. > Private accounts are structurally identical to public accounts, but their values are stored off-chain. On-chain, only a 32-byte commitment is recorded.
> Transactions include encrypted private values so the owner can recover them, and the decryption keys are never shared. > Transactions include encrypted private values so the owner can recover them, and the decryption keys are never shared.
> Private accounts use two keypairs: nullifier keys for privacy-preserving executions and viewing keys for encrypting and decrypting values. > Private accounts use two keypairs: nullifier keys for privacy-preserving executions and viewing keys for encrypting and decrypting values.
> The private account ID is derived from the nullifier public key. > The private account ID is derived from the nullifier public key and a numeric identifier: `SHA256(prefix || npk || identifier)`. The same `npk` paired with different identifiers yields different, independent account IDs.
> Private accounts can be initialized by anyone, but once initialized they can only be modified by the owners keys. > Private accounts can be initialized by anyone, but once initialized they can only be modified by the owners keys.
> Updates include a new commitment and a nullifier for the old state, which prevents linkage between versions. > Updates include a new commitment and a nullifier for the old state, which prevents linkage between versions.
@ -158,7 +159,9 @@ With vpk 02ddc96d0eb56e00ce14994cfdaec5ae1f76244180a919545983156e3519940a17
``` ```
> [!Tip] > [!Tip]
> Focus on the account ID for now. The `npk` and `vpk` values are stored locally and used to build privacy-preserving transactions. The private account ID is derived from `npk`. > Save this account ID. You will use it in later commands.
### b. Check the account status
Just like public accounts, new private accounts start out uninitialized: Just like public accounts, new private accounts start out uninitialized:
@ -218,21 +221,23 @@ Account owned by authenticated-transfer program
## 6. Native token transfer from a public account to a private account owned by someone else ## 6. Native token transfer from a public account to a private account owned by someone else
> [!Important] > [!Important]
> Well simulate transferring to someone else by creating a new private account we own and treating it as if it belonged to another user. > Well simulate transferring to someone else by creating a new private accounts key and treating it as if it belonged to another user. When the recipient is someone else, you only have their `npk` and `vpk` — not an account ID.
### a. Create a new uninitialized private account ### a. Create a new private accounts key to simulate a foreign recipient
```bash ```bash
wallet account new private wallet account new private-accounts-key
# Output: # Output:
Generated new account with account_id Private/AukXPRBmrYVqoqEW2HTs7N3hvTn3qdNFDcxDHVr5hMm5 Generated new private accounts key at path /1
With npk 0c95ebc4b3830f53da77bb0b80a276a776cdcf6410932acc718dcdb3f788a00e With npk 0c95ebc4b3830f53da77bb0b80a276a776cdcf6410932acc718dcdb3f788a00e
With vpk 039fd12a3674a880d3e917804129141e4170d419d1f9e28a3dcf979c1f2369cb72 With vpk 039fd12a3674a880d3e917804129141e4170d419d1f9e28a3dcf979c1f2369cb72
``` ```
> [!Tip] > [!Tip]
> Ignore the private account ID here and use the `npk` and `vpk` values to send to a foreign private account. > Ignore the account ID here and use the `npk` and `vpk` values to send to a foreign private account.
### b. Send 3 tokens using the recipients npk and vpk
```bash ```bash
wallet auth-transfer send \ wallet auth-transfer send \
@ -242,9 +247,74 @@ wallet auth-transfer send \
--amount 3 --amount 3
``` ```
> [!Note]
> `--to-identifier` is omitted here. When omitted, the wallet picks a random identifier, which is usually fine. Use the flag explicitly when a specific identifier is required.
> [!Warning] > [!Warning]
> This command creates a privacy-preserving transaction, which may take a few minutes. The updated values are encrypted and included in the transaction. > This command creates a privacy-preserving transaction, which may take a few minutes. The updated values are encrypted and included in the transaction.
> Once accepted, the recipient must run `wallet account sync-private` to scan the chain for their encrypted updates and refresh local state. > Once accepted, the recipient must run `wallet account sync-private` to scan the chain for their encrypted updates and refresh local state.
> [!Note] > [!Note]
> You have seen transfers between two public accounts and from a public sender to a private recipient. Transfers from a private sender, whether to a public account or to another private account, follow the same pattern. > You have seen transfers between two public accounts and from a public sender to a private recipient. Transfers from a private sender, whether to a public account or to another private account, follow the same pattern.
## 7. Sending to a private accounts key from multiple independent senders
> [!Important]
> A private accounts key (`npk` + `vpk`) can be shared with multiple senders. Each sender independently chooses an identifier; the recipient's account ID is derived from `(npk, identifier)`. Two senders using different identifiers produce two separate private accounts under the same key.
### a. Alice creates a private accounts key
```bash
wallet account new private-accounts-key
# Output:
Generated new private accounts key at path /2
With npk a3f7c21b8e905d4f6a1bc783d0e2f94c1d5a6b7e8f9012345678abcdef012345
With vpk 03b1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6071819202122232425262728292a2b2c
```
Alice shares the `npk` and `vpk` values with Bob and Charlie out of band.
### b. Bob sends 10 tokens to Alice using identifier 1
```bash
wallet auth-transfer send \
--from Public/BobXqJprP9BmhbFVQyBcbznU8bAXcwrzwRoPTetXdQPA \
--to-npk a3f7c21b8e905d4f6a1bc783d0e2f94c1d5a6b7e8f9012345678abcdef012345 \
--to-vpk 03b1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6071819202122232425262728292a2b2c \
--to-identifier 1 \
--amount 10
```
### c. Charlie sends 5 tokens to Alice using identifier 2
```bash
wallet auth-transfer send \
--from Public/CharlieYrP9BmhbFVQyBcbznU8bAXcwrzwRoPTetXdQPB \
--to-npk a3f7c21b8e905d4f6a1bc783d0e2f94c1d5a6b7e8f9012345678abcdef012345 \
--to-vpk 03b1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6071819202122232425262728292a2b2c \
--to-identifier 2 \
--amount 5
```
> [!Note]
> Bob and Charlie each chose a different identifier. They do not need to coordinate — any two distinct values work.
### d. Alice syncs to discover the new accounts
```bash
wallet account sync-private
```
```bash
wallet account list
# Output (private account entries under key /2):
/2 Private/AliceBobAcctXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
/2 Private/AliceCharlieAcctXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```
Alice now has two separate private accounts, one funded by Bob and one by Charlie, both controlled by the same key at path `/2`.
> [!Tip]
> Alice can check each account balance with `wallet account get --account-id Private/...`. Neither balance is visible on-chain.

11
docs/benchmarks/README.md Normal file
View File

@ -0,0 +1,11 @@
# Benchmarks
Bench tools live under `tools/` with READMEs for how to run each one. This directory holds the result write-ups: machine, raw tables, and short findings.
| Bench | Doc |
|---|---|
| cycle_bench | [cycle_bench.md](cycle_bench.md) |
| crypto_primitives_bench | [crypto_primitives_bench.md](crypto_primitives_bench.md) |
| integration_bench | [integration_bench.md](integration_bench.md) |
All numbers are from a single M2 Pro dev box unless noted otherwise.

View File

@ -0,0 +1,56 @@
# crypto_primitives_bench
Cryptographic primitives used by client/wallet code. Measures the per-call cost of key derivation, sender-side DH for note encryption, and Account note symmetric encrypt/decrypt. Standalone host binary, no live stack required.
## Machine
| Field | Value |
|---|---|
| Chip | Apple M2 Pro (8P+4E) |
| RAM | 16 GB |
| OS | macOS 15.5 |
| Rust | 1.94.0 |
| Profile | release |
## Results
Criterion sample_size = 50, warm_up_time = 2 s, measurement_time = 10 s. Slope-regression point estimate in the middle column; 95% confidence interval bounds in the outer columns.
| Operation | low | point | high | outliers (mild + severe) |
|---|---:|---:|---:|---:|
| keychain/new_os_random | 3.11 ms | 3.21 ms | 3.34 ms | 3 + 5 |
| keychain/new_mnemonic | 3.05 ms | 3.11 ms | 3.23 ms | 0 + 2 |
| shared_secret_key/sender_dh | 76.7 µs | 78.4 µs | 80.6 µs | 3 + 4 |
| encryption/encrypt | 1.11 µs | 1.17 µs | 1.25 µs | 1 + 5 |
| encryption/decrypt | 907 ns | 928 ns | 954 ns | 0 + 3 |
Numbers from a single M2 Pro dev box. For full estimates (slope, mean, median, MAD, std-dev) and the noise model, see `target/criterion/<group>/<bench>/estimates.json` after running locally.
## Findings
- Keychain creation is dominated by the 2048-round HMAC-SHA512 PBKDF in the mnemonic-to-SSK path. ≈ 3 ms.
- Per-recipient DH (secp256k1) is ≈ 80 µs. Outbound shielded transfers to N recipients cost ≈ 80·N µs of crypto on top of proving.
- Symmetric encrypt/decrypt over a 49-byte Account note is sub-µs. Bulk encryption is not the bottleneck.
## Reproduce
```sh
cargo bench -p crypto_primitives_bench --bench primitives
```
JSON estimates: `target/criterion/<group>/<bench>/estimates.json`. HTML report: `target/criterion/report/index.html`.
## Baseline comparison
```sh
# On main:
cargo bench -p crypto_primitives_bench --bench primitives -- --save-baseline main
# On your branch:
cargo bench -p crypto_primitives_bench --bench primitives -- --baseline main
```
Criterion reports per-bench change as a percentage with a 95% confidence interval; deltas within the CI are reported as "no significant change" rather than red.
## Caveats
- Single-thread, no SIMD acceleration. Bench dev box uses the pure-Rust secp256k1 backend.

View File

@ -0,0 +1,101 @@
# cycle_bench
Per-program Risc0 cycle counts, prover wall time, PPE composition cost, and verifier wall time for the built-in LEZ programs. Inputs for the fee model's `G_executor`, `G_prove`, `G_verify`, and `S_agg` parameters.
## Machine
| Field | Value |
|---|---|
| Chip | Apple M2 Pro (8P+4E) |
| RAM | 16 GB |
| OS | macOS 15.5 |
| Rust | 1.94.0 |
| Risc0 zkVM | 3.0.5 |
| Profile | release |
| GPU acceleration | none |
## Executor cycles
`SessionInfo::cycles()` per instruction. Deterministic across runs. Wall time is `best / mean ± stdev` over 5 timed iterations (1 warmup discarded).
| Program | Instruction | user_cycles | segments | exec_ms (best / mean ± stdev) |
|---|---|---:|---:|---|
| authenticated_transfer | Initialize | 43,642 | 1 | 18.86 / 19.41 ± 0.48 |
| authenticated_transfer | Transfer | 77,095 | 1 | 19.67 / 20.84 ± 1.16 |
| token | Burn | 116,546 | 1 | 24.86 / 25.46 ± 0.63 |
| token | Mint | 116,862 | 1 | 24.47 / 25.08 ± 0.42 |
| token | Transfer | 127,726 | 1 | 25.00 / 25.40 ± 0.29 |
| clock | Tick (no rollups) | 137,022 | 1 | 21.18 / 21.57 ± 0.41 |
| ata | Create | 175,056 | 1 | 23.64 / 24.94 ± 1.09 |
| amm | SwapExactInput | 508,634 | 1 | 34.21 / 34.77 ± 0.55 |
| amm | AddLiquidity | 642,774 | 1 | 37.59 / 37.87 ± 0.28 |
## Real proving (`--prove`)
`prover.prove(env, elf)` wall time per program on CPU. `total_cycles` is `user_cycles` rounded up to the next power of two (Risc0 padding).
| Program | Instruction | total_cycles | prove_ms | prove_s |
|---|---|---:|---:|---:|
| authenticated_transfer | Initialize | 131,072 | 11,881 | 11.9 |
| authenticated_transfer | Transfer | 131,072 | 13,705 | 13.7 |
| token | Burn | 262,144 | 22,893 | 22.9 |
| token | Mint | 262,144 | 23,927 | 23.9 |
| token | Transfer | 262,144 | 27,178 | 27.2 |
| clock | Tick | 262,144 | 23,486 | 23.5 |
| ata | Create | 262,144 | 21,093 | 21.1 |
| amm | AddLiquidity | 1,048,576 | 111,654 | 111.7 |
| amm | SwapExactInput | 1,048,576 | 126,400 | 126.4 |
Linear fit across po2 buckets: ≈ 100 µs per total cycle (≈ 10k cycles/s throughput on this CPU).
## PPE composition + chain-call sweep (`--ppe`)
Same `auth_transfer Transfer` instruction, standalone vs wrapped in the privacy circuit; plus the `chain_caller` test program with N chained `authenticated_transfer` calls. `proof_bytes` is the borsh-serialized. InnerReceipt (S_agg in the fee model).
| Case | prove_ms | prove_s | proof_bytes |
|---|---:|---:|---:|
| auth_transfer Transfer standalone | 13,705 | 13.7 | n/a |
| auth_transfer Transfer in PPE | 61,486 | 61.5 | 223,551 |
| chain_caller depth=1 | 122,590 | 122.6 | 223,551 |
| chain_caller depth=3 | 231,974 | 232.0 | 223,551 |
| chain_caller depth=5 | 372,123 | 372.1 | 223,551 |
| chain_caller depth=9 | 544,280 | 544.3 | 223,551 |
Linear fit depth=1..9: ≈ 53 s per additional chained call, intercept ≈ 73 s. Composition tax (single program PPE standalone): ≈ 48 s. `proof_bytes` is constant: the outer succinct proof has fixed size; the journal carried alongside it scales with public state and is reported separately by `--verify`.
## Verifier (criterion bench)
One PPE receipt generated once (auth_transfer Transfer in PPE), then `Receipt::verify(PRIVACY_PRESERVING_CIRCUIT_ID)` measured under criterion's statistical sampler. Bench file: `tools/cycle_bench/benches/verify.rs`. Setup (one full PPE prove) is outside the timed `iter` loop.
Numbers from the most recent local run on the machine listed above. Criterion sample_size = 100, measurement_time = 15 s, warm_up_time = 2 s. Slope-regression point estimate in the middle column; 95% CI bounds on either side. Run `cargo bench -p cycle_bench --features ppe --bench verify` to refresh.
| Bench | low | point | high | outliers (mild + severe) |
|---|---:|---:|---:|---:|
| ppe/verify_auth_transfer | 12.016 ms | 12.215 ms | 12.469 ms | 1 + 10 |
The corresponding `proof_bytes` (S_agg) for the bench receipt is captured by `--ppe` above; the verify bench itself only times the verify call.
## Findings
- Proving cost scales with po2-bucketed `total_cycles`, not raw `user_cycles`. Trimming user_cycles only helps if it crosses a 2^N boundary.
- Single-program PPE composition tax on M2 Pro CPU: ≈ 48 s (61.5 13.7).
- Chained-call cost is linear at ≈ 53 s per call. A max-depth chain (10) would take ≈ 600 s standalone on this CPU.
- `G_verify` is ≈ 12 ms (criterion CI: 12.012.5 ms over 100 samples) and roughly constant per outer receipt. The succinct outer proof is fixed at 223,551 bytes (S_agg); verify is not on the latency critical path.
## Reproduce
```sh
cargo run --release -p cycle_bench
cargo run --release -p cycle_bench --features prove -- --prove
cargo run --release -p cycle_bench --features ppe -- --prove --ppe
# Verifier microbench via criterion:
cargo bench -p cycle_bench --features ppe --bench verify
```
JSON output: `target/cycle_bench.json` (bin), `target/criterion/ppe/verify_auth_transfer/` (verify bench).
## Caveats
- CPU-only proving on a dev laptop. Production prover hardware (GPU, specialised CPU pipelines) will produce much smaller numbers; relative ordering should be preserved.
- Single-segment cases only; multi-segment programs would pay continuation overhead not measured here.

View File

@ -0,0 +1,120 @@
# integration_bench
End-to-end LEZ scenarios driven through the wallet against a docker-compose Bedrock node + in-process sequencer + indexer (via `test_fixtures::TestContext`). Times each step and records borsh sizes per block, split by tx variant.
Numbers below are from a single-host docker-compose run on an Apple M2 Pro (CPU only, no GPU acceleration). Absolute wall time and block sizes depend heavily on the bedrock config (block cadence and confirmation depth) and on dev-mode vs real proving; re-run the bench locally to characterise your own setup.
## Scenarios
| Scenario | Description |
|---|---|
| token | Sequential public token Send + one shielded recipient setup. |
| amm | Pool create, add liquidity, swap, remove liquidity. All public. |
| fanout | One sender → N recipients, sequential. All public. |
| private | Shielded, deshielded, private→private chained private flow. |
| parallel | N senders submit concurrently into one block. All public. |
## Dev-mode vs real-proving
`RISC0_DEV_MODE=1` makes the prover emit stub receipts instead of running the recursive STARK pipeline. The table compares each quantity in dev mode vs real proving for the two classes of scenarios:
| Quantity | Public-only scenarios (dev → real) | PPE-bearing scenarios (dev → real) |
|---|---|---|
| Wall time per step | same in both modes | real adds ~100 s per PPE step |
| `public_tx_bytes` | same in both modes | same in both modes |
| `ppe_tx_bytes` | n/a | dev ≈ 2 KB stub → real ≈ 225 KB (matches `S_agg` from cycle_bench) |
| `block_bytes` | same in both modes | real adds ~225 KB per PPE tx in the block |
| `bedrock_finality_s` | same in both modes | same in both modes (L1 cadence, not LEZ prover) |
| Blocks captured | similar in both modes | real captures more empty clock-only ticks that fill prove wall-time |
Tables below report dev-mode for all five scenarios. Real-proving numbers are included for `amm_swap_flow` (representative all-public) and `private_chained_flow` (representative chained-private flow); public-only scenarios converge between modes within run-to-run jitter, so a full real-proving sweep is not run here.
## Methodology
Per scenario, every produced block is fetched via `getBlock(BlockId)` and serialized with `borsh::to_vec(&Block)`. Each transaction is serialized individually and counted by variant. Empty clock-only ticks give the per-block fixed-cost baseline. Wall time is captured per step (submit + inclusion + wallet sync) and aggregated to the per-scenario `total_s`. The one-time stack-setup cost (`shared_setup_s` at the run level) and the closing bedrock finality wait (`bedrock_finality_s` per scenario) are reported separately, not folded into `total_s`.
## Step latencies — dev mode (`RISC0_DEV_MODE=1`)
Per-scenario wall time and Bedrock L1-finality latency for the closing tip.
| Scenario | total_s | bedrock_finality_s |
|---|---:|---:|
| token_onboarding | 61.36 | 5.88 |
| amm_swap_flow | 156.50 | 27.99 |
| multi_recipient_fanout | 214.40 | 31.71 |
| private_chained_flow | 109.31 | 8.73 |
| parallel_fanout | 234.42 | 20.29 |
Shared TestContext setup: 139.80 s (paid once per run). Total dev-mode wall time across all five scenarios: 1010.4 s.
## Step latencies — real proving (selected scenarios)
| Scenario | total_s | bedrock_finality_s | Δ vs dev |
|---|---:|---:|---:|
| amm_swap_flow | 156.20 | 26.95 | ~0 (all-public) |
| private_chained_flow | 391.74 | 9.40 | +282.4 s (≈ 94 s per PPE step × 3) |
Per-step breakdown for `private_chained_flow` in real proving:
| Step | submit_s | inclusion_s | total_s |
|---|---:|---:|---:|
| token_new_fungible (public) | 0.003 | 10.857 | 11.006 |
| shielded_transfer (PPE) | 125.416 | 0.001 | 125.469 |
| deshielded_transfer (PPE) | 126.261 | 0.001 | 126.311 |
| private_to_private (PPE) | 128.875 | 0.001 | 128.934 |
PPE steps move the cost from `inclusion_s` (waiting for the next sealed block) to `submit_s` (the wallet itself proving the PPE circuit before sending). Each PPE prove is ≈ 127 s on this CPU.
## Block + tx sizes (borsh) — dev mode
Per scenario, every produced block is fetched via `getBlock(BlockId)` and serialized with `borsh::to_vec(&Block)`. Each transaction is serialized individually and counted by variant. The empty clock-only ticks at `min` give the per-block fixed-cost baseline (≈ 334 bytes across all scenarios).
| Scenario | blocks | block_bytes (mean) | block_bytes (min..max) | public_tx (mean / n) | ppe_tx (mean / n) |
|---|---:|---:|---|---:|---:|
| token_onboarding | 6 | 881 | 334..2,890 | 206 / 8 | 2,556 / 1 |
| amm_swap_flow | 16 | 553 | 334..1,011 | 248 / 24 | n/a |
| multi_recipient_fanout | 22 | 513 | 334..707 | 221 / 33 | n/a |
| private_chained_flow | 10 | 1,186 | 334..3,565 | 173 / 11 | 2,715 / 3 |
| parallel_fanout | 24 | 646 | 334..3,904 | 248 / 45 | n/a |
## Block + tx sizes (borsh) — real proving
| Scenario | blocks | block_bytes (mean) | block_bytes (min..max) | public_tx (mean / n) | ppe_tx (mean / n) |
|---|---:|---:|---|---:|---:|
| amm_swap_flow | 16 | 553 | 334..1,011 | 248 / 24 | n/a |
| private_chained_flow | 39 | 17,707 | 334..226,578 | 158 / 40 | 225,728 / 3 |
`amm_swap_flow` is byte-identical between dev and real (no proof payload). `private_chained_flow`'s `ppe_tx_bytes` matches the cycle_bench `S_agg` measurement (≈ 225 KB borsh InnerReceipt). The `block_bytes` max (226,578) is the block containing the largest PPE transaction.
## Findings
- Public-only scenarios converge between dev mode and real proving in both latency and byte counts. Either mode is suitable to characterize them.
- PPE transactions are ≈ 225 KB on the wire in real proving, dominated by the outer succinct proof. Dev mode emits a ≈ 2.7 KB stub that does not represent the L1 payload; fee-model storage gas inputs must come from a real-proving run.
- Per-PPE-step prove cost on this CPU is ≈ 127 s, paid on the wallet side at submit time, not on the sequencer. For a single-program chained flow the cost stacks linearly.
- Empty clock-only ticks set the per-block fixed-cost baseline at ≈ 334 bytes across all scenarios and both modes.
- Bedrock L1 finality varies in the 6 to 32 s range across scenarios, driven by L1 cadence and which tick the closing wait happens to land on, not by the LEZ prover.
## Reproduce
Prerequisite: a running local Docker daemon (the `bedrock/docker-compose.yml` is brought up by the bench).
```sh
# Dev-mode sweep (fast)
RISC0_DEV_MODE=1 cargo run --release -p integration_bench -- --scenario all
# Real-proving for representative private flow
cargo run --release -p integration_bench -- --scenario private
# Real-proving for representative public flow
cargo run --release -p integration_bench -- --scenario amm
```
JSON output: `target/integration_bench_dev.json` / `target/integration_bench_prove.json` (suffix toggled by `RISC0_DEV_MODE`).
## Caveats
- Dev-mode `ppe_tx_bytes` and PPE-step latencies are not representative of production; use real-proving numbers for any fee-model input that touches the storage or prover-cost components.
- Single-host run, no GPU acceleration. Real-proving on production prover hardware will move per-step latencies by orders of magnitude; byte counts will not change.
- Bedrock running locally via docker-compose; no real network latency between sequencer and Bedrock.
- Bedrock L1 finality (`bedrock_finality_s`) is set by the bedrock config in `bedrock/docker-compose.yml` (block cadence × confirmation depth). Different configs will shift `bedrock_finality_s` materially.
- All scenarios share a single TestContext for the run (one bedrock + sequencer + indexer + wallet for the whole run, chain state accumulating across scenarios), which matches how the node runs in production.

View File

@ -4,9 +4,14 @@ version = "0.1.0"
edition = "2024" edition = "2024"
license = { workspace = true } license = { workspace = true }
[lints]
workspace = true
[dependencies] [dependencies]
common.workspace = true
nssa.workspace = true nssa.workspace = true
nssa_core.workspace = true nssa_core.workspace = true
sequencer_service_rpc = { workspace = true, features = ["client"] }
wallet.workspace = true wallet.workspace = true
tokio = { workspace = true, features = ["macros"] } tokio = { workspace = true, features = ["macros"] }

View File

@ -4,6 +4,9 @@ version = "0.1.0"
edition = "2024" edition = "2024"
license = { workspace = true } license = { workspace = true }
[lints]
workspace = true
[build-dependencies] [build-dependencies]
risc0-build.workspace = true risc0-build.workspace = true

Some files were not shown because too many files have changed in this diff Show More