mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-02-28 01:03:25 +00:00
Merge branch 'main' into Pravdyvy/indexer-explorer-integration-fixes
This commit is contained in:
commit
3c8e81c11c
30
.github/workflows/ci.yml
vendored
30
.github/workflows/ci.yml
vendored
@ -154,7 +154,35 @@ jobs:
|
||||
env:
|
||||
RISC0_DEV_MODE: "1"
|
||||
RUST_LOG: "info"
|
||||
run: cargo nextest run -p integration_tests -- --skip tps_test
|
||||
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:
|
||||
ref: ${{ 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: Install nextest
|
||||
run: cargo install --locked cargo-nextest
|
||||
|
||||
- name: Run tests
|
||||
env:
|
||||
RISC0_DEV_MODE: "1"
|
||||
RUST_LOG: "info"
|
||||
run: cargo nextest run -p integration_tests indexer -- --skip tps_test
|
||||
|
||||
valid-proof-test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
98
Cargo.lock
generated
98
Cargo.lock
generated
@ -1210,8 +1210,10 @@ dependencies = [
|
||||
"anyhow",
|
||||
"common",
|
||||
"futures",
|
||||
"humantime-serde",
|
||||
"log",
|
||||
"logos-blockchain-chain-broadcast-service",
|
||||
"logos-blockchain-chain-service",
|
||||
"logos-blockchain-common-http-client",
|
||||
"logos-blockchain-core",
|
||||
"reqwest",
|
||||
@ -1448,9 +1450,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.19.1"
|
||||
version = "3.20.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
|
||||
checksum = "5c6f81257d10a0f602a294ae4182251151ff97dbb504ef9afcdda4a64b24d9b4"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
@ -1487,6 +1489,15 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytesize"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6bd91ee7b2422bcb158d90ef4d14f75ef67f340943fc4149891dcce8f8b972a3"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytestring"
|
||||
version = "1.5.0"
|
||||
@ -1742,6 +1753,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
"borsh",
|
||||
"bytesize",
|
||||
"hex",
|
||||
"log",
|
||||
"logos-blockchain-common-http-client",
|
||||
@ -3430,6 +3442,16 @@ version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424"
|
||||
|
||||
[[package]]
|
||||
name = "humantime-serde"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c"
|
||||
dependencies = [
|
||||
"humantime",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hydration_context"
|
||||
version = "0.3.0"
|
||||
@ -3725,6 +3747,7 @@ dependencies = [
|
||||
"borsh",
|
||||
"common",
|
||||
"futures",
|
||||
"humantime-serde",
|
||||
"log",
|
||||
"logos-blockchain-core",
|
||||
"nssa",
|
||||
@ -3835,6 +3858,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
"borsh",
|
||||
"bytesize",
|
||||
"common",
|
||||
"env_logger",
|
||||
"futures",
|
||||
@ -4648,7 +4672,7 @@ checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
[[package]]
|
||||
name = "logos-blockchain-blend-crypto"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b862e6f640a79097b8a42c072e1f78bc430fa222"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290"
|
||||
dependencies = [
|
||||
"blake2",
|
||||
"logos-blockchain-groth16",
|
||||
@ -4662,7 +4686,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "logos-blockchain-blend-message"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b862e6f640a79097b8a42c072e1f78bc430fa222"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290"
|
||||
dependencies = [
|
||||
"blake2",
|
||||
"derivative",
|
||||
@ -4684,7 +4708,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "logos-blockchain-blend-proofs"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b862e6f640a79097b8a42c072e1f78bc430fa222"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290"
|
||||
dependencies = [
|
||||
"ed25519-dalek",
|
||||
"generic-array 1.3.5",
|
||||
@ -4701,7 +4725,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "logos-blockchain-chain-broadcast-service"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b862e6f640a79097b8a42c072e1f78bc430fa222"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"derivative",
|
||||
@ -4717,7 +4741,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "logos-blockchain-chain-service"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b862e6f640a79097b8a42c072e1f78bc430fa222"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bytes",
|
||||
@ -4747,7 +4771,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "logos-blockchain-circuits-prover"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b862e6f640a79097b8a42c072e1f78bc430fa222"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290"
|
||||
dependencies = [
|
||||
"logos-blockchain-circuits-utils",
|
||||
"tempfile",
|
||||
@ -4756,7 +4780,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "logos-blockchain-circuits-utils"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b862e6f640a79097b8a42c072e1f78bc430fa222"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290"
|
||||
dependencies = [
|
||||
"dirs",
|
||||
]
|
||||
@ -4764,7 +4788,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "logos-blockchain-common-http-client"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b862e6f640a79097b8a42c072e1f78bc430fa222"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"hex",
|
||||
@ -4784,7 +4808,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "logos-blockchain-core"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b862e6f640a79097b8a42c072e1f78bc430fa222"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290"
|
||||
dependencies = [
|
||||
"ark-ff 0.4.2",
|
||||
"bincode",
|
||||
@ -4814,7 +4838,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "logos-blockchain-cryptarchia-engine"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b862e6f640a79097b8a42c072e1f78bc430fa222"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290"
|
||||
dependencies = [
|
||||
"cfg_eval",
|
||||
"logos-blockchain-pol",
|
||||
@ -4830,7 +4854,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "logos-blockchain-cryptarchia-sync"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b862e6f640a79097b8a42c072e1f78bc430fa222"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures",
|
||||
@ -4847,7 +4871,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "logos-blockchain-groth16"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b862e6f640a79097b8a42c072e1f78bc430fa222"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290"
|
||||
dependencies = [
|
||||
"ark-bn254 0.4.0",
|
||||
"ark-ec 0.4.2",
|
||||
@ -4865,7 +4889,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "logos-blockchain-http-api-common"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b862e6f640a79097b8a42c072e1f78bc430fa222"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"logos-blockchain-core",
|
||||
@ -4879,7 +4903,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "logos-blockchain-key-management-system-keys"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b862e6f640a79097b8a42c072e1f78bc430fa222"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bytes",
|
||||
@ -4905,7 +4929,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "logos-blockchain-key-management-system-macros"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b862e6f640a79097b8a42c072e1f78bc430fa222"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -4915,7 +4939,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "logos-blockchain-key-management-system-operators"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b862e6f640a79097b8a42c072e1f78bc430fa222"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"logos-blockchain-blend-proofs",
|
||||
@ -4931,7 +4955,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "logos-blockchain-key-management-system-service"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b862e6f640a79097b8a42c072e1f78bc430fa222"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"log",
|
||||
@ -4947,7 +4971,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "logos-blockchain-ledger"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b862e6f640a79097b8a42c072e1f78bc430fa222"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290"
|
||||
dependencies = [
|
||||
"derivative",
|
||||
"logos-blockchain-blend-crypto",
|
||||
@ -4971,7 +4995,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "logos-blockchain-network-service"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b862e6f640a79097b8a42c072e1f78bc430fa222"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"futures",
|
||||
@ -4987,7 +5011,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "logos-blockchain-poc"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b862e6f640a79097b8a42c072e1f78bc430fa222"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290"
|
||||
dependencies = [
|
||||
"logos-blockchain-circuits-prover",
|
||||
"logos-blockchain-circuits-utils",
|
||||
@ -5003,7 +5027,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "logos-blockchain-pol"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b862e6f640a79097b8a42c072e1f78bc430fa222"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290"
|
||||
dependencies = [
|
||||
"astro-float",
|
||||
"logos-blockchain-circuits-prover",
|
||||
@ -5022,7 +5046,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "logos-blockchain-poq"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b862e6f640a79097b8a42c072e1f78bc430fa222"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290"
|
||||
dependencies = [
|
||||
"logos-blockchain-circuits-prover",
|
||||
"logos-blockchain-circuits-utils",
|
||||
@ -5039,7 +5063,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "logos-blockchain-poseidon2"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b862e6f640a79097b8a42c072e1f78bc430fa222"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290"
|
||||
dependencies = [
|
||||
"ark-bn254 0.4.0",
|
||||
"ark-ff 0.4.2",
|
||||
@ -5050,7 +5074,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "logos-blockchain-services-utils"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b862e6f640a79097b8a42c072e1f78bc430fa222"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"futures",
|
||||
@ -5065,7 +5089,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "logos-blockchain-storage-service"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b862e6f640a79097b8a42c072e1f78bc430fa222"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bytes",
|
||||
@ -5082,7 +5106,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "logos-blockchain-time-service"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b862e6f640a79097b8a42c072e1f78bc430fa222"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"futures",
|
||||
@ -5100,7 +5124,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "logos-blockchain-utils"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b862e6f640a79097b8a42c072e1f78bc430fa222"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"blake2",
|
||||
@ -5117,7 +5141,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "logos-blockchain-utxotree"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b862e6f640a79097b8a42c072e1f78bc430fa222"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290"
|
||||
dependencies = [
|
||||
"ark-ff 0.4.2",
|
||||
"logos-blockchain-groth16",
|
||||
@ -5131,7 +5155,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "logos-blockchain-witness-generator"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b862e6f640a79097b8a42c072e1f78bc430fa222"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290"
|
||||
dependencies = [
|
||||
"tempfile",
|
||||
]
|
||||
@ -5139,7 +5163,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "logos-blockchain-zksign"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b862e6f640a79097b8a42c072e1f78bc430fa222"
|
||||
source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290"
|
||||
dependencies = [
|
||||
"logos-blockchain-circuits-prover",
|
||||
"logos-blockchain-circuits-utils",
|
||||
@ -5451,9 +5475,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.16"
|
||||
version = "0.2.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d5d26952a508f321b4d3d2e80e78fc2603eaefcdf0c30783867f19586518bdc"
|
||||
checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
@ -5562,6 +5586,7 @@ dependencies = [
|
||||
"base58",
|
||||
"borsh",
|
||||
"bytemuck",
|
||||
"bytesize",
|
||||
"chacha20",
|
||||
"k256",
|
||||
"risc0-zkvm",
|
||||
@ -7430,9 +7455,11 @@ dependencies = [
|
||||
"base58",
|
||||
"bedrock_client",
|
||||
"borsh",
|
||||
"bytesize",
|
||||
"chrono",
|
||||
"common",
|
||||
"futures",
|
||||
"humantime-serde",
|
||||
"jsonrpsee",
|
||||
"log",
|
||||
"logos-blockchain-core",
|
||||
@ -7460,6 +7487,7 @@ dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bedrock_client",
|
||||
"borsh",
|
||||
"bytesize",
|
||||
"common",
|
||||
"futures",
|
||||
"hex",
|
||||
@ -8968,6 +8996,8 @@ dependencies = [
|
||||
"env_logger",
|
||||
"futures",
|
||||
"hex",
|
||||
"humantime",
|
||||
"humantime-serde",
|
||||
"indicatif",
|
||||
"itertools 0.14.0",
|
||||
"key_protocol",
|
||||
|
||||
@ -25,8 +25,6 @@ members = [
|
||||
"indexer/service/protocol",
|
||||
"indexer/service/rpc",
|
||||
"explorer_service",
|
||||
"programs/token/core",
|
||||
"programs/token",
|
||||
"program_methods",
|
||||
"program_methods/guest",
|
||||
"test_program_methods",
|
||||
@ -52,7 +50,7 @@ indexer_service = { path = "indexer/service" }
|
||||
indexer_service_protocol = { path = "indexer/service/protocol" }
|
||||
indexer_service_rpc = { path = "indexer/service/rpc" }
|
||||
wallet = { path = "wallet" }
|
||||
wallet-ffi = { path = "wallet-ffi" }
|
||||
wallet-ffi = { path = "wallet-ffi", default-features = false }
|
||||
token_core = { path = "programs/token/core" }
|
||||
token_program = { path = "programs/token" }
|
||||
amm_core = { path = "programs/amm/core" }
|
||||
@ -89,6 +87,9 @@ thiserror = "2.0.12"
|
||||
sha2 = "0.10.8"
|
||||
hex = "0.4.3"
|
||||
bytemuck = "1.24.0"
|
||||
bytesize = { version = "2.3.1", features = ["serde"] }
|
||||
humantime-serde = "1.1"
|
||||
humantime = "2.1"
|
||||
aes-gcm = "0.10.3"
|
||||
toml = "0.7.4"
|
||||
bincode = "1.3.3"
|
||||
@ -113,6 +114,7 @@ logos-blockchain-common-http-client = { git = "https://github.com/logos-blockcha
|
||||
logos-blockchain-key-management-system-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git" }
|
||||
logos-blockchain-core = { git = "https://github.com/logos-blockchain/logos-blockchain.git" }
|
||||
logos-blockchain-chain-broadcast-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git" }
|
||||
logos-blockchain-chain-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git" }
|
||||
|
||||
rocksdb = { version = "0.24.0", default-features = false, features = [
|
||||
"snappy",
|
||||
|
||||
44
Justfile
44
Justfile
@ -8,7 +8,7 @@ METHODS_PATH := "program_methods"
|
||||
TEST_METHODS_PATH := "test_program_methods"
|
||||
ARTIFACTS := "artifacts"
|
||||
|
||||
# ---- Artifacts build ----
|
||||
# Build risc0 program artifacts
|
||||
build-artifacts:
|
||||
@echo "🔨 Building artifacts"
|
||||
@for methods_path in {{METHODS_PATH}} {{TEST_METHODS_PATH}}; do \
|
||||
@ -17,3 +17,45 @@ build-artifacts:
|
||||
mkdir -p {{ARTIFACTS}}/$methods_path; \
|
||||
cp target/$methods_path/riscv32im-risc0-zkvm-elf/docker/*.bin {{ARTIFACTS}}/$methods_path; \
|
||||
done
|
||||
|
||||
# Run tests
|
||||
test:
|
||||
@echo "🧪 Running tests"
|
||||
RISC0_DEV_MODE=1 cargo nextest run --no-fail-fast
|
||||
|
||||
# Run Bedrock node in docker
|
||||
[working-directory: 'bedrock']
|
||||
run-bedrock:
|
||||
@echo "⛓️ Running bedrock"
|
||||
docker compose up
|
||||
|
||||
# Run Sequencer
|
||||
[working-directory: 'sequencer_runner']
|
||||
run-sequencer:
|
||||
@echo "🧠 Running sequencer"
|
||||
RUST_LOG=info RISC0_DEV_MODE=1 cargo run --release -p sequencer_runner configs/debug
|
||||
|
||||
# Run Indexer
|
||||
[working-directory: 'indexer/service']
|
||||
run-indexer:
|
||||
@echo "🔍 Running indexer"
|
||||
RUST_LOG=info RISC0_DEV_MODE=1 cargo run --release -p indexer_service configs/indexer_config.json
|
||||
|
||||
# Run Explorer
|
||||
[working-directory: 'explorer_service']
|
||||
run-explorer:
|
||||
@echo "🌐 Running explorer"
|
||||
RUST_LOG=info cargo leptos serve
|
||||
|
||||
# Run Wallet
|
||||
[working-directory: 'wallet']
|
||||
run-wallet +args:
|
||||
@echo "🔑 Running wallet"
|
||||
NSSA_WALLET_HOME_DIR=$(pwd)/configs/debug cargo run --release -p wallet -- {{args}}
|
||||
|
||||
# Clean runtime data
|
||||
clean:
|
||||
@echo "🧹 Cleaning run artifacts"
|
||||
rm -rf sequencer_runner/bedrock_signing_key
|
||||
rm -rf sequencer_runner/rocksdb
|
||||
rm -rf wallet/configs/debug/storage.json
|
||||
|
||||
46
README.md
46
README.md
@ -142,7 +142,7 @@ The sequencer and logos blockchain node can be run locally:
|
||||
- `./scripts/setup-logos-blockchain-circuits.sh`
|
||||
- `cargo build --all-features`
|
||||
- `./target/debug/logos-blockchain-node --deployment nodes/node/standalone-deployment-config.yaml nodes/node/standalone-node-config.yaml`
|
||||
|
||||
|
||||
2. Alternatively (WARNING: This node is outdated) go to ``logos-blockchain/lssa/` repo and run the node from docker:
|
||||
- `cd bedrock`
|
||||
- Change line 14 of `docker-compose.yml` from `"0:18080/tcp"` into `"8080:18080/tcp"`
|
||||
@ -157,11 +157,53 @@ The sequencer and logos blockchain node can be run locally:
|
||||
### Notes on cleanup
|
||||
|
||||
After stopping services above you need to remove 3 folders to start cleanly:
|
||||
1. In the `logos-blockchain/logos-blockchain` folder `db` (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`
|
||||
3. In the `lssa` file `sequencer_runner/bedrock_signing_key`
|
||||
4. In the `lssa` folder `indexer/service/rocksdb`
|
||||
|
||||
### Normal mode (`just` commands)
|
||||
We provide a `Justfile` for developer and user needs, you can run the whole setup with it. The only difference will be that logos-blockchain (bedrock) will be started from docker.
|
||||
|
||||
#### 1'st Terminal
|
||||
|
||||
```bash
|
||||
just run-bedrock
|
||||
```
|
||||
|
||||
#### 2'nd Terminal
|
||||
|
||||
```bash
|
||||
just run-indexer
|
||||
```
|
||||
|
||||
#### 3'rd Terminal
|
||||
|
||||
```bash
|
||||
just run-sequencer
|
||||
```
|
||||
|
||||
#### 4'th Terminal
|
||||
|
||||
```bash
|
||||
just run-explorer
|
||||
```
|
||||
|
||||
#### 5'th Terminal
|
||||
|
||||
You can run any command our wallet support by passing it as an argument for `just run-wallet`, for example:
|
||||
|
||||
```bash
|
||||
just run-wallet check-health
|
||||
```
|
||||
|
||||
This will use a wallet binary built from this repo and not the one installed in your system if you have some. Also another wallet home directory will be used. This is done to not to mess up with your local wallet and to easily clean generated files (see next section).
|
||||
|
||||
#### Shutdown
|
||||
|
||||
1. Press `ctrl-c` in every terminal
|
||||
2. Run `just clean` to clean runtime data
|
||||
|
||||
### Standalone mode
|
||||
The sequencer can be run in standalone mode with:
|
||||
```bash
|
||||
|
||||
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.
@ -10,8 +10,7 @@ services:
|
||||
logos-blockchain-node-0:
|
||||
image: ghcr.io/logos-blockchain/logos-blockchain@sha256:000982e751dfd346ca5346b8025c685fc3abc585079c59cde3bde7fd63100657
|
||||
ports:
|
||||
# Map 0 port so that multiple instances can run on the same host
|
||||
- "0:18080/tcp"
|
||||
- "${PORT:-8080}:18080/tcp"
|
||||
volumes:
|
||||
- ./scripts:/etc/logos-blockchain/scripts
|
||||
- ./kzgrs_test_params:/kzgrs_test_params:z
|
||||
|
||||
@ -13,6 +13,8 @@ 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
|
||||
|
||||
@ -3,8 +3,11 @@ 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};
|
||||
@ -14,14 +17,15 @@ use tokio_retry::Retry;
|
||||
/// Fibonacci backoff retry strategy configuration
|
||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
|
||||
pub struct BackoffConfig {
|
||||
pub start_delay_millis: u64,
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub start_delay: Duration,
|
||||
pub max_retries: usize,
|
||||
}
|
||||
|
||||
impl Default for BackoffConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
start_delay_millis: 100,
|
||||
start_delay: Duration::from_millis(100),
|
||||
max_retries: 5,
|
||||
}
|
||||
}
|
||||
@ -82,8 +86,19 @@ impl BedrockClient {
|
||||
.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_millis)
|
||||
.take(self.backoff.max_retries)
|
||||
tokio_retry::strategy::FibonacciBackoff::from_millis(
|
||||
self.backoff.start_delay.as_millis() as u64
|
||||
)
|
||||
.take(self.backoff.max_retries)
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,6 +18,7 @@ sha2.workspace = true
|
||||
log.workspace = true
|
||||
hex.workspace = true
|
||||
borsh.workspace = true
|
||||
bytesize.workspace = true
|
||||
base64.workspace = true
|
||||
url.workspace = true
|
||||
logos-blockchain-common-http-client.workspace = true
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
use bytesize::ByteSize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub mod errors;
|
||||
@ -8,13 +9,13 @@ pub mod requests;
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct RpcLimitsConfig {
|
||||
/// Maximum byte size of the json payload.
|
||||
pub json_payload_max_size: usize,
|
||||
pub json_payload_max_size: ByteSize,
|
||||
}
|
||||
|
||||
impl Default for RpcLimitsConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
json_payload_max_size: 10 * 1024 * 1024,
|
||||
json_payload_max_size: ByteSize::mib(10),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,4 +103,6 @@ pub enum TransactionMalformationError {
|
||||
InvalidSignature,
|
||||
#[error("Failed to decode transaction with hash: {tx:?}")]
|
||||
FailedToDecode { tx: HashType },
|
||||
#[error("Transaction size {size} exceeds maximum allowed size of {max} bytes")]
|
||||
TransactionTooLarge { size: usize, max: usize },
|
||||
}
|
||||
|
||||
@ -346,7 +346,7 @@ _wallet_config() {
|
||||
'all'
|
||||
'override_rust_log'
|
||||
'sequencer_addr'
|
||||
'seq_poll_timeout_millis'
|
||||
'seq_poll_timeout'
|
||||
'seq_tx_poll_max_blocks'
|
||||
'seq_poll_max_retries'
|
||||
'seq_block_poll_max_amount'
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
{
|
||||
"resubscribe_interval_millis": 1000,
|
||||
"resubscribe_interval": "1s",
|
||||
"bedrock_client_config": {
|
||||
"addr": "http://logos-blockchain-node-0:18080",
|
||||
"backoff": {
|
||||
"start_delay_millis": 100,
|
||||
"start_delay": "100ms",
|
||||
"max_retries": 5
|
||||
}
|
||||
},
|
||||
|
||||
@ -4,13 +4,14 @@
|
||||
"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_millis": 10000,
|
||||
"retry_pending_blocks_timeout_millis": 7000,
|
||||
"block_create_timeout": "10s",
|
||||
"retry_pending_blocks_timeout": "7s",
|
||||
"port": 3040,
|
||||
"bedrock_config": {
|
||||
"backoff": {
|
||||
"start_delay_millis": 100,
|
||||
"start_delay": "100ms",
|
||||
"max_retries": 5
|
||||
},
|
||||
"channel_id": "0101010101010101010101010101010101010101010101010101010101010101",
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
This guide walks you through running the sequencer, compiling example programs, deploying a Hello World program, and interacting with accounts.
|
||||
|
||||
You'll find:
|
||||
- Programs: example NSSA programs under `methods/guest/src/bin`.
|
||||
- Programs: example LEZ programs under `methods/guest/src/bin`.
|
||||
- Runners: scripts to create and submit transactions to invoke these programs publicly and privately under `src/bin`.
|
||||
|
||||
# 0. Install the wallet
|
||||
@ -13,16 +13,7 @@ cargo install --path wallet --force
|
||||
```
|
||||
|
||||
# 1. Run the sequencer
|
||||
From the project’s root directory, start the sequencer:
|
||||
```bash
|
||||
cd sequencer_runner
|
||||
RUST_LOG=info cargo run $(pwd)/configs/debug
|
||||
```
|
||||
Keep this terminal open. We’ll use it only to observe the node logs.
|
||||
|
||||
> [!NOTE]
|
||||
> If you have already ran this before you'll see a `rocksdb` directory with stored blocks. Be sure to remove that directory to follow this tutorial.
|
||||
|
||||
From the project’s root directory, start the sequencer by following [these instructions](https://github.com/logos-blockchain/lssa#run-the-sequencer-and-node).
|
||||
|
||||
## Checking and setting up the wallet
|
||||
For sanity let's check that the wallet can connect to it.
|
||||
|
||||
11
flake.nix
11
flake.nix
@ -91,11 +91,22 @@
|
||||
pkgs.pkg-config
|
||||
pkgs.clang
|
||||
pkgs.llvmPackages.libclang.lib
|
||||
pkgs.gnutar # Required for crane's archive operations (macOS tar lacks --sort)
|
||||
];
|
||||
LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib";
|
||||
# Point the risc0-circuit-recursion build script to the pre-fetched zip
|
||||
# so it doesn't try to download it inside the sandbox.
|
||||
RECURSION_SRC_PATH = "${recursionZkr}";
|
||||
# Provide a writable HOME so risc0-build-kernel can use its cache directory
|
||||
# (needed on macOS for Metal kernel compilation cache).
|
||||
# On macOS, append /usr/bin to PATH so xcrun (Metal compiler) can be found,
|
||||
# while keeping Nix tools (like gnutar) first in PATH.
|
||||
# This requires running with --option sandbox false for Metal GPU support.
|
||||
preBuild = ''
|
||||
export HOME=$(mktemp -d)
|
||||
'' + pkgs.lib.optionalString pkgs.stdenv.isDarwin ''
|
||||
export PATH="$PATH:/usr/bin"
|
||||
'';
|
||||
};
|
||||
|
||||
walletFfiPackage = craneLib.buildPackage (
|
||||
|
||||
@ -14,6 +14,7 @@ storage.workspace = true
|
||||
anyhow.workspace = true
|
||||
log.workspace = true
|
||||
serde.workspace = true
|
||||
humantime-serde.workspace = true
|
||||
tokio.workspace = true
|
||||
borsh.workspace = true
|
||||
futures.workspace = true
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
use std::{path::Path, sync::Arc};
|
||||
|
||||
use anyhow::Result;
|
||||
use bedrock_client::HeaderId;
|
||||
use common::{
|
||||
block::{BedrockStatus, Block},
|
||||
transaction::NSSATransaction,
|
||||
@ -34,6 +35,13 @@ impl IndexerStore {
|
||||
Self::open_db_with_genesis(location, None)
|
||||
}
|
||||
|
||||
pub fn last_observed_l1_lib_header(&self) -> Result<Option<HeaderId>> {
|
||||
Ok(self
|
||||
.dbio
|
||||
.get_meta_last_observed_l1_lib_header_in_db()?
|
||||
.map(HeaderId::from))
|
||||
}
|
||||
|
||||
pub fn get_last_block_id(&self) -> Result<u64> {
|
||||
Ok(self.dbio.get_meta_last_block_in_db()?)
|
||||
}
|
||||
@ -95,7 +103,7 @@ impl IndexerStore {
|
||||
Ok(self.final_state()?.get_account_by_id(*account_id))
|
||||
}
|
||||
|
||||
pub fn put_block(&self, mut block: Block) -> Result<()> {
|
||||
pub fn put_block(&self, mut block: Block, l1_header: HeaderId) -> Result<()> {
|
||||
let mut final_state = self.dbio.final_state()?;
|
||||
|
||||
for transaction in &block.body.transactions {
|
||||
@ -110,6 +118,6 @@ impl IndexerStore {
|
||||
// to represent correct block finality
|
||||
block.bedrock_status = BedrockStatus::Finalized;
|
||||
|
||||
Ok(self.dbio.put_block(block)?)
|
||||
Ok(self.dbio.put_block(block, l1_header.into())?)
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ use std::{
|
||||
fs::File,
|
||||
io::BufReader,
|
||||
path::{Path, PathBuf},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use anyhow::{Context as _, Result};
|
||||
@ -10,6 +11,7 @@ use common::{
|
||||
block::{AccountInitialData, CommitmentsInitialData},
|
||||
config::BasicAuth,
|
||||
};
|
||||
use humantime_serde;
|
||||
pub use logos_blockchain_core::mantle::ops::channel::ChannelId;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
@ -32,12 +34,9 @@ pub struct IndexerConfig {
|
||||
/// List of initial commitments
|
||||
pub initial_commitments: Vec<CommitmentsInitialData>,
|
||||
/// Sequencers signing key
|
||||
///
|
||||
/// ToDo: Remove it after introducing bedrock block parsing.
|
||||
/// Currently can not be removed, because indexer must start
|
||||
/// chain BEFORE sequencer.
|
||||
pub signing_key: [u8; 32],
|
||||
pub resubscribe_interval_millis: u64,
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub consensus_info_polling_interval: Duration,
|
||||
pub bedrock_client_config: ClientConfig,
|
||||
pub channel_id: ChannelId,
|
||||
}
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use anyhow::Result;
|
||||
use bedrock_client::BedrockClient;
|
||||
use bedrock_client::{BedrockClient, HeaderId};
|
||||
use common::block::{Block, HashableBlockData};
|
||||
// ToDo: Remove after testnet
|
||||
use common::{HashType, PINATA_BASE58};
|
||||
use futures::StreamExt;
|
||||
use log::info;
|
||||
use log::{debug, error, info};
|
||||
use logos_blockchain_core::mantle::{
|
||||
Op, SignedMantleTx,
|
||||
ops::channel::{ChannelId, inscribe::InscriptionOp},
|
||||
@ -22,9 +23,22 @@ pub struct IndexerCore {
|
||||
pub store: IndexerStore,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
/// This struct represents one L1 block data fetched from backfilling
|
||||
pub struct BackfillBlockData {
|
||||
l2_blocks: Vec<Block>,
|
||||
l1_header: HeaderId,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
/// This struct represents data fetched fom backfilling in one iteration
|
||||
pub struct BackfillData {
|
||||
block_data: VecDeque<BackfillBlockData>,
|
||||
curr_fin_l1_lib_header: HeaderId,
|
||||
}
|
||||
|
||||
impl IndexerCore {
|
||||
pub fn new(config: IndexerConfig) -> Result<Self> {
|
||||
// ToDo: replace with correct startup
|
||||
let hashable_data = HashableBlockData {
|
||||
block_id: 1,
|
||||
transactions: vec![],
|
||||
@ -32,10 +46,20 @@ impl IndexerCore {
|
||||
timestamp: 0,
|
||||
};
|
||||
|
||||
// Genesis creation is fine as it is,
|
||||
// because it will be overwritten by sequencer.
|
||||
// Therefore:
|
||||
// ToDo: remove key from indexer config, use some default.
|
||||
let signing_key = nssa::PrivateKey::try_new(config.signing_key).unwrap();
|
||||
let channel_genesis_msg_id = [0; 32];
|
||||
let start_block = hashable_data.into_pending_block(&signing_key, channel_genesis_msg_id);
|
||||
|
||||
// This is a troubling moment, because changes in key protocol can
|
||||
// affect this. And indexer can not reliably ask this data from sequencer
|
||||
// because indexer must be independent from it.
|
||||
// ToDo: move initial state generation into common and use the same method
|
||||
// for indexer and sequencer. This way both services buit at same version
|
||||
// could be in sync.
|
||||
let initial_commitments: Vec<nssa_core::Commitment> = config
|
||||
.initial_commitments
|
||||
.iter()
|
||||
@ -70,71 +94,255 @@ impl IndexerCore {
|
||||
config.bedrock_client_config.auth.clone(),
|
||||
)?,
|
||||
config,
|
||||
// ToDo: Implement restarts
|
||||
store: IndexerStore::open_db_with_genesis(&home, Some((start_block, state)))?,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn subscribe_parse_block_stream(&self) -> impl futures::Stream<Item = Result<Block>> {
|
||||
async_stream::stream! {
|
||||
loop {
|
||||
let mut stream_pinned = Box::pin(self.bedrock_client.get_lib_stream().await?);
|
||||
info!("Searching for initial header");
|
||||
|
||||
info!("Block stream joined");
|
||||
let last_l1_lib_header = self.store.last_observed_l1_lib_header()?;
|
||||
|
||||
while let Some(block_info) = stream_pinned.next().await {
|
||||
let header_id = block_info.header_id;
|
||||
let mut prev_last_l1_lib_header = match last_l1_lib_header {
|
||||
Some(last_l1_lib_header) => {
|
||||
info!("Last l1 lib header found: {last_l1_lib_header}");
|
||||
last_l1_lib_header
|
||||
},
|
||||
None => {
|
||||
info!("Last l1 lib header not found in DB");
|
||||
info!("Searching for the start of a channel");
|
||||
|
||||
info!("Observed L1 block at height {}", block_info.height);
|
||||
let BackfillData {
|
||||
block_data: start_buff,
|
||||
curr_fin_l1_lib_header: last_l1_lib_header,
|
||||
} = self.search_for_channel_start().await?;
|
||||
|
||||
if let Some(l1_block) = self
|
||||
.bedrock_client
|
||||
.get_block_by_id(header_id)
|
||||
.await?
|
||||
{
|
||||
info!("Extracted L1 block at height {}", block_info.height);
|
||||
for BackfillBlockData {
|
||||
l2_blocks: l2_block_vec,
|
||||
l1_header,
|
||||
} in start_buff {
|
||||
let mut l2_blocks_parsed_ids: Vec<_> = l2_block_vec.iter().map(|block| block.header.block_id).collect();
|
||||
l2_blocks_parsed_ids.sort();
|
||||
info!("Parsed {} L2 blocks with ids {:?}", l2_block_vec.len(), l2_blocks_parsed_ids);
|
||||
|
||||
let l2_blocks_parsed = parse_blocks(
|
||||
l1_block.into_transactions().into_iter(),
|
||||
&self.config.channel_id,
|
||||
).collect::<Vec<_>>();
|
||||
for l2_block in l2_block_vec {
|
||||
self.store.put_block(l2_block.clone(), l1_header)?;
|
||||
|
||||
let mut l2_blocks_parsed_ids: Vec<_> = l2_blocks_parsed.iter().map(|block| block.header.block_id).collect();
|
||||
yield Ok(l2_block);
|
||||
}
|
||||
}
|
||||
|
||||
last_l1_lib_header
|
||||
},
|
||||
};
|
||||
|
||||
info!("Searching for initial header finished");
|
||||
|
||||
info!("Starting backfilling from {prev_last_l1_lib_header}");
|
||||
|
||||
loop {
|
||||
let BackfillData {
|
||||
block_data: buff,
|
||||
curr_fin_l1_lib_header,
|
||||
} = self
|
||||
.backfill_to_last_l1_lib_header_id(prev_last_l1_lib_header, &self.config.channel_id)
|
||||
.await
|
||||
.inspect_err(|err| error!("Failed to backfill to last l1 lib header id with err {err:#?}"))?;
|
||||
|
||||
prev_last_l1_lib_header = curr_fin_l1_lib_header;
|
||||
|
||||
for BackfillBlockData {
|
||||
l2_blocks: l2_block_vec,
|
||||
l1_header: header,
|
||||
} in buff {
|
||||
let mut l2_blocks_parsed_ids: Vec<_> = l2_block_vec.iter().map(|block| block.header.block_id).collect();
|
||||
l2_blocks_parsed_ids.sort();
|
||||
info!("Parsed {} L2 blocks with ids {:?}", l2_blocks_parsed.len(), l2_blocks_parsed_ids);
|
||||
info!("Parsed {} L2 blocks with ids {:?}", l2_block_vec.len(), l2_blocks_parsed_ids);
|
||||
|
||||
for l2_block in l2_blocks_parsed {
|
||||
self.store.put_block(l2_block.clone())?;
|
||||
for l2_block in l2_block_vec {
|
||||
self.store.put_block(l2_block.clone(), header)?;
|
||||
|
||||
yield Ok(l2_block);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Refetch stream after delay
|
||||
tokio::time::sleep(std::time::Duration::from_millis(
|
||||
self.config.resubscribe_interval_millis,
|
||||
))
|
||||
.await;
|
||||
async fn get_lib(&self) -> Result<HeaderId> {
|
||||
Ok(self.bedrock_client.get_consensus_info().await?.lib)
|
||||
}
|
||||
|
||||
async fn get_next_lib(&self, prev_lib: HeaderId) -> Result<HeaderId> {
|
||||
loop {
|
||||
let next_lib = self.get_lib().await?;
|
||||
if next_lib != prev_lib {
|
||||
break Ok(next_lib);
|
||||
} else {
|
||||
info!(
|
||||
"Wait {:?} to not spam the node",
|
||||
self.config.consensus_info_polling_interval
|
||||
);
|
||||
tokio::time::sleep(self.config.consensus_info_polling_interval).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// WARNING: depending on channel state,
|
||||
/// may take indefinite amount of time
|
||||
pub async fn search_for_channel_start(&self) -> Result<BackfillData> {
|
||||
let mut curr_last_l1_lib_header = self.get_lib().await?;
|
||||
let mut backfill_start = curr_last_l1_lib_header;
|
||||
// ToDo: How to get root?
|
||||
let mut backfill_limit = HeaderId::from([0; 32]);
|
||||
// ToDo: Not scalable, initial buffer should be stored in DB to not run out of memory
|
||||
// Don't want to complicate DB even more right now.
|
||||
let mut block_buffer = VecDeque::new();
|
||||
|
||||
'outer: loop {
|
||||
let mut cycle_header = curr_last_l1_lib_header;
|
||||
|
||||
loop {
|
||||
let cycle_block =
|
||||
if let Some(block) = self.bedrock_client.get_block_by_id(cycle_header).await? {
|
||||
block
|
||||
} else {
|
||||
// First run can reach root easily
|
||||
// so here we are optimistic about L1
|
||||
// failing to get parent.
|
||||
break;
|
||||
};
|
||||
|
||||
// It would be better to have id, but block does not have it, so slot will do.
|
||||
info!(
|
||||
"INITIAL SEARCH: Observed L1 block at slot {}",
|
||||
cycle_block.header().slot().into_inner()
|
||||
);
|
||||
debug!(
|
||||
"INITIAL SEARCH: This block header is {}",
|
||||
cycle_block.header().id()
|
||||
);
|
||||
debug!(
|
||||
"INITIAL SEARCH: This block parent is {}",
|
||||
cycle_block.header().parent()
|
||||
);
|
||||
|
||||
let (l2_block_vec, l1_header) =
|
||||
parse_block_owned(&cycle_block, &self.config.channel_id);
|
||||
|
||||
info!("Parsed {} L2 blocks", l2_block_vec.len());
|
||||
|
||||
if !l2_block_vec.is_empty() {
|
||||
block_buffer.push_front(BackfillBlockData {
|
||||
l2_blocks: l2_block_vec.clone(),
|
||||
l1_header,
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(first_l2_block) = l2_block_vec.first()
|
||||
&& first_l2_block.header.block_id == 1
|
||||
{
|
||||
info!("INITIAL_SEARCH: Found channel start");
|
||||
break 'outer;
|
||||
}
|
||||
|
||||
// Step back to parent
|
||||
let parent = cycle_block.header().parent();
|
||||
|
||||
if parent == backfill_limit {
|
||||
break;
|
||||
}
|
||||
|
||||
cycle_header = parent;
|
||||
}
|
||||
|
||||
info!("INITIAL_SEARCH: Reached backfill limit, refetching last l1 lib header");
|
||||
|
||||
block_buffer.clear();
|
||||
backfill_limit = backfill_start;
|
||||
curr_last_l1_lib_header = self.get_next_lib(curr_last_l1_lib_header).await?;
|
||||
backfill_start = curr_last_l1_lib_header;
|
||||
}
|
||||
|
||||
Ok(BackfillData {
|
||||
block_data: block_buffer,
|
||||
curr_fin_l1_lib_header: backfill_limit,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn backfill_to_last_l1_lib_header_id(
|
||||
&self,
|
||||
last_fin_l1_lib_header: HeaderId,
|
||||
channel_id: &ChannelId,
|
||||
) -> Result<BackfillData> {
|
||||
let curr_fin_l1_lib_header = self.get_next_lib(last_fin_l1_lib_header).await?;
|
||||
// ToDo: Not scalable, buffer should be stored in DB to not run out of memory
|
||||
// Don't want to complicate DB even more right now.
|
||||
let mut block_buffer = VecDeque::new();
|
||||
|
||||
let mut cycle_header = curr_fin_l1_lib_header;
|
||||
loop {
|
||||
let Some(cycle_block) = self.bedrock_client.get_block_by_id(cycle_header).await? else {
|
||||
return Err(anyhow::anyhow!("Parent not found"));
|
||||
};
|
||||
|
||||
if cycle_block.header().id() == last_fin_l1_lib_header {
|
||||
break;
|
||||
} else {
|
||||
// Step back to parent
|
||||
cycle_header = cycle_block.header().parent();
|
||||
}
|
||||
|
||||
// It would be better to have id, but block does not have it, so slot will do.
|
||||
info!(
|
||||
"Observed L1 block at slot {}",
|
||||
cycle_block.header().slot().into_inner()
|
||||
);
|
||||
|
||||
let (l2_block_vec, l1_header) = parse_block_owned(&cycle_block, channel_id);
|
||||
|
||||
info!("Parsed {} L2 blocks", l2_block_vec.len());
|
||||
|
||||
if !l2_block_vec.is_empty() {
|
||||
block_buffer.push_front(BackfillBlockData {
|
||||
l2_blocks: l2_block_vec,
|
||||
l1_header,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(BackfillData {
|
||||
block_data: block_buffer,
|
||||
curr_fin_l1_lib_header,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_blocks(
|
||||
block_txs: impl Iterator<Item = SignedMantleTx>,
|
||||
fn parse_block_owned(
|
||||
l1_block: &bedrock_client::Block<SignedMantleTx>,
|
||||
decoded_channel_id: &ChannelId,
|
||||
) -> impl Iterator<Item = Block> {
|
||||
block_txs.flat_map(|tx| {
|
||||
tx.mantle_tx.ops.into_iter().filter_map(|op| match op {
|
||||
Op::ChannelInscribe(InscriptionOp {
|
||||
channel_id,
|
||||
inscription,
|
||||
..
|
||||
}) if channel_id == *decoded_channel_id => {
|
||||
borsh::from_slice::<Block>(&inscription).ok()
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
})
|
||||
) -> (Vec<Block>, HeaderId) {
|
||||
(
|
||||
l1_block
|
||||
.transactions()
|
||||
.flat_map(|tx| {
|
||||
tx.mantle_tx.ops.iter().filter_map(|op| match op {
|
||||
Op::ChannelInscribe(InscriptionOp {
|
||||
channel_id,
|
||||
inscription,
|
||||
..
|
||||
}) if channel_id == decoded_channel_id => {
|
||||
borsh::from_slice::<Block>(inscription)
|
||||
.inspect_err(|err| {
|
||||
error!("Failed to deserialize our inscription with err: {err:#?}")
|
||||
})
|
||||
.ok()
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
})
|
||||
.collect(),
|
||||
l1_block.header().id(),
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
{
|
||||
"home": "./indexer/service",
|
||||
"resubscribe_interval_millis": 1000,
|
||||
"consensus_info_polling_interval": "1s",
|
||||
"bedrock_client_config": {
|
||||
"addr": "http://localhost:8080",
|
||||
"backoff": {
|
||||
"start_delay_millis": 100,
|
||||
"start_delay": "100ms",
|
||||
"max_retries": 5
|
||||
}
|
||||
},
|
||||
|
||||
@ -28,5 +28,6 @@ tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
|
||||
hex.workspace = true
|
||||
tempfile.workspace = true
|
||||
borsh.workspace = true
|
||||
bytesize.workspace = true
|
||||
futures.workspace = true
|
||||
testcontainers = { version = "0.27.0", features = ["docker-compose"] }
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
use std::{net::SocketAddr, path::PathBuf};
|
||||
use std::{net::SocketAddr, path::PathBuf, time::Duration};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use bytesize::ByteSize;
|
||||
use common::block::{AccountInitialData, CommitmentsInitialData};
|
||||
use indexer_service::{BackoffConfig, ChannelId, ClientConfig, IndexerConfig};
|
||||
use key_protocol::key_management::KeyChain;
|
||||
@ -19,13 +20,13 @@ pub fn indexer_config(
|
||||
) -> Result<IndexerConfig> {
|
||||
Ok(IndexerConfig {
|
||||
home,
|
||||
resubscribe_interval_millis: 1000,
|
||||
consensus_info_polling_interval: Duration::from_secs(1),
|
||||
bedrock_client_config: ClientConfig {
|
||||
addr: addr_to_url(UrlProtocol::Http, bedrock_addr)
|
||||
.context("Failed to convert bedrock addr to URL")?,
|
||||
auth: None,
|
||||
backoff: BackoffConfig {
|
||||
start_delay_millis: 100,
|
||||
start_delay: Duration::from_millis(100),
|
||||
max_retries: 10,
|
||||
},
|
||||
},
|
||||
@ -39,16 +40,18 @@ pub fn indexer_config(
|
||||
/// Sequencer config options available for custom changes in integration tests.
|
||||
pub struct SequencerPartialConfig {
|
||||
pub max_num_tx_in_block: usize,
|
||||
pub max_block_size: ByteSize,
|
||||
pub mempool_max_size: usize,
|
||||
pub block_create_timeout_millis: u64,
|
||||
pub block_create_timeout: Duration,
|
||||
}
|
||||
|
||||
impl Default for SequencerPartialConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
max_num_tx_in_block: 20,
|
||||
max_block_size: ByteSize::mib(1),
|
||||
mempool_max_size: 10_000,
|
||||
block_create_timeout_millis: 10_000,
|
||||
block_create_timeout: Duration::from_secs(10),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -62,8 +65,9 @@ pub fn sequencer_config(
|
||||
) -> Result<SequencerConfig> {
|
||||
let SequencerPartialConfig {
|
||||
max_num_tx_in_block,
|
||||
max_block_size,
|
||||
mempool_max_size,
|
||||
block_create_timeout_millis,
|
||||
block_create_timeout,
|
||||
} = partial;
|
||||
|
||||
Ok(SequencerConfig {
|
||||
@ -72,16 +76,17 @@ pub fn sequencer_config(
|
||||
genesis_id: 1,
|
||||
is_genesis_random: true,
|
||||
max_num_tx_in_block,
|
||||
max_block_size,
|
||||
mempool_max_size,
|
||||
block_create_timeout_millis,
|
||||
retry_pending_blocks_timeout_millis: 240_000,
|
||||
block_create_timeout,
|
||||
retry_pending_blocks_timeout: Duration::from_secs(120),
|
||||
port: 0,
|
||||
initial_accounts: initial_data.sequencer_initial_accounts(),
|
||||
initial_commitments: initial_data.sequencer_initial_commitments(),
|
||||
signing_key: [37; 32],
|
||||
bedrock_config: BedrockConfig {
|
||||
backoff: BackoffConfig {
|
||||
start_delay_millis: 100,
|
||||
start_delay: Duration::from_millis(100),
|
||||
max_retries: 5,
|
||||
},
|
||||
channel_id: bedrock_channel_id(),
|
||||
@ -102,7 +107,7 @@ pub fn wallet_config(
|
||||
override_rust_log: None,
|
||||
sequencer_addr: addr_to_url(UrlProtocol::Http, sequencer_addr)
|
||||
.context("Failed to convert sequencer addr to URL")?,
|
||||
seq_poll_timeout_millis: 30_000,
|
||||
seq_poll_timeout: Duration::from_secs(30),
|
||||
seq_tx_poll_max_blocks: 15,
|
||||
seq_poll_max_retries: 10,
|
||||
seq_block_poll_max_amount: 100,
|
||||
|
||||
@ -21,6 +21,7 @@ pub mod config;
|
||||
// TODO: Remove this and control time from tests
|
||||
pub const TIME_TO_WAIT_FOR_BLOCK_SECONDS: u64 = 12;
|
||||
pub const NSSA_PROGRAM_FOR_TEST_DATA_CHANGER: &str = "data_changer.bin";
|
||||
pub const NSSA_PROGRAM_FOR_TEST_NOOP: &str = "noop.bin";
|
||||
|
||||
const BEDROCK_SERVICE_WITH_OPEN_PORT: &str = "logos-blockchain-node-0";
|
||||
const BEDROCK_SERVICE_PORT: u16 = 18080;
|
||||
@ -115,7 +116,9 @@ impl TestContext {
|
||||
|
||||
let mut compose = DockerCompose::with_auto_client(&[bedrock_compose_path])
|
||||
.await
|
||||
.context("Failed to setup docker compose for Bedrock")?;
|
||||
.context("Failed to setup docker compose for Bedrock")?
|
||||
// Setting port to 0 to avoid conflicts between parallel tests, actual port will be retrieved after container is up
|
||||
.with_env("PORT", "0");
|
||||
|
||||
async fn up_and_retrieve_port(compose: &mut DockerCompose) -> Result<u16> {
|
||||
compose
|
||||
|
||||
185
integration_tests/tests/block_size_limit.rs
Normal file
185
integration_tests/tests/block_size_limit.rs
Normal file
@ -0,0 +1,185 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::Result;
|
||||
use bytesize::ByteSize;
|
||||
use common::{block::HashableBlockData, transaction::NSSATransaction};
|
||||
use integration_tests::{
|
||||
TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, config::SequencerPartialConfig,
|
||||
};
|
||||
use nssa::program::Program;
|
||||
use tokio::test;
|
||||
|
||||
#[test]
|
||||
async fn reject_oversized_transaction() -> Result<()> {
|
||||
let ctx = TestContext::builder()
|
||||
.with_sequencer_partial_config(SequencerPartialConfig {
|
||||
max_num_tx_in_block: 100,
|
||||
max_block_size: ByteSize::mib(1),
|
||||
mempool_max_size: 1000,
|
||||
block_create_timeout: Duration::from_secs(10),
|
||||
})
|
||||
.build()
|
||||
.await?;
|
||||
|
||||
// Create a transaction that's definitely too large
|
||||
// Block size is 1 MiB (1,048,576 bytes), minus ~200 bytes for header = ~1,048,376 bytes max tx
|
||||
// Create a 1.1 MiB binary to ensure it exceeds the limit
|
||||
let oversized_binary = vec![0u8; 1100 * 1024]; // 1.1 MiB binary
|
||||
|
||||
let message = nssa::program_deployment_transaction::Message::new(oversized_binary);
|
||||
let tx = nssa::ProgramDeploymentTransaction::new(message);
|
||||
|
||||
// Try to submit the transaction and expect an error
|
||||
let result = ctx.sequencer_client().send_tx_program(tx).await;
|
||||
|
||||
assert!(
|
||||
result.is_err(),
|
||||
"Expected error when submitting oversized transaction"
|
||||
);
|
||||
|
||||
let err = result.unwrap_err();
|
||||
let err_str = format!("{:?}", err);
|
||||
|
||||
// Check if the error contains information about transaction being too large
|
||||
assert!(
|
||||
err_str.contains("TransactionTooLarge") || err_str.contains("too large"),
|
||||
"Expected TransactionTooLarge error, got: {}",
|
||||
err_str
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
async fn accept_transaction_within_limit() -> Result<()> {
|
||||
let ctx = TestContext::builder()
|
||||
.with_sequencer_partial_config(SequencerPartialConfig {
|
||||
max_num_tx_in_block: 100,
|
||||
max_block_size: ByteSize::mib(1),
|
||||
mempool_max_size: 1000,
|
||||
block_create_timeout: Duration::from_secs(10),
|
||||
})
|
||||
.build()
|
||||
.await?;
|
||||
|
||||
// Create a small program deployment that should fit
|
||||
let small_binary = vec![0u8; 1024]; // 1 KiB binary
|
||||
|
||||
let message = nssa::program_deployment_transaction::Message::new(small_binary);
|
||||
let tx = nssa::ProgramDeploymentTransaction::new(message);
|
||||
|
||||
// This should succeed
|
||||
let result = ctx.sequencer_client().send_tx_program(tx).await;
|
||||
|
||||
assert!(
|
||||
result.is_ok(),
|
||||
"Expected successful submission of small transaction, got error: {:?}",
|
||||
result.as_ref().unwrap_err()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
async fn transaction_deferred_to_next_block_when_current_full() -> Result<()> {
|
||||
let manifest_dir = env!("CARGO_MANIFEST_DIR");
|
||||
let artifacts_dir =
|
||||
std::path::PathBuf::from(manifest_dir).join("../artifacts/test_program_methods");
|
||||
|
||||
let burner_bytecode = std::fs::read(artifacts_dir.join("burner.bin"))?;
|
||||
let chain_caller_bytecode = std::fs::read(artifacts_dir.join("chain_caller.bin"))?;
|
||||
|
||||
// Calculate block size to fit only one of the two transactions, leaving some room for headers
|
||||
// (e.g., 10 KiB)
|
||||
let max_program_size = burner_bytecode.len().max(chain_caller_bytecode.len());
|
||||
let block_size = ByteSize::b((max_program_size + 10 * 1024) as u64);
|
||||
|
||||
let ctx = TestContext::builder()
|
||||
.with_sequencer_partial_config(SequencerPartialConfig {
|
||||
max_num_tx_in_block: 100,
|
||||
max_block_size: block_size,
|
||||
mempool_max_size: 1000,
|
||||
block_create_timeout: Duration::from_secs(10),
|
||||
})
|
||||
.build()
|
||||
.await?;
|
||||
|
||||
let burner_id = Program::new(burner_bytecode.clone())?.id();
|
||||
let chain_caller_id = Program::new(chain_caller_bytecode.clone())?.id();
|
||||
|
||||
let initial_block_height = ctx.sequencer_client().get_last_block().await?.last_block;
|
||||
|
||||
// Submit both program deployments
|
||||
ctx.sequencer_client()
|
||||
.send_tx_program(nssa::ProgramDeploymentTransaction::new(
|
||||
nssa::program_deployment_transaction::Message::new(burner_bytecode),
|
||||
))
|
||||
.await?;
|
||||
|
||||
ctx.sequencer_client()
|
||||
.send_tx_program(nssa::ProgramDeploymentTransaction::new(
|
||||
nssa::program_deployment_transaction::Message::new(chain_caller_bytecode),
|
||||
))
|
||||
.await?;
|
||||
|
||||
// Wait for first block
|
||||
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
|
||||
|
||||
let block1_response = ctx
|
||||
.sequencer_client()
|
||||
.get_block(initial_block_height + 1)
|
||||
.await?;
|
||||
let block1: HashableBlockData = borsh::from_slice(&block1_response.block)?;
|
||||
|
||||
// Check which program is in block 1
|
||||
let get_program_ids = |block: &HashableBlockData| -> Vec<nssa::ProgramId> {
|
||||
block
|
||||
.transactions
|
||||
.iter()
|
||||
.filter_map(|tx| {
|
||||
if let NSSATransaction::ProgramDeployment(deployment) = tx {
|
||||
let bytecode = deployment.message.clone().into_bytecode();
|
||||
Program::new(bytecode).ok().map(|p| p.id())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
|
||||
let block1_program_ids = get_program_ids(&block1);
|
||||
|
||||
// First program should be in block 1, but not both due to block size limit
|
||||
assert_eq!(
|
||||
block1_program_ids.len(),
|
||||
1,
|
||||
"Expected exactly one program deployment in block 1"
|
||||
);
|
||||
assert_eq!(
|
||||
block1_program_ids[0], burner_id,
|
||||
"Expected burner program to be deployed in block 1"
|
||||
);
|
||||
|
||||
// Wait for second block
|
||||
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
|
||||
|
||||
let block2_response = ctx
|
||||
.sequencer_client()
|
||||
.get_block(initial_block_height + 2)
|
||||
.await?;
|
||||
let block2: HashableBlockData = borsh::from_slice(&block2_response.block)?;
|
||||
let block2_program_ids = get_program_ids(&block2);
|
||||
|
||||
// The other program should be in block 2
|
||||
assert_eq!(
|
||||
block2_program_ids.len(),
|
||||
1,
|
||||
"Expected exactly one program deployment in block 2"
|
||||
);
|
||||
assert_eq!(
|
||||
block2_program_ids[0], chain_caller_id,
|
||||
"Expected chain_caller program to be deployed in block 2"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -8,22 +8,22 @@ use wallet::cli::{Command, config::ConfigSubcommand};
|
||||
async fn modify_config_field() -> Result<()> {
|
||||
let mut ctx = TestContext::new().await?;
|
||||
|
||||
let old_seq_poll_timeout_millis = ctx.wallet().config().seq_poll_timeout_millis;
|
||||
let old_seq_poll_timeout = ctx.wallet().config().seq_poll_timeout;
|
||||
|
||||
// Change config field
|
||||
let command = Command::Config(ConfigSubcommand::Set {
|
||||
key: "seq_poll_timeout_millis".to_string(),
|
||||
value: "1000".to_string(),
|
||||
key: "seq_poll_timeout".to_string(),
|
||||
value: "1s".to_string(),
|
||||
});
|
||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
|
||||
|
||||
let new_seq_poll_timeout_millis = ctx.wallet().config().seq_poll_timeout_millis;
|
||||
assert_eq!(new_seq_poll_timeout_millis, 1000);
|
||||
let new_seq_poll_timeout = ctx.wallet().config().seq_poll_timeout;
|
||||
assert_eq!(new_seq_poll_timeout, std::time::Duration::from_secs(1));
|
||||
|
||||
// Return how it was at the beginning
|
||||
let command = Command::Config(ConfigSubcommand::Set {
|
||||
key: "seq_poll_timeout_millis".to_string(),
|
||||
value: old_seq_poll_timeout_millis.to_string(),
|
||||
key: "seq_poll_timeout".to_string(),
|
||||
value: format!("{:?}", old_seq_poll_timeout),
|
||||
});
|
||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
|
||||
|
||||
|
||||
@ -12,10 +12,9 @@ use tokio::test;
|
||||
use wallet::cli::{Command, programs::native_token_transfer::AuthTransferSubcommand};
|
||||
|
||||
/// Timeout in milliseconds to reliably await for block finalization
|
||||
const L2_TO_L1_TIMEOUT_MILLIS: u64 = 300000;
|
||||
const L2_TO_L1_TIMEOUT_MILLIS: u64 = 600000;
|
||||
|
||||
#[test]
|
||||
#[ignore = "Not reliable with current bedrock node"]
|
||||
async fn indexer_test_run() -> Result<()> {
|
||||
let ctx = TestContext::new().await?;
|
||||
|
||||
@ -45,7 +44,6 @@ async fn indexer_test_run() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "Not reliable with current bedrock node"]
|
||||
async fn indexer_block_batching() -> Result<()> {
|
||||
let ctx = TestContext::new().await?;
|
||||
|
||||
@ -81,7 +79,6 @@ async fn indexer_block_batching() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "Not reliable with current bedrock node"]
|
||||
async fn indexer_state_consistency() -> Result<()> {
|
||||
let mut ctx = TestContext::new().await?;
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use anyhow::Result;
|
||||
use bytesize::ByteSize;
|
||||
use integration_tests::{
|
||||
TestContext,
|
||||
config::{InitialData, SequencerPartialConfig},
|
||||
@ -178,8 +179,9 @@ impl TpsTestManager {
|
||||
fn generate_sequencer_partial_config() -> SequencerPartialConfig {
|
||||
SequencerPartialConfig {
|
||||
max_num_tx_in_block: 300,
|
||||
max_block_size: ByteSize::mb(500),
|
||||
mempool_max_size: 10_000,
|
||||
block_create_timeout_millis: 12_000,
|
||||
block_create_timeout: Duration::from_secs(12),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,13 +2,17 @@ use tokio::sync::mpsc::{Receiver, Sender};
|
||||
|
||||
pub struct MemPool<T> {
|
||||
receiver: Receiver<T>,
|
||||
front_buffer: Vec<T>,
|
||||
}
|
||||
|
||||
impl<T> MemPool<T> {
|
||||
pub fn new(max_size: usize) -> (Self, MemPoolHandle<T>) {
|
||||
let (sender, receiver) = tokio::sync::mpsc::channel(max_size);
|
||||
|
||||
let mem_pool = Self { receiver };
|
||||
let mem_pool = Self {
|
||||
receiver,
|
||||
front_buffer: Vec::new(),
|
||||
};
|
||||
let sender = MemPoolHandle::new(sender);
|
||||
(mem_pool, sender)
|
||||
}
|
||||
@ -16,6 +20,13 @@ impl<T> MemPool<T> {
|
||||
pub fn pop(&mut self) -> Option<T> {
|
||||
use tokio::sync::mpsc::error::TryRecvError;
|
||||
|
||||
// First check if there are any items in the front buffer (LIFO)
|
||||
if let Some(item) = self.front_buffer.pop() {
|
||||
return Some(item);
|
||||
}
|
||||
|
||||
// Otherwise, try to receive from the channel (FIFO)
|
||||
|
||||
match self.receiver.try_recv() {
|
||||
Ok(item) => Some(item),
|
||||
Err(TryRecvError::Empty) => None,
|
||||
@ -24,6 +35,11 @@ impl<T> MemPool<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Push an item to the front of the mempool (will be popped first)
|
||||
pub fn push_front(&mut self, item: T) {
|
||||
self.front_buffer.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MemPoolHandle<T> {
|
||||
@ -96,4 +112,24 @@ mod tests {
|
||||
assert_eq!(pool.pop(), Some(1));
|
||||
assert_eq!(pool.pop(), Some(2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
async fn test_push_front() {
|
||||
let (mut pool, handle) = MemPool::new(10);
|
||||
|
||||
handle.push(1).await.unwrap();
|
||||
handle.push(2).await.unwrap();
|
||||
|
||||
// Push items to the front - these should be popped first
|
||||
pool.push_front(10);
|
||||
pool.push_front(20);
|
||||
|
||||
// Items pushed to front are popped in LIFO order
|
||||
assert_eq!(pool.pop(), Some(20));
|
||||
assert_eq!(pool.pop(), Some(10));
|
||||
// Original items are then popped in FIFO order
|
||||
assert_eq!(pool.pop(), Some(1));
|
||||
assert_eq!(pool.pop(), Some(2));
|
||||
assert_eq!(pool.pop(), None);
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@ serde.workspace = true
|
||||
serde_with.workspace = true
|
||||
thiserror.workspace = true
|
||||
bytemuck.workspace = true
|
||||
bytesize.workspace = true
|
||||
base58.workspace = true
|
||||
k256 = { workspace = true, optional = true }
|
||||
chacha20 = { version = "0.9", default-features = false }
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
use std::ops::Deref;
|
||||
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use bytesize::ByteSize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub const DATA_MAX_LENGTH_IN_BYTES: usize = 100 * 1024; // 100 KiB
|
||||
pub const DATA_MAX_LENGTH: ByteSize = ByteSize::kib(100);
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, BorshSerialize)]
|
||||
pub struct Data(Vec<u8>);
|
||||
@ -22,7 +23,7 @@ impl Data {
|
||||
let mut u32_bytes = [0u8; 4];
|
||||
cursor.read_exact(&mut u32_bytes)?;
|
||||
let data_length = u32::from_le_bytes(u32_bytes);
|
||||
if data_length as usize > DATA_MAX_LENGTH_IN_BYTES {
|
||||
if data_length as usize > DATA_MAX_LENGTH.as_u64() as usize {
|
||||
return Err(
|
||||
std::io::Error::new(std::io::ErrorKind::InvalidData, DataTooBigError).into(),
|
||||
);
|
||||
@ -35,7 +36,7 @@ impl Data {
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error, Clone, Copy, PartialEq, Eq)]
|
||||
#[error("data length exceeds maximum allowed length of {DATA_MAX_LENGTH_IN_BYTES} bytes")]
|
||||
#[error("data length exceeds maximum allowed length of {} bytes", DATA_MAX_LENGTH.as_u64())]
|
||||
pub struct DataTooBigError;
|
||||
|
||||
impl From<Data> for Vec<u8> {
|
||||
@ -48,7 +49,7 @@ impl TryFrom<Vec<u8>> for Data {
|
||||
type Error = DataTooBigError;
|
||||
|
||||
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
if value.len() > DATA_MAX_LENGTH_IN_BYTES {
|
||||
if value.len() > DATA_MAX_LENGTH.as_u64() as usize {
|
||||
Err(DataTooBigError)
|
||||
} else {
|
||||
Ok(Self(value))
|
||||
@ -78,7 +79,7 @@ impl<'de> Deserialize<'de> for Data {
|
||||
/// Data deserialization visitor.
|
||||
///
|
||||
/// Compared to a simple deserialization into a `Vec<u8>`, this visitor enforces
|
||||
/// early length check defined by [`DATA_MAX_LENGTH_IN_BYTES`].
|
||||
/// early length check defined by [`DATA_MAX_LENGTH`].
|
||||
struct DataVisitor;
|
||||
|
||||
impl<'de> serde::de::Visitor<'de> for DataVisitor {
|
||||
@ -88,7 +89,7 @@ impl<'de> Deserialize<'de> for Data {
|
||||
write!(
|
||||
formatter,
|
||||
"a byte array with length not exceeding {} bytes",
|
||||
DATA_MAX_LENGTH_IN_BYTES
|
||||
DATA_MAX_LENGTH.as_u64()
|
||||
)
|
||||
}
|
||||
|
||||
@ -96,11 +97,14 @@ impl<'de> Deserialize<'de> for Data {
|
||||
where
|
||||
A: serde::de::SeqAccess<'de>,
|
||||
{
|
||||
let mut vec =
|
||||
Vec::with_capacity(seq.size_hint().unwrap_or(0).min(DATA_MAX_LENGTH_IN_BYTES));
|
||||
let mut vec = Vec::with_capacity(
|
||||
seq.size_hint()
|
||||
.unwrap_or(0)
|
||||
.min(DATA_MAX_LENGTH.as_u64() as usize),
|
||||
);
|
||||
|
||||
while let Some(value) = seq.next_element()? {
|
||||
if vec.len() >= DATA_MAX_LENGTH_IN_BYTES {
|
||||
if vec.len() >= DATA_MAX_LENGTH.as_u64() as usize {
|
||||
return Err(serde::de::Error::custom(DataTooBigError));
|
||||
}
|
||||
vec.push(value);
|
||||
@ -121,7 +125,7 @@ impl BorshDeserialize for Data {
|
||||
let len = u32::deserialize_reader(reader)?;
|
||||
match len {
|
||||
0 => Ok(Self::default()),
|
||||
len if len as usize > DATA_MAX_LENGTH_IN_BYTES => Err(std::io::Error::new(
|
||||
len if len as usize > DATA_MAX_LENGTH.as_u64() as usize => Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
DataTooBigError,
|
||||
)),
|
||||
@ -140,21 +144,21 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_data_max_length_allowed() {
|
||||
let max_vec = vec![0u8; DATA_MAX_LENGTH_IN_BYTES];
|
||||
let max_vec = vec![0u8; DATA_MAX_LENGTH.as_u64() as usize];
|
||||
let result = Data::try_from(max_vec);
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_data_too_big_error() {
|
||||
let big_vec = vec![0u8; DATA_MAX_LENGTH_IN_BYTES + 1];
|
||||
let big_vec = vec![0u8; DATA_MAX_LENGTH.as_u64() as usize + 1];
|
||||
let result = Data::try_from(big_vec);
|
||||
assert!(matches!(result, Err(DataTooBigError)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_borsh_deserialize_exceeding_limit_error() {
|
||||
let too_big_data = vec![0u8; DATA_MAX_LENGTH_IN_BYTES + 1];
|
||||
let too_big_data = vec![0u8; DATA_MAX_LENGTH.as_u64() as usize + 1];
|
||||
let mut serialized = Vec::new();
|
||||
<_ as BorshSerialize>::serialize(&too_big_data, &mut serialized).unwrap();
|
||||
|
||||
@ -164,7 +168,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_json_deserialize_exceeding_limit_error() {
|
||||
let data = vec![0u8; DATA_MAX_LENGTH_IN_BYTES + 1];
|
||||
let data = vec![0u8; DATA_MAX_LENGTH.as_u64() as usize + 1];
|
||||
let json = serde_json::to_string(&data).unwrap();
|
||||
|
||||
let result: Result<Data, _> = serde_json::from_str(&json);
|
||||
|
||||
@ -21,7 +21,7 @@ fn hash_value(value: &Value) -> Node {
|
||||
}
|
||||
|
||||
#[cfg_attr(test, derive(Debug, PartialEq, Eq))]
|
||||
#[derive(BorshSerialize, BorshDeserialize)]
|
||||
#[derive(Clone, BorshSerialize, BorshDeserialize)]
|
||||
pub struct MerkleTree {
|
||||
nodes: Vec<Node>,
|
||||
capacity: usize,
|
||||
|
||||
@ -16,7 +16,7 @@ use crate::{
|
||||
|
||||
pub const MAX_NUMBER_CHAINED_CALLS: usize = 10;
|
||||
|
||||
#[derive(BorshSerialize, BorshDeserialize)]
|
||||
#[derive(Clone, BorshSerialize, BorshDeserialize)]
|
||||
#[cfg_attr(test, derive(Debug, PartialEq, Eq))]
|
||||
pub(crate) struct CommitmentSet {
|
||||
merkle_tree: MerkleTree,
|
||||
@ -64,6 +64,7 @@ impl CommitmentSet {
|
||||
}
|
||||
|
||||
#[cfg_attr(test, derive(Debug, PartialEq, Eq))]
|
||||
#[derive(Clone)]
|
||||
struct NullifierSet(BTreeSet<Nullifier>);
|
||||
|
||||
impl NullifierSet {
|
||||
@ -104,7 +105,7 @@ impl BorshDeserialize for NullifierSet {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(BorshSerialize, BorshDeserialize)]
|
||||
#[derive(Clone, BorshSerialize, BorshDeserialize)]
|
||||
#[cfg_attr(test, derive(Debug, PartialEq, Eq))]
|
||||
pub struct V02State {
|
||||
public_state: HashMap<AccountId, Account>,
|
||||
@ -1331,7 +1332,8 @@ pub mod tests {
|
||||
AccountId::new([0; 32]),
|
||||
);
|
||||
|
||||
let large_data: Vec<u8> = vec![0; nssa_core::account::data::DATA_MAX_LENGTH_IN_BYTES + 1];
|
||||
let large_data: Vec<u8> =
|
||||
vec![0; nssa_core::account::data::DATA_MAX_LENGTH.as_u64() as usize + 1];
|
||||
|
||||
let result = execute_and_prove(
|
||||
vec![public_account],
|
||||
|
||||
@ -16,6 +16,7 @@ base58.workspace = true
|
||||
anyhow.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
humantime-serde.workspace = true
|
||||
tempfile.workspace = true
|
||||
chrono.workspace = true
|
||||
log.workspace = true
|
||||
@ -24,6 +25,7 @@ logos-blockchain-key-management-system-service.workspace = true
|
||||
logos-blockchain-core.workspace = true
|
||||
rand.workspace = true
|
||||
borsh.workspace = true
|
||||
bytesize.workspace = true
|
||||
url.workspace = true
|
||||
jsonrpsee = { workspace = true, features = ["ws-client"] }
|
||||
|
||||
|
||||
@ -2,14 +2,17 @@ use std::{
|
||||
fs::File,
|
||||
io::BufReader,
|
||||
path::{Path, PathBuf},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use anyhow::Result;
|
||||
use bedrock_client::BackoffConfig;
|
||||
use bytesize::ByteSize;
|
||||
use common::{
|
||||
block::{AccountInitialData, CommitmentsInitialData},
|
||||
config::BasicAuth,
|
||||
};
|
||||
use humantime_serde;
|
||||
use logos_blockchain_core::mantle::ops::channel::ChannelId;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
@ -27,12 +30,17 @@ pub struct SequencerConfig {
|
||||
pub is_genesis_random: bool,
|
||||
/// Maximum number of transactions in block
|
||||
pub max_num_tx_in_block: usize,
|
||||
/// Maximum block size (includes header and transactions)
|
||||
#[serde(default = "default_max_block_size")]
|
||||
pub max_block_size: ByteSize,
|
||||
/// Mempool maximum size
|
||||
pub mempool_max_size: usize,
|
||||
/// Interval in which blocks produced
|
||||
pub block_create_timeout_millis: u64,
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub block_create_timeout: Duration,
|
||||
/// Interval in which pending blocks are retried
|
||||
pub retry_pending_blocks_timeout_millis: u64,
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub retry_pending_blocks_timeout: Duration,
|
||||
/// Port to listen
|
||||
pub port: u16,
|
||||
/// List of initial accounts data
|
||||
@ -68,3 +76,7 @@ impl SequencerConfig {
|
||||
Ok(serde_json::from_reader(reader)?)
|
||||
}
|
||||
}
|
||||
|
||||
fn default_max_block_size() -> ByteSize {
|
||||
ByteSize::mib(1)
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use std::{fmt::Display, path::Path, time::Instant};
|
||||
use std::{path::Path, time::Instant};
|
||||
|
||||
use anyhow::{Context as _, Result, anyhow};
|
||||
use bedrock_client::SignedMantleTx;
|
||||
@ -13,7 +13,6 @@ use config::SequencerConfig;
|
||||
use log::{error, info, warn};
|
||||
use logos_blockchain_key_management_system_service::keys::{ED25519_SECRET_KEY_SIZE, Ed25519Key};
|
||||
use mempool::{MemPool, MemPoolHandle};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
block_settlement_client::{BlockSettlementClient, BlockSettlementClientTrait, MsgId},
|
||||
@ -44,20 +43,6 @@ pub struct SequencerCore<
|
||||
indexer_client: IC,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub enum TransactionMalformationError {
|
||||
InvalidSignature,
|
||||
FailedToDecode { tx: HashType },
|
||||
}
|
||||
|
||||
impl Display for TransactionMalformationError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{self:#?}")
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for TransactionMalformationError {}
|
||||
|
||||
impl<BC: BlockSettlementClientTrait, IC: IndexerClientTrait> SequencerCore<BC, IC> {
|
||||
/// Starts the sequencer using the provided configuration.
|
||||
/// If an existing database is found, the sequencer state is loaded from it and
|
||||
@ -204,13 +189,49 @@ impl<BC: BlockSettlementClientTrait, IC: IndexerClientTrait> SequencerCore<BC, I
|
||||
|
||||
let mut valid_transactions = vec![];
|
||||
|
||||
let max_block_size = self.sequencer_config.max_block_size.as_u64() as usize;
|
||||
|
||||
let latest_block_meta = self
|
||||
.store
|
||||
.latest_block_meta()
|
||||
.context("Failed to get latest block meta from store")?;
|
||||
|
||||
let curr_time = chrono::Utc::now().timestamp_millis() as u64;
|
||||
|
||||
while let Some(tx) = self.mempool.pop() {
|
||||
let tx_hash = tx.hash();
|
||||
|
||||
// Check if block size exceeds limit
|
||||
let temp_valid_transactions =
|
||||
[valid_transactions.as_slice(), std::slice::from_ref(&tx)].concat();
|
||||
let temp_hashable_data = HashableBlockData {
|
||||
block_id: new_block_height,
|
||||
transactions: temp_valid_transactions,
|
||||
prev_block_hash: latest_block_meta.hash,
|
||||
timestamp: curr_time,
|
||||
};
|
||||
|
||||
let block_size = borsh::to_vec(&temp_hashable_data)
|
||||
.context("Failed to serialize block for size check")?
|
||||
.len();
|
||||
|
||||
if block_size > max_block_size {
|
||||
// Block would exceed size limit, remove last transaction and push back
|
||||
warn!(
|
||||
"Transaction with hash {tx_hash} deferred to next block: \
|
||||
block size {block_size} bytes would exceed limit of {max_block_size} bytes",
|
||||
);
|
||||
|
||||
self.mempool.push_front(tx);
|
||||
break;
|
||||
}
|
||||
|
||||
match self.execute_check_transaction_on_state(tx) {
|
||||
Ok(valid_tx) => {
|
||||
info!("Validated transaction with hash {tx_hash}, including it in block",);
|
||||
valid_transactions.push(valid_tx);
|
||||
|
||||
info!("Validated transaction with hash {tx_hash}, including it in block");
|
||||
|
||||
if valid_transactions.len() >= self.sequencer_config.max_num_tx_in_block {
|
||||
break;
|
||||
}
|
||||
@ -224,13 +245,6 @@ impl<BC: BlockSettlementClientTrait, IC: IndexerClientTrait> SequencerCore<BC, I
|
||||
}
|
||||
}
|
||||
|
||||
let latest_block_meta = self
|
||||
.store
|
||||
.latest_block_meta()
|
||||
.context("Failed to get latest block meta from store")?;
|
||||
|
||||
let curr_time = chrono::Utc::now().timestamp_millis() as u64;
|
||||
|
||||
let hashable_data = HashableBlockData {
|
||||
block_id: new_block_height,
|
||||
transactions: valid_transactions,
|
||||
@ -346,7 +360,7 @@ fn load_or_create_signing_key(path: &Path) -> Result<Ed25519Key> {
|
||||
|
||||
#[cfg(all(test, feature = "mock"))]
|
||||
mod tests {
|
||||
use std::{pin::pin, str::FromStr as _};
|
||||
use std::{pin::pin, str::FromStr as _, time::Duration};
|
||||
|
||||
use base58::ToBase58;
|
||||
use bedrock_client::BackoffConfig;
|
||||
@ -375,22 +389,23 @@ mod tests {
|
||||
genesis_id: 1,
|
||||
is_genesis_random: false,
|
||||
max_num_tx_in_block: 10,
|
||||
max_block_size: bytesize::ByteSize::mib(1),
|
||||
mempool_max_size: 10000,
|
||||
block_create_timeout_millis: 1000,
|
||||
block_create_timeout: Duration::from_secs(1),
|
||||
port: 8080,
|
||||
initial_accounts,
|
||||
initial_commitments: vec![],
|
||||
signing_key: *sequencer_sign_key_for_testing().value(),
|
||||
bedrock_config: BedrockConfig {
|
||||
backoff: BackoffConfig {
|
||||
start_delay_millis: 100,
|
||||
start_delay: Duration::from_millis(100),
|
||||
max_retries: 5,
|
||||
},
|
||||
channel_id: ChannelId::from([0; 32]),
|
||||
node_url: "http://not-used-in-unit-tests".parse().unwrap(),
|
||||
auth: None,
|
||||
},
|
||||
retry_pending_blocks_timeout_millis: 1000 * 60 * 4,
|
||||
retry_pending_blocks_timeout: Duration::from_secs(60 * 4),
|
||||
indexer_rpc_url: "ws://localhost:8779".parse().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,6 +25,7 @@ itertools.workspace = true
|
||||
actix-web.workspace = true
|
||||
tokio.workspace = true
|
||||
borsh.workspace = true
|
||||
bytesize.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
sequencer_core = { workspace = true, features = ["mock"] }
|
||||
|
||||
@ -28,6 +28,7 @@ pub struct JsonHandler<
|
||||
> {
|
||||
sequencer_state: Arc<Mutex<SequencerCore<BC, IC>>>,
|
||||
mempool_handle: MemPoolHandle<NSSATransaction>,
|
||||
max_block_size: usize,
|
||||
}
|
||||
|
||||
fn respond<T: Serialize>(val: T) -> Result<Value, RpcErr> {
|
||||
|
||||
@ -52,7 +52,7 @@ fn get_cors(cors_allowed_origins: &[String]) -> Cors {
|
||||
.max_age(3600)
|
||||
}
|
||||
|
||||
pub fn new_http_server(
|
||||
pub async fn new_http_server(
|
||||
config: RpcConfig,
|
||||
seuquencer_core: Arc<Mutex<SequencerCore>>,
|
||||
mempool_handle: MemPoolHandle<NSSATransaction>,
|
||||
@ -63,9 +63,16 @@ pub fn new_http_server(
|
||||
limits_config,
|
||||
} = config;
|
||||
info!(target:NETWORK, "Starting HTTP server at {addr}");
|
||||
let max_block_size = seuquencer_core
|
||||
.lock()
|
||||
.await
|
||||
.sequencer_config()
|
||||
.max_block_size
|
||||
.as_u64() as usize;
|
||||
let handler = web::Data::new(JsonHandler {
|
||||
sequencer_state: seuquencer_core.clone(),
|
||||
mempool_handle,
|
||||
max_block_size,
|
||||
});
|
||||
|
||||
// HTTP server
|
||||
@ -73,7 +80,10 @@ pub fn new_http_server(
|
||||
App::new()
|
||||
.wrap(get_cors(&cors_allowed_origins))
|
||||
.app_data(handler.clone())
|
||||
.app_data(web::JsonConfig::default().limit(limits_config.json_payload_max_size))
|
||||
.app_data(
|
||||
web::JsonConfig::default()
|
||||
.limit(limits_config.json_payload_max_size.as_u64() as usize),
|
||||
)
|
||||
.wrap(middleware::Logger::default())
|
||||
.service(web::resource("/").route(web::post().to(rpc_handler::<JsonHandler>)))
|
||||
})
|
||||
|
||||
@ -20,7 +20,7 @@ use common::{
|
||||
SendTxResponse,
|
||||
},
|
||||
},
|
||||
transaction::NSSATransaction,
|
||||
transaction::{NSSATransaction, TransactionMalformationError},
|
||||
};
|
||||
use itertools::Itertools as _;
|
||||
use log::warn;
|
||||
@ -94,6 +94,23 @@ impl<BC: BlockSettlementClientTrait, IC: IndexerClientTrait> JsonHandler<BC, IC>
|
||||
let tx = borsh::from_slice::<NSSATransaction>(&send_tx_req.transaction).unwrap();
|
||||
let tx_hash = tx.hash();
|
||||
|
||||
// Check transaction size against block size limit
|
||||
// Reserve ~200 bytes for block header overhead
|
||||
const BLOCK_HEADER_OVERHEAD: usize = 200;
|
||||
let tx_size = borsh::to_vec(&tx)
|
||||
.map_err(|_| TransactionMalformationError::FailedToDecode { tx: tx_hash })?
|
||||
.len();
|
||||
|
||||
let max_tx_size = self.max_block_size.saturating_sub(BLOCK_HEADER_OVERHEAD);
|
||||
|
||||
if tx_size > max_tx_size {
|
||||
return Err(TransactionMalformationError::TransactionTooLarge {
|
||||
size: tx_size,
|
||||
max: max_tx_size,
|
||||
}
|
||||
.into());
|
||||
}
|
||||
|
||||
let authenticated_tx = tx
|
||||
.transaction_stateless_check()
|
||||
.inspect_err(|err| warn!("Error at pre_check {err:#?}"))?;
|
||||
@ -323,7 +340,7 @@ impl<BC: BlockSettlementClientTrait, IC: IndexerClientTrait> JsonHandler<BC, IC>
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{str::FromStr as _, sync::Arc};
|
||||
use std::{str::FromStr as _, sync::Arc, time::Duration};
|
||||
|
||||
use base58::ToBase58;
|
||||
use base64::{Engine, engine::general_purpose};
|
||||
@ -377,16 +394,17 @@ mod tests {
|
||||
genesis_id: 1,
|
||||
is_genesis_random: false,
|
||||
max_num_tx_in_block: 10,
|
||||
max_block_size: bytesize::ByteSize::mib(1),
|
||||
mempool_max_size: 1000,
|
||||
block_create_timeout_millis: 1000,
|
||||
block_create_timeout: Duration::from_secs(1),
|
||||
port: 8080,
|
||||
initial_accounts,
|
||||
initial_commitments: vec![],
|
||||
signing_key: *sequencer_sign_key_for_testing().value(),
|
||||
retry_pending_blocks_timeout_millis: 1000 * 60 * 4,
|
||||
retry_pending_blocks_timeout: Duration::from_secs(60 * 4),
|
||||
bedrock_config: BedrockConfig {
|
||||
backoff: BackoffConfig {
|
||||
start_delay_millis: 100,
|
||||
start_delay: Duration::from_millis(100),
|
||||
max_retries: 5,
|
||||
},
|
||||
channel_id: [42; 32].into(),
|
||||
@ -437,12 +455,14 @@ mod tests {
|
||||
.produce_new_block_with_mempool_transactions()
|
||||
.unwrap();
|
||||
|
||||
let max_block_size = sequencer_core.sequencer_config().max_block_size.as_u64() as usize;
|
||||
let sequencer_core = Arc::new(Mutex::new(sequencer_core));
|
||||
|
||||
(
|
||||
JsonHandlerWithMockClients {
|
||||
sequencer_state: sequencer_core,
|
||||
mempool_handle,
|
||||
max_block_size,
|
||||
},
|
||||
initial_accounts,
|
||||
tx,
|
||||
|
||||
@ -44,10 +44,7 @@ impl RpcErrKind for RpcErrInternal {
|
||||
|
||||
impl RpcErrKind for TransactionMalformationError {
|
||||
fn into_rpc_err(self) -> RpcError {
|
||||
RpcError::new_internal_error(
|
||||
Some(serde_json::to_value(self).unwrap()),
|
||||
"transaction not accepted",
|
||||
)
|
||||
RpcError::invalid_params(Some(serde_json::to_value(self).unwrap()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -4,13 +4,14 @@
|
||||
"genesis_id": 1,
|
||||
"is_genesis_random": true,
|
||||
"max_num_tx_in_block": 20,
|
||||
"max_block_size": "1 MiB",
|
||||
"mempool_max_size": 1000,
|
||||
"block_create_timeout_millis": 15000,
|
||||
"retry_pending_blocks_timeout_millis": 5000,
|
||||
"block_create_timeout": "15s",
|
||||
"retry_pending_blocks_timeout": "5s",
|
||||
"port": 3040,
|
||||
"bedrock_config": {
|
||||
"backoff": {
|
||||
"start_delay_millis": 100,
|
||||
"start_delay": "100ms",
|
||||
"max_retries": 5
|
||||
},
|
||||
"channel_id": "0101010101010101010101010101010101010101010101010101010101010101",
|
||||
|
||||
@ -4,13 +4,14 @@
|
||||
"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_millis": 10000,
|
||||
"block_create_timeout": "10s",
|
||||
"port": 3040,
|
||||
"retry_pending_blocks_timeout_millis": 7000,
|
||||
"retry_pending_blocks_timeout": "7s",
|
||||
"bedrock_config": {
|
||||
"backoff": {
|
||||
"start_delay_millis": 100,
|
||||
"start_delay": "100ms",
|
||||
"max_retries": 5
|
||||
},
|
||||
"channel_id": "0101010101010101010101010101010101010101010101010101010101010101",
|
||||
|
||||
@ -99,9 +99,8 @@ impl Drop for SequencerHandle {
|
||||
}
|
||||
|
||||
pub async fn startup_sequencer(app_config: SequencerConfig) -> Result<SequencerHandle> {
|
||||
let block_timeout = Duration::from_millis(app_config.block_create_timeout_millis);
|
||||
let retry_pending_blocks_timeout =
|
||||
Duration::from_millis(app_config.retry_pending_blocks_timeout_millis);
|
||||
let block_timeout = app_config.block_create_timeout;
|
||||
let retry_pending_blocks_timeout = app_config.retry_pending_blocks_timeout;
|
||||
let port = app_config.port;
|
||||
|
||||
let (sequencer_core, mempool_handle) = SequencerCore::start_from_config(app_config).await;
|
||||
@ -114,7 +113,8 @@ pub async fn startup_sequencer(app_config: SequencerConfig) -> Result<SequencerH
|
||||
RpcConfig::with_port(port),
|
||||
Arc::clone(&seq_core_wrapped),
|
||||
mempool_handle,
|
||||
)?;
|
||||
)
|
||||
.await?;
|
||||
info!("HTTP server started");
|
||||
let http_server_handle = http_server.handle();
|
||||
tokio::spawn(http_server);
|
||||
|
||||
@ -24,6 +24,9 @@ pub const CACHE_SIZE: usize = 1000;
|
||||
pub const DB_META_FIRST_BLOCK_IN_DB_KEY: &str = "first_block_in_db";
|
||||
/// Key base for storing metainformation about id of last current block in db
|
||||
pub const DB_META_LAST_BLOCK_IN_DB_KEY: &str = "last_block_in_db";
|
||||
/// Key base for storing metainformation about id of last observed L1 lib header in db
|
||||
pub const DB_META_LAST_OBSERVED_L1_LIB_HEADER_ID_IN_DB_KEY: &str =
|
||||
"last_observed_l1_lib_header_in_db";
|
||||
/// Key base for storing metainformation which describe if first block has been set
|
||||
pub const DB_META_FIRST_BLOCK_SET_KEY: &str = "first_block_set";
|
||||
/// Key base for storing metainformation about the last breakpoint
|
||||
@ -214,6 +217,37 @@ impl RocksDBIO {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_meta_last_observed_l1_lib_header_in_db(&self) -> DbResult<Option<[u8; 32]>> {
|
||||
let cf_meta = self.meta_column();
|
||||
let res = self
|
||||
.db
|
||||
.get_cf(
|
||||
&cf_meta,
|
||||
borsh::to_vec(&DB_META_LAST_OBSERVED_L1_LIB_HEADER_ID_IN_DB_KEY).map_err(
|
||||
|err| {
|
||||
DbError::borsh_cast_message(
|
||||
err,
|
||||
Some(
|
||||
"Failed to serialize DB_META_LAST_OBSERVED_L1_LIB_HEADER_ID_IN_DB_KEY"
|
||||
.to_string(),
|
||||
),
|
||||
)
|
||||
},
|
||||
)?,
|
||||
)
|
||||
.map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?;
|
||||
|
||||
res.map(|data| {
|
||||
borsh::from_slice::<[u8; 32]>(&data).map_err(|err| {
|
||||
DbError::borsh_cast_message(
|
||||
err,
|
||||
Some("Failed to deserialize last l1 lib header".to_string()),
|
||||
)
|
||||
})
|
||||
})
|
||||
.transpose()
|
||||
}
|
||||
|
||||
pub fn get_meta_is_first_block_set(&self) -> DbResult<bool> {
|
||||
let cf_meta = self.meta_column();
|
||||
let res = self
|
||||
@ -281,7 +315,7 @@ impl RocksDBIO {
|
||||
)
|
||||
.map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?;
|
||||
|
||||
self.put_block(block)?;
|
||||
self.put_block(block, [0; 32])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -307,6 +341,36 @@ impl RocksDBIO {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn put_meta_last_observed_l1_lib_header_in_db(
|
||||
&self,
|
||||
l1_lib_header: [u8; 32],
|
||||
) -> DbResult<()> {
|
||||
let cf_meta = self.meta_column();
|
||||
self.db
|
||||
.put_cf(
|
||||
&cf_meta,
|
||||
borsh::to_vec(&DB_META_LAST_OBSERVED_L1_LIB_HEADER_ID_IN_DB_KEY).map_err(
|
||||
|err| {
|
||||
DbError::borsh_cast_message(
|
||||
err,
|
||||
Some(
|
||||
"Failed to serialize DB_META_LAST_OBSERVED_L1_LIB_HEADER_ID_IN_DB_KEY"
|
||||
.to_string(),
|
||||
),
|
||||
)
|
||||
},
|
||||
)?,
|
||||
borsh::to_vec(&l1_lib_header).map_err(|err| {
|
||||
DbError::borsh_cast_message(
|
||||
err,
|
||||
Some("Failed to serialize last l1 block header".to_string()),
|
||||
)
|
||||
})?,
|
||||
)
|
||||
.map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn put_meta_last_breakpoint_id(&self, br_id: u64) -> DbResult<()> {
|
||||
let cf_meta = self.meta_column();
|
||||
self.db
|
||||
@ -348,7 +412,7 @@ impl RocksDBIO {
|
||||
|
||||
// Block
|
||||
|
||||
pub fn put_block(&self, block: Block) -> DbResult<()> {
|
||||
pub fn put_block(&self, block: Block, l1_lib_header: [u8; 32]) -> DbResult<()> {
|
||||
let cf_block = self.block_column();
|
||||
let cf_hti = self.hash_to_id_column();
|
||||
let cf_tti: Arc<BoundColumnFamily<'_>> = self.tx_hash_to_id_column();
|
||||
@ -377,6 +441,7 @@ impl RocksDBIO {
|
||||
|
||||
if block.header.block_id > last_curr_block {
|
||||
self.put_meta_last_block_in_db(block.header.block_id)?;
|
||||
self.put_meta_last_observed_l1_lib_header_in_db(l1_lib_header)?;
|
||||
}
|
||||
|
||||
self.db
|
||||
@ -954,7 +1019,7 @@ mod tests {
|
||||
let transfer_tx = transfer(1, 0, true);
|
||||
let block = common::test_utils::produce_dummy_block(2, Some(prev_hash), vec![transfer_tx]);
|
||||
|
||||
dbio.put_block(block).unwrap();
|
||||
dbio.put_block(block, [1; 32]).unwrap();
|
||||
|
||||
let last_id = dbio.get_meta_last_block_in_db().unwrap();
|
||||
let first_id = dbio.get_meta_first_block_in_db().unwrap();
|
||||
@ -997,7 +1062,7 @@ mod tests {
|
||||
let transfer_tx = transfer(1, (i - 1) as u128, true);
|
||||
let block =
|
||||
common::test_utils::produce_dummy_block(i + 1, Some(prev_hash), vec![transfer_tx]);
|
||||
dbio.put_block(block).unwrap();
|
||||
dbio.put_block(block, [i as u8; 32]).unwrap();
|
||||
}
|
||||
|
||||
let last_id = dbio.get_meta_last_block_in_db().unwrap();
|
||||
@ -1051,7 +1116,7 @@ mod tests {
|
||||
|
||||
let control_hash1 = block.header.hash;
|
||||
|
||||
dbio.put_block(block).unwrap();
|
||||
dbio.put_block(block, [1; 32]).unwrap();
|
||||
|
||||
let last_id = dbio.get_meta_last_block_in_db().unwrap();
|
||||
let last_block = dbio.get_block(last_id).unwrap();
|
||||
@ -1062,7 +1127,7 @@ mod tests {
|
||||
|
||||
let control_hash2 = block.header.hash;
|
||||
|
||||
dbio.put_block(block).unwrap();
|
||||
dbio.put_block(block, [2; 32]).unwrap();
|
||||
|
||||
let last_id = dbio.get_meta_last_block_in_db().unwrap();
|
||||
let last_block = dbio.get_block(last_id).unwrap();
|
||||
@ -1073,7 +1138,7 @@ mod tests {
|
||||
let control_tx_hash1 = transfer_tx.hash();
|
||||
|
||||
let block = common::test_utils::produce_dummy_block(4, Some(prev_hash), vec![transfer_tx]);
|
||||
dbio.put_block(block).unwrap();
|
||||
dbio.put_block(block, [3; 32]).unwrap();
|
||||
|
||||
let last_id = dbio.get_meta_last_block_in_db().unwrap();
|
||||
let last_block = dbio.get_block(last_id).unwrap();
|
||||
@ -1084,7 +1149,7 @@ mod tests {
|
||||
let control_tx_hash2 = transfer_tx.hash();
|
||||
|
||||
let block = common::test_utils::produce_dummy_block(5, Some(prev_hash), vec![transfer_tx]);
|
||||
dbio.put_block(block).unwrap();
|
||||
dbio.put_block(block, [4; 32]).unwrap();
|
||||
|
||||
let control_block_id1 = dbio.get_block_id_by_hash(control_hash1.0).unwrap();
|
||||
let control_block_id2 = dbio.get_block_id_by_hash(control_hash2.0).unwrap();
|
||||
@ -1115,7 +1180,7 @@ mod tests {
|
||||
let block = common::test_utils::produce_dummy_block(2, Some(prev_hash), vec![transfer_tx]);
|
||||
|
||||
block_res.push(block.clone());
|
||||
dbio.put_block(block).unwrap();
|
||||
dbio.put_block(block, [1; 32]).unwrap();
|
||||
|
||||
let last_id = dbio.get_meta_last_block_in_db().unwrap();
|
||||
let last_block = dbio.get_block(last_id).unwrap();
|
||||
@ -1125,7 +1190,7 @@ mod tests {
|
||||
let block = common::test_utils::produce_dummy_block(3, Some(prev_hash), vec![transfer_tx]);
|
||||
|
||||
block_res.push(block.clone());
|
||||
dbio.put_block(block).unwrap();
|
||||
dbio.put_block(block, [2; 32]).unwrap();
|
||||
|
||||
let last_id = dbio.get_meta_last_block_in_db().unwrap();
|
||||
let last_block = dbio.get_block(last_id).unwrap();
|
||||
@ -1135,7 +1200,7 @@ mod tests {
|
||||
|
||||
let block = common::test_utils::produce_dummy_block(4, Some(prev_hash), vec![transfer_tx]);
|
||||
block_res.push(block.clone());
|
||||
dbio.put_block(block).unwrap();
|
||||
dbio.put_block(block, [3; 32]).unwrap();
|
||||
|
||||
let last_id = dbio.get_meta_last_block_in_db().unwrap();
|
||||
let last_block = dbio.get_block(last_id).unwrap();
|
||||
@ -1145,7 +1210,7 @@ mod tests {
|
||||
|
||||
let block = common::test_utils::produce_dummy_block(5, Some(prev_hash), vec![transfer_tx]);
|
||||
block_res.push(block.clone());
|
||||
dbio.put_block(block).unwrap();
|
||||
dbio.put_block(block, [4; 32]).unwrap();
|
||||
|
||||
let block_hashes_mem: Vec<[u8; 32]> =
|
||||
block_res.into_iter().map(|bl| bl.header.hash.0).collect();
|
||||
@ -1189,7 +1254,7 @@ mod tests {
|
||||
|
||||
let block = common::test_utils::produce_dummy_block(2, Some(prev_hash), vec![transfer_tx]);
|
||||
|
||||
dbio.put_block(block).unwrap();
|
||||
dbio.put_block(block, [1; 32]).unwrap();
|
||||
|
||||
let last_id = dbio.get_meta_last_block_in_db().unwrap();
|
||||
let last_block = dbio.get_block(last_id).unwrap();
|
||||
@ -1201,7 +1266,7 @@ mod tests {
|
||||
|
||||
let block = common::test_utils::produce_dummy_block(3, Some(prev_hash), vec![transfer_tx]);
|
||||
|
||||
dbio.put_block(block).unwrap();
|
||||
dbio.put_block(block, [2; 32]).unwrap();
|
||||
|
||||
let last_id = dbio.get_meta_last_block_in_db().unwrap();
|
||||
let last_block = dbio.get_block(last_id).unwrap();
|
||||
@ -1213,7 +1278,7 @@ mod tests {
|
||||
|
||||
let block = common::test_utils::produce_dummy_block(4, Some(prev_hash), vec![transfer_tx]);
|
||||
|
||||
dbio.put_block(block).unwrap();
|
||||
dbio.put_block(block, [3; 32]).unwrap();
|
||||
|
||||
let last_id = dbio.get_meta_last_block_in_db().unwrap();
|
||||
let last_block = dbio.get_block(last_id).unwrap();
|
||||
@ -1225,7 +1290,7 @@ mod tests {
|
||||
|
||||
let block = common::test_utils::produce_dummy_block(5, Some(prev_hash), vec![transfer_tx]);
|
||||
|
||||
dbio.put_block(block).unwrap();
|
||||
dbio.put_block(block, [4; 32]).unwrap();
|
||||
|
||||
let acc1_tx = dbio.get_acc_transactions(*acc1().value(), 0, 4).unwrap();
|
||||
let acc1_tx_hashes: Vec<[u8; 32]> = acc1_tx.into_iter().map(|tx| tx.hash().0).collect();
|
||||
|
||||
@ -17,6 +17,8 @@ serde_json.workspace = true
|
||||
env_logger.workspace = true
|
||||
log.workspace = true
|
||||
serde.workspace = true
|
||||
humantime-serde.workspace = true
|
||||
humantime.workspace = true
|
||||
tokio = { workspace = true, features = ["macros"] }
|
||||
clap.workspace = true
|
||||
base64.workspace = true
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"override_rust_log": null,
|
||||
"sequencer_addr": "http://127.0.0.1:3040",
|
||||
"seq_poll_timeout_millis": 30000,
|
||||
"seq_poll_timeout": "30s",
|
||||
"seq_tx_poll_max_blocks": 15,
|
||||
"seq_poll_max_retries": 10,
|
||||
"seq_block_poll_max_amount": 100,
|
||||
|
||||
@ -262,7 +262,7 @@ mod tests {
|
||||
WalletConfig {
|
||||
override_rust_log: None,
|
||||
sequencer_addr: "http://127.0.0.1".parse().unwrap(),
|
||||
seq_poll_timeout_millis: 12000,
|
||||
seq_poll_timeout: std::time::Duration::from_secs(12),
|
||||
seq_tx_poll_max_blocks: 5,
|
||||
seq_poll_max_retries: 10,
|
||||
seq_block_poll_max_amount: 100,
|
||||
|
||||
@ -49,11 +49,8 @@ impl WalletSubcommand for ConfigSubcommand {
|
||||
"sequencer_addr" => {
|
||||
println!("{}", wallet_core.storage.wallet_config.sequencer_addr);
|
||||
}
|
||||
"seq_poll_timeout_millis" => {
|
||||
println!(
|
||||
"{}",
|
||||
wallet_core.storage.wallet_config.seq_poll_timeout_millis
|
||||
);
|
||||
"seq_poll_timeout" => {
|
||||
println!("{:?}", wallet_core.storage.wallet_config.seq_poll_timeout);
|
||||
}
|
||||
"seq_tx_poll_max_blocks" => {
|
||||
println!(
|
||||
@ -97,9 +94,10 @@ impl WalletSubcommand for ConfigSubcommand {
|
||||
"sequencer_addr" => {
|
||||
wallet_core.storage.wallet_config.sequencer_addr = value.parse()?;
|
||||
}
|
||||
"seq_poll_timeout_millis" => {
|
||||
wallet_core.storage.wallet_config.seq_poll_timeout_millis =
|
||||
value.parse()?;
|
||||
"seq_poll_timeout" => {
|
||||
wallet_core.storage.wallet_config.seq_poll_timeout =
|
||||
humantime::parse_duration(&value)
|
||||
.map_err(|e| anyhow::anyhow!("Invalid duration: {}", e))?;
|
||||
}
|
||||
"seq_tx_poll_max_blocks" => {
|
||||
wallet_core.storage.wallet_config.seq_tx_poll_max_blocks = value.parse()?;
|
||||
@ -131,9 +129,9 @@ impl WalletSubcommand for ConfigSubcommand {
|
||||
"sequencer_addr" => {
|
||||
println!("HTTP V4 account_id of sequencer");
|
||||
}
|
||||
"seq_poll_timeout_millis" => {
|
||||
"seq_poll_timeout" => {
|
||||
println!(
|
||||
"Sequencer client retry variable: how much time to wait between retries in milliseconds(can be zero)"
|
||||
"Sequencer client retry variable: how much time to wait between retries (human readable duration)"
|
||||
);
|
||||
}
|
||||
"seq_tx_poll_max_blocks" => {
|
||||
|
||||
@ -173,7 +173,7 @@ pub async fn execute_subcommand(
|
||||
.sequencer_client
|
||||
.send_tx_program(transaction)
|
||||
.await
|
||||
.context("Transaction submission error");
|
||||
.context("Transaction submission error")?;
|
||||
|
||||
SubcommandReturnValue::Empty
|
||||
}
|
||||
@ -191,10 +191,7 @@ pub async fn execute_continuous_run(wallet_core: &mut WalletCore) -> Result<()>
|
||||
.last_block;
|
||||
wallet_core.sync_to_block(latest_block_num).await?;
|
||||
|
||||
tokio::time::sleep(std::time::Duration::from_millis(
|
||||
wallet_core.config().seq_poll_timeout_millis,
|
||||
))
|
||||
.await;
|
||||
tokio::time::sleep(wallet_core.config().seq_poll_timeout).await;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2,10 +2,12 @@ use std::{
|
||||
collections::HashMap,
|
||||
io::{BufReader, Write as _},
|
||||
path::Path,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use anyhow::{Context as _, Result};
|
||||
use common::config::BasicAuth;
|
||||
use humantime_serde;
|
||||
use key_protocol::key_management::{
|
||||
KeyChain,
|
||||
key_tree::{
|
||||
@ -184,8 +186,9 @@ pub struct WalletConfig {
|
||||
pub override_rust_log: Option<String>,
|
||||
/// Sequencer URL
|
||||
pub sequencer_addr: Url,
|
||||
/// Sequencer polling duration for new blocks in milliseconds
|
||||
pub seq_poll_timeout_millis: u64,
|
||||
/// Sequencer polling duration for new blocks
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub seq_poll_timeout: Duration,
|
||||
/// Sequencer polling max number of blocks to find transaction
|
||||
pub seq_tx_poll_max_blocks: usize,
|
||||
/// Sequencer polling max number error retries
|
||||
@ -204,7 +207,7 @@ impl Default for WalletConfig {
|
||||
Self {
|
||||
override_rust_log: None,
|
||||
sequencer_addr: "http://127.0.0.1:3040".parse().unwrap(),
|
||||
seq_poll_timeout_millis: 12000,
|
||||
seq_poll_timeout: Duration::from_secs(12),
|
||||
seq_tx_poll_max_blocks: 5,
|
||||
seq_poll_max_retries: 5,
|
||||
seq_block_poll_max_amount: 100,
|
||||
@ -539,7 +542,7 @@ impl WalletConfig {
|
||||
let WalletConfig {
|
||||
override_rust_log,
|
||||
sequencer_addr,
|
||||
seq_poll_timeout_millis,
|
||||
seq_poll_timeout,
|
||||
seq_tx_poll_max_blocks,
|
||||
seq_poll_max_retries,
|
||||
seq_block_poll_max_amount,
|
||||
@ -550,7 +553,7 @@ impl WalletConfig {
|
||||
let WalletConfigOverrides {
|
||||
override_rust_log: o_override_rust_log,
|
||||
sequencer_addr: o_sequencer_addr,
|
||||
seq_poll_timeout_millis: o_seq_poll_timeout_millis,
|
||||
seq_poll_timeout: o_seq_poll_timeout,
|
||||
seq_tx_poll_max_blocks: o_seq_tx_poll_max_blocks,
|
||||
seq_poll_max_retries: o_seq_poll_max_retries,
|
||||
seq_block_poll_max_amount: o_seq_block_poll_max_amount,
|
||||
@ -566,9 +569,9 @@ impl WalletConfig {
|
||||
warn!("Overriding wallet config 'sequencer_addr' to {v}");
|
||||
*sequencer_addr = v;
|
||||
}
|
||||
if let Some(v) = o_seq_poll_timeout_millis {
|
||||
warn!("Overriding wallet config 'seq_poll_timeout_millis' to {v}");
|
||||
*seq_poll_timeout_millis = v;
|
||||
if let Some(v) = o_seq_poll_timeout {
|
||||
warn!("Overriding wallet config 'seq_poll_timeout' to {v:?}");
|
||||
*seq_poll_timeout = v;
|
||||
}
|
||||
if let Some(v) = o_seq_tx_poll_max_blocks {
|
||||
warn!("Overriding wallet config 'seq_tx_poll_max_blocks' to {v}");
|
||||
|
||||
@ -156,6 +156,8 @@ impl WalletCore {
|
||||
|
||||
let mut storage_file = tokio::fs::File::create(&self.storage_path).await?;
|
||||
storage_file.write_all(&storage).await?;
|
||||
// Ensure data is flushed to disk before returning to prevent race conditions
|
||||
storage_file.sync_all().await?;
|
||||
|
||||
println!("Stored persistent accounts at {:#?}", self.storage_path);
|
||||
|
||||
@ -168,6 +170,8 @@ impl WalletCore {
|
||||
|
||||
let mut config_file = tokio::fs::File::create(&self.config_path).await?;
|
||||
config_file.write_all(&config).await?;
|
||||
// Ensure data is flushed to disk before returning to prevent race conditions
|
||||
config_file.sync_all().await?;
|
||||
|
||||
info!("Stored data at {:#?}", self.config_path);
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use std::sync::Arc;
|
||||
use std::{sync::Arc, time::Duration};
|
||||
|
||||
use anyhow::Result;
|
||||
use common::{HashType, block::HashableBlockData, sequencer_client::SequencerClient};
|
||||
@ -11,8 +11,7 @@ use crate::config::WalletConfig;
|
||||
pub struct TxPoller {
|
||||
polling_max_blocks_to_query: usize,
|
||||
polling_max_error_attempts: u64,
|
||||
// TODO: This should be Duration
|
||||
polling_delay_millis: u64,
|
||||
polling_delay: Duration,
|
||||
block_poll_max_amount: u64,
|
||||
client: Arc<SequencerClient>,
|
||||
}
|
||||
@ -20,7 +19,7 @@ pub struct TxPoller {
|
||||
impl TxPoller {
|
||||
pub fn new(config: WalletConfig, client: Arc<SequencerClient>) -> Self {
|
||||
Self {
|
||||
polling_delay_millis: config.seq_poll_timeout_millis,
|
||||
polling_delay: config.seq_poll_timeout,
|
||||
polling_max_blocks_to_query: config.seq_tx_poll_max_blocks,
|
||||
polling_max_error_attempts: config.seq_poll_max_retries,
|
||||
block_poll_max_amount: config.seq_block_poll_max_amount,
|
||||
@ -62,7 +61,7 @@ impl TxPoller {
|
||||
return Ok(tx);
|
||||
}
|
||||
|
||||
tokio::time::sleep(std::time::Duration::from_millis(self.polling_delay_millis)).await;
|
||||
tokio::time::sleep(self.polling_delay).await;
|
||||
}
|
||||
|
||||
anyhow::bail!("Transaction not found in preconfigured amount of blocks");
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user