This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
InfiniteZero / DIN Protocol DevNet: a federated-learning network coordinated by Solidity contracts on Optimism Sepolia, operated through dincli, a Python/Typer CLI. Raw training data never leaves a client's device — only model artifacts (pinned to IPFS) and on-chain coordination state are shared.
The Solidity tree at foundry/ (Foundry workflow) is the primary toolchain for building, testing, deploying, and verifying contracts, via foundry/script/. hardhat/ remains in the tree as a complementary/secondary toolchain, not the primary path.
Docs convention: Documentation/ describes what exists in the code on develop (not the live Sepolia deployment) — Documentation/public/ for network participants, Documentation/technical/ for code readers. Developer/ holds forward-looking material: design/ (planned mechanism designs), issues/ (backlog), proposals/ (tooling proposals), tasks/ (contributor task specs), tasks-plan/<github-user>/ (contributor task plans pending maintainer review, forwarded into tasks/ once approved), discussion/, rejected-ideas/, plus ROADMAP.md and process docs. Placement rules: Developer/README.md.
pip install -e . # editable install (repo root, requires Python >=3.12)
dincli --help # CLI entrypoint (dincli.main:app)
pytest # run all tests
pytest tests/test_dintoken.py -k test_name # run a single testThere is no [tool.ruff] config committed in pyproject.toml — .ruff_cache is a local artifact, not enforced CI config. [tool.pytest.ini_options] registers the integration marker. Code standard is plain PEP 8 (per Developer/CONTRIBUTING.md).
Tests mock chain/IPFS state via monkeypatch and typer.testing.CliRunner rather than hitting a live network (see tests/test_dintoken.py, tests/test_ipfs_config.py).
cd foundry && npm ci && forge build && forge test # primary: build/test
forge script script/DeployPlatform.s.sol ... # primary: deploy (see script/ for deploy/upgrade scripts)
cd hardhat && npx hardhat compile && npx hardhat test # secondary/complementary toolchainnpm ci in foundry/ must run before forge test: Upgrades.validateImplementation (used by the upgrade-validation tests and DeployPlatform.s.sol/UpgradePlatform.s.sol) shells out over FFI to npx @openzeppelin/upgrades-core, and without foundry/node_modules already populated, forge test's parallel test functions race each other fetching the package into the shared npx cache — a source of both flakiness and unnecessary supply-chain exposure from fetching a package at test time. foundry/package.json already pins the dependency, so npm ci installs from the committed lockfile rather than resolving anything new.
Foundry config: solc 0.8.28, via_ir = true. Hardhat config: solc 0.8.28, evmVersion: cancun, local hardhat network forced to hardfork: cancun (required for TSTORE/TLOAD support used by the contracts). hardhat/hardhat.config.ts loads ../.env then ../.env.<NETWORK> (defaults to local).
./foundry/anvil.sh # local anvil chain
dincli system configure-network --network local # or sepolia_op_devnet / mainnet
dincli system register-wallet --account <account_id>- Platform-level contracts (deployed once by the DIN-Representative):
DinCoordinator(ETH<->DIN exchange, slasher registry admin),DinToken(ERC20, minted on ETH deposit),DinValidatorStake(validator staking/slashing),DinModelRegistry(model registration, open-source vs proprietary fee). SeeDocumentation/public/workflows/din-workflow.md. - Task-level contracts (deployed per model by the model owner):
DINTaskCoordinatorandDINTaskAuditor. These must be authorized as "slashers" onDinValidatorStake(viaDinCoordinator, only callable by the DIN-Representative) before the model owner can register the model inDinModelRegistry. SeeDocumentation/public/workflows/model-workflow.mdfor the full step-by-step (deploy → slasher auth request → genesis model → register → global iterations).
A model's lifecycle runs in Global Iterations (GI): aggregator/auditor registration → Local Model Submission (LMS) by clients → auditor evaluation/scoring → two-tier aggregation (T1 sub-batches, T2 combines T1 into the new global model) → slashing of misbehaving validators → GI end. Each phase is opened/closed explicitly by the model owner via dincli model-owner ... subcommands, and other roles act only within an open phase.
main.pyregisters one Typer sub-app per role/concern:system,dinrep(DIN-Representative actions),model-owner,aggregator,auditor,client,dintoken,task,ipfs. Role-specific submodules for the model-owner workflow live undercli/modelownerd/(deploy, gi, lms, lms_evaluation, aggregation, auditor_batches, slash, model, task, setup).cli/context.py(DinContext) is the shared runtime object injected into commands: lazily resolves network/web3/account, fetches deployed platform contracts, resolves task contract artifacts (custom ABIs can be supplied via the manifest'stask_contractsblock, falling back to bundled ABIs indincli/abis/), and tracks per-directory IPFS CID caches (local.json.cid) so files are only re-fetched when their CID changes.cli/utils.pyholds config/cache paths (viaplatformdirs:CONFIG_DIR/CACHE_DIRunderdincli), account loading/keystore handling, manifest load/cache/key lookup, GI state enum helpers, and tx building.services/runtime.pybuilds aServiceRuntimeContext(network, manifest, manifest_path, model_id/role) that is auto-injected into model-owner-supplied service functions when they declare aruntimeparameter.
Each model owner supplies their own Python service files (not part of this repo) implementing model-specific logic: model.py (architecture), modelowner.py (genesis model, scoring, audit test data), client.py (local training/DP), auditor.py (scoring), aggregator.py (T1/T2 aggregation). These are uploaded to IPFS and referenced by CID in a per-model manifest.json at <root_dir>/tasks/<network>/task_<coordinator_address>/manifest.json. dincli dynamically loads and calls these functions at runtime (DinContext.load_custom_fn), fetching them from IPFS on demand and caching by CID. cache_model_0/ and cid_services/ in this repo are example/reference service implementations (MNIST-style), not framework code.
Differential privacy is opt-in per model via a nested dp block in the manifest; when enabled, client.py's training function applies the configured DP mechanism.
dincli/services/ipfs.py supports three interchangeable upload/retrieve backends (env-var-configured IPFS node, Filebase, or a fully custom Python provider) selected via resolve_ipfs_config()/ipfs_provider config — see Documentation/public/guides/ipfs.md. All CID-bearing artifacts (services, manifests, model weights, ABIs) flow through this layer rather than direct HTTP calls scattered through the CLI.
Network selection (local / sepolia_op_devnet / mainnet) is resolved per-command (--network flag, falling back to configured default) and drives which .env.<network> file, RPC endpoint, and deployed contract addresses (load_din_info()) are used. The live DevNet runs on Optimism Sepolia (sepolia_op_devnet, chainId 11155420).