logos-devnet 0.2.3

A local LEZ chain for Logos development: one command to start it, fund accounts and deploy programs
logos-devnet-0.2.3 is not a library.

logos-devnet

A local LEZ chain for Logos development. One command starts a sequencer on your machine with 1 second blocks and a funded faucet, and a project file in your repo says what else it needs: programs, a hundred funded test accounts, logosctl sessions. A transfer lands in about 2 seconds and a program deploy in about 10, and you never run out of testnet funds.

Keep the public testnet for final checks. Messaging (chat, discovery) isn't part of this: it stays on the Logos fleets.

Install

cargo install --locked logos-devnet

Or take a binary from the latest release, built for macOS (aarch64-apple-darwin, x86_64-apple-darwin) and Linux (x86_64-unknown-linux-musl, aarch64-unknown-linux-musl, static, so any distribution), and check it against its .sha256:

v=v0.2.0 t=aarch64-apple-darwin
curl -fsSL --remote-name-all https://github.com/drptbl/logos-devnet/releases/download/$v/logos-devnet-$v-$t.tar.gz{,.sha256}
shasum -a 256 -c logos-devnet-$v-$t.tar.gz.sha256 \
  && tar -xzf logos-devnet-$v-$t.tar.gz \
  && mkdir -p ~/.local/bin && install logos-devnet-$v-$t/logos-devnet ~/.local/bin/

Put ~/.local/bin on your PATH if it isn't. cargo install --locked --git https://github.com/drptbl/logos-devnet builds the latest main.

You need Docker (OrbStack, Docker Desktop or Colima). With --native you need git, a Rust toolchain and LEZ's build dependencies instead: clang, pkg-config and OpenSSL (brew install pkg-config openssl on macOS), plus libpcsclite-dev on Linux. The first native build takes a few minutes.

Use it

logos-devnet up                                    # start, or resume
logos-devnet fund Public/<your account id>         # 10B lepta from the faucet
logos-devnet deploy my-program my_program.bin      # prints LOGOS_PROGRAM_MY_PROGRAM=<id>
logos-devnet wallet-config ~/my-app/devnet-wallet  # a wallet_config.json for lez_core
source ~/.logos-devnet/env                         # LOGOS_DEVNET_SEQUENCER, LOGOS_PROGRAM_*
Command What it does
up Starts the devnet, or resumes it, and makes it match your logos-devnet.toml. Takes --docker or --native, --lez <tag>, --port, --block-time and --max-txs-per-block, and remembers them. --rebuild builds the image or binaries again.
down Stops it. The chain is kept.
reset Deletes the chain and starts a new one, every account group funded at genesis. Takes the same options as up.
status What runs, the chain head, the faucet, programs, account groups and sessions.
doctor Checks the project file, Docker and the image (or git and cargo with --native), the port, the faucet and, when sessions need it, logosctl, and says how to fix what fails.
fund <account> [amount] Sends lepta from the faucet to a public account, 10B by default.
deploy <name> <elf> Deploys a program and adds LOGOS_PROGRAM_<NAME> and LOGOS_PROGRAM_<NAME>_HEX to the env file. Names are lower-case letters, digits and -.
wallet-config <dir> Writes a wallet_config.json pointing at the devnet into a new wallet directory.
env Prints the env file.
logs [-f] Prints the sequencer's log.
session new <name> [--module <m>] Makes a logosctl session with a funded devnet wallet, see below.
session ls, session env <name>, session rm <name> Lists sessions, prints one's env to source, deletes one.
accounts new <name> --count <n> [--amount <a>] Makes a group of funded accounts, see below.
accounts ls Lists account groups.

Every command takes --json for scripts and agents, --config <file> for a project file elsewhere, and --home <dir>. A bad option changes nothing: up and reset check it, and build what they need, before they stop the running chain.

Set up a project

Commit a logos-devnet.toml at your project's root, and logos-devnet up anywhere below it makes the devnet match:

[devnet]                 # optional, the same settings as up's flags
block_time = "1s"
max_txs_per_block = 100

[programs]               # name = guest binary, relative to this file
counter = "target/riscv32im-risc0-zkvm-elf/docker/counter.bin"

[accounts.users]         # a group of funded accounts
count = 100
amount = 5_000_000_000   # lepta each, 10B when left out

[sessions.alice]         # a logosctl session, as session new
modules = ["chat_module"]

up deploys missing programs, makes and funds missing accounts and sessions, and changes nothing the second time (0.6 s). A program whose binary changed since it was deployed stays as it is, with a note: deployed programs are immutable, so reset deploys the new build. up checks the whole file before it touches the devnet.

Projects share one devnet, ~/.logos-devnet on port 3040, unless the file sets home, e.g. home = ".logos-devnet", for a devnet of the project's own (give it its own [devnet] port too, if another one runs). On a shared devnet, a [devnet] section that disagrees with the running chain is refused rather than restarting the chain under the other projects: down and up apply it. A reset there funds every project's account groups again, and each project's next up deploys its programs on the new chain.

Many accounts

An account group is any number of funded accounts for tests with many users: a hundred parties calling one program, say. Declare it in the project file or make one by hand:

logos-devnet accounts new users --count 100 --amount 5000000000

Its keys live in one LEZ wallet, ~/.logos-devnet/accounts/users/wallet, which lez_core and the LEZ CLI wallet can both use. Its accounts.json lists every account, and LOGOS_DEVNET_ACCOUNTS_USERS in the env file points at it:

{
  "amount": 5000000000,
  "accounts": [
    { "index": 0, "id": "CfcghzvosNM114Akg71vuQyDApHPYpPVC9p4uPFQygye", "hex": "ad54f9c3…" }
  ]
}

A group that exists when a chain starts is funded at genesis, so a hundred accounts, or a thousand, are ready at once. One added to a running chain is funded one transfer at a time, about 2.4 s each (100 accounts took 237 s), so declare groups before the first up, or reset. A group never shrinks: a lower count keeps the accounts it has. A new amount applies to new accounts, and to every account after reset. Making the keys is quick, 500 in under 3 s. Accounts that send need more than they move: every call holds about 1.6M lepta of fees up front, so give a sending group at least a few million each (the default is 10B).

How many transactions fit is set by max_txs_per_block. Upstream allows 20 per block, so at 1 s blocks about 20 per second. Measured with every account of a group sending one transfer at the same moment:

max_txs_per_block Burst Landed
20 100 transfers 5 blocks of 20, 5.5 s
100 100 transfers 1 block, 1.6 s
200 200 transfers 197 in 1 block. 3 were refused by the RPC, which takes 100 connections at a time

Up to 1000 is accepted. Above 100 clients at the same instant, the RPC answers 429 to the rest, so retry or spread them.

Point your project at it

The env file is the contract. A project reads LOGOS_DEVNET_SEQUENCER for the RPC URL, and LOGOS_PROGRAM_<NAME> (base58) or LOGOS_PROGRAM_<NAME>_HEX (what lez_core takes as program_id_hex) for its programs, falling back to the testnet values when they're not set.

Program ids stay the same. deploy my-program always deploys from the dev wallet's program-my-program header key, and a program id is that key's account id, so reset and redeploy give you the same LOGOS_PROGRAM_MY_PROGRAM. A second deploy of the same name on the same chain does nothing: reset first to deploy a new build.

For a wallet, give it its own directory. wallet-config refuses a directory that already holds a wallet, because repointing a testnet wallet at the devnet mixes up its sync state. Then create the wallet the usual way, e.g. logosctl call lez_core create_new str:<dir>/wallet_config.json str:<dir>/storage.json str:, and fund its public account. After a reset, such a wallet still holds the old chain's sync position and private notes, and skips the new chain's blocks up to the old height: make a new one.

Sessions

A session is a throwaway logosctl session wired to the devnet, for an app or an agent that talks to Logos through logosctl. You need logosctl on your PATH.

logos-devnet session new alice                          # lez_core and a funded wallet
logos-devnet session new bob --module chat_module       # plus any module from the catalog
logos-devnet session new carol --amount 5000000         # fund it with less than 10B lepta
source <(logos-devnet session env alice)                # LOGOSCTL_CONFIG_DIR and the wallet

Each session runs its own daemon in ~/.logos-devnet/sessions/<name>, with lez_core and any --module installed from the official catalog. Its wallet points at the devnet, is open in that daemon, and holds a public account the faucet funded. session env prints the devnet's env plus:

Variable
LOGOSCTL_CONFIG_DIR the session, so logosctl and anything built on it talk to its daemon
LOGOS_DEVNET_WALLET_CONFIG the wallet's wallet_config.json
LOGOS_DEVNET_WALLET_STORAGE the wallet's storage.json
LOGOS_DEVNET_ACCOUNT its funded account, Public/<id>

Sessions follow the chain. down stops their daemons. up starts them again, loads their modules and opens their wallets, also after a --port change. reset funds each account again with what it was created with, and makes each wallet forget the old chain: its sync position and the state of its private accounts. Its keys and accounts stay. session rm deletes a session and its wallet.

The catalog's lez_core is built for the LEZ release the testnet runs, so sessions match the default --lez tag.

What runs

LEZ's own sequencer_service runs, built with two features. standalone replaces Bedrock, the L1, with an in-process mock that accepts every block. prove runs programs in-process, so you don't need r0vm. The default is v0.3.1, the release lez_core 0.5.1 is built on. --lez picks another tag, and a chain only runs one tag: reset --lez <tag> to switch.

devnet testnet
Block time 1 s (--block-time) 60 s, often several minutes
Funding faucet, funded at genesis testnet faucet
Base fee 8 8
Block timestamp your clock the sequencer's clock
L1 finality, indexer none yes

Fees are the testnet's. Every call holds gas_limit × base fee up front, plus a little for its size, so about 1.6M lepta at a gas_limit of 200,000. The faucet makes that irrelevant.

Private calls still prove on your machine, so they take as long as on the testnet.

Where things live

Everything is in ~/.logos-devnet (or LOGOS_DEVNET_HOME):

Path
settings.json LEZ tag, runtime, port, block time, faucet
env the env file
chain/ the sequencer's config, database and keys. reset deletes it.
wallet/ the dev wallet: the faucet and each program's header key. Kept across resets.
compose.yaml the Docker runtime's compose file
native/ the native runtime's LEZ checkout, build, log and pid file
sessions/ one directory per session: its logosctl home, wallet and session.json
accounts/ one directory per account group: its wallet and accounts.json
programs.json the binary hash each program was deployed from
lock held while a command changes the devnet, so two projects sharing the home take turns

The RPC listens on 127.0.0.1 only. It has no authentication, so don't publish the port.

JSON

With --json, a command prints one JSON document and nothing else on stdout. Notes go to stderr. The fields:

Command Fields
up, reset, status lez, runtime, sequencer (url, running, block, max_txs_per_block, block_time), faucet (account, balance), programs, account_groups, sessions, env_file
deploy name, id, hex, var, deployed_now
fund account, balance
accounts new, accounts ls name, accounts, amount, file, wallet, var (a list for ls)
session new name, modules, vars
session ls a list of name, running, account, balance, error
env, session env an object of variable to value
wallet-config path
down, session rm message
doctor ok, checks (name, ok, detail, fix)

In CI

The repo is a GitHub Action. On a Linux runner it installs logos-devnet, starts the devnet from your project file and exports the env into the job:

- uses: drptbl/logos-devnet@v0.2.0
  with:
    args: --max-txs-per-block 100     # optional, more options for up
- run: cargo test                     # LOGOS_DEVNET_SEQUENCER, LOGOS_PROGRAM_*, LOGOS_DEVNET_ACCOUNTS_* are set

Inputs: version (default: the release tag you use the action at, so set it when you pin a commit SHA), config, working-directory, args (space-separated, without shell quoting. Set the home with LOGOS_DEVNET_HOME, not --home), and start: "false" to only install. Output: sequencer. macOS runners have no Docker, so it refuses them.

The image

ghcr.io/drptbl/logos-devnet-sequencer:<lez tag> holds the sequencer and the LEZ wallet for linux/amd64 and linux/arm64. up pulls it, and builds it locally from docker/Dockerfile when no image is published for your tag. To build it yourself:

docker build --build-arg LEZ_TAG=v0.3.1 -t ghcr.io/drptbl/logos-devnet-sequencer:v0.3.1 - < docker/Dockerfile

License

MIT or Apache-2.0, at your option.