Skip to content

feat(cli): add check-status, verify-hash and generate-bindings commands + multi-role token tests - #829

Merged
p3ris0n merged 4 commits into
BCPathway:mainfrom
davidugorji:feat/cli-utilities-and-multirole-tests
Aug 26, 2026
Merged

feat(cli): add check-status, verify-hash and generate-bindings commands + multi-role token tests#829
p3ris0n merged 4 commits into
BCPathway:mainfrom
davidugorji:feat/cli-utilities-and-multirole-tests

Conversation

@davidugorji

Copy link
Copy Markdown
Contributor

Bundles three CLI deployment utilities and the multi-role RBAC test coverage.

check-status (#699)

Pings every contract declared under contracts in .bc-forge.json by reading its instance ledger entry, reporting per-contract reachability and latency. Distinct verdicts (responsive, not_deployed, unreachable, invalid) separate the failure modes; exits non-zero when any contract is not responsive so it can gate a deployment.

verify-hash (#700)

Answers whether a deployed contract is actually running the code in the local build tree. The local side takes the SHA-256 of the raw .wasm bytes (how Soroban derives its code hash); the on-chain side reads the contract instance ledger entry and walks to the referenced wasmHash rather than trusting a hash cached in config. Exits non-zero on anything but a match.

generate-bindings (#701)

Wraps stellar contract bindings <language> with the configured network already applied. All seven generators supported (typescript, rust, python, java, flutter, swift, php). Option combinations are validated before spawning so misuse is reported by this CLI rather than as an opaque subprocess failure. The binary resolves through STELLAR_CLI_BIN/SOROBAN_CLI_BIN for the soroban→stellar rename.

Multi-role token tests (#759)

Role membership is a single per-address bitmask under AdminKey::RoleMask, so granting a second role is a bitwise OR onto the existing mask. These tests grant Minter | Pauser to one address and execute a real mint and a real pause from it — covering simultaneous roles in one mask entry, verified transfer halt, per-address isolation, and that clearing one bit leaves the other enforced.

Verification

  • tsc -b clean; 72/72 vitest pass (41 new + 31 existing)
  • All three commands registered and visible in --help
  • Rust: 46 + 7 + 6 token tests pass, no regressions
  • cargo fmt --all -- --check and cargo clippy --all-targets --all-features -- -D warnings clean
  • check-status and verify-hash exercised against live Soroban testnet RPC; generate-bindings verified end-to-end against Stellar CLI 25.2.0

Note: PR #643 covers related ground via fuzz tests in contracts/admin/src/fuzz_roles.rs (issue #484). The tests here are token-level integration tests in a separate file, so the two do not conflict.

Closes #699
Closes #700
Closes #701
Closes #759

Adds a `bc-forge check-status` command that pings every contract declared
under `contracts` in .bc-forge.json and reports per-contract reachability
and latency.

Each contract is probed by reading its instance ledger entry via
getLedgerEntries. An entry proves the contract is deployed and served by
the RPC node, so the check needs no contract-specific method and works
uniformly across token, wrapper and any future contract.

Four distinct statuses are reported rather than a single pass/fail, so an
operator can tell the failure modes apart:

- responsive   instance entry returned, with measured latency
- not_deployed RPC answered but holds no instance for that id
- unreachable  the RPC call itself failed
- invalid      contract id is malformed or absent from the config

Contract ids are validated locally before any network call, so a typo in
the config is reported immediately instead of as a spurious RPC failure.
The command exits non-zero when any contract is not responsive, making it
usable as a deployment gate in CI.

The clock is injected so latency assertions in tests are deterministic.

Closes BCPathway#699
Adds a `bc-forge verify-hash` command that answers whether a deployed
contract is actually running the code in the local build tree.

The local side hashes the raw .wasm bytes with SHA-256, which is exactly
how Soroban derives the code hash it stores, so the two values are
directly comparable. Verified against sha256sum on the same artifact.

The on-chain side reads the contract instance ledger entry and walks
val -> instance -> executable to the referenced wasmHash, rather than
trusting a hash cached in .bc-forge.json, so the check reflects what the
network currently serves.

Distinct verdicts separate the failure modes an operator cares about:

- match           local build is what the network runs
- mismatch        contract runs different code than the local build
- missing_local   the build artifact was not found or is unreadable
- missing_onchain no instance entry, or a Stellar-asset contract with
                  no uploaded WASM
- invalid         malformed input or a failed RPC lookup

The command exits non-zero on anything other than a match so it can gate
a release pipeline.

Closes BCPathway#700
…nerator

Adds a `bc-forge generate-bindings` command that drives
`stellar contract bindings <language>` so binding generation is reachable
from the project CLI with the configured network already applied.

All seven generators the Stellar CLI exposes are supported: typescript,
rust, python, java, flutter, swift and php.

Option combinations are validated before spawning, so misuse is reported
by this CLI rather than as an opaque subprocess failure:

- exactly one contract source (--wasm, --wasm-hash or --contract-id)
- --output-dir required for the generators that write a package
- the rust generator accepts a local --wasm only, matching its actual
  flag surface, and is rejected early for network sources

RPC flags are forwarded only when generating from the network, since a
local wasm needs no node. The binary is resolved through STELLAR_CLI_BIN
/ SOROBAN_CLI_BIN to accommodate the soroban -> stellar rename, and a
missing binary produces an install hint instead of a bare ENOENT.

The command runner is injected so tests assert the argument vector
without spawning a process. Verified end to end against Stellar CLI
25.2.0: typescript generation produced a package exposing the real
contract methods, and the rust generator emitted a client module.

Closes BCPathway#701
Adds integration coverage for one address holding several roles at once,
exercised through the token contract rather than asserted at the storage
layer alone.

Role membership is a single per-address bitmask under AdminKey::RoleMask,
so granting a second role is a bitwise OR onto the existing mask. The
tests grant Minter | Pauser to one address and then execute a real mint
and a real pause from it, which is the behaviour the bitmask layout is
supposed to make possible.

Coverage:

- both roles are held simultaneously and share one mask entry, while
  never-granted roles stay absent
- the address mints, pauses, is confirmed to have actually halted
  transfers, and unpauses
- exercising one role does not consume or disturb the other
- clearing one bit leaves the other role held and enforced, and the
  cleared role is genuinely rejected
- an address holding only one of the two roles cannot exercise the other
- assignments are isolated per address and do not leak to a bystander
- an address with no roles can neither mint nor pause

The setup writes the RoleMask entry directly, matching how grant_role
persists a combined assignment, so the tests exercise the bitmask path
rather than the superseded per-role legacy keys.

Closes BCPathway#759
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants