> ## Documentation Index
> Fetch the complete documentation index at: https://docs.blockx.chaintable.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 本地开发环境

> 搭建 BlockX 的 Go + Python 开发环境、构建二进制、跑测试，并在本机拉起一个不依赖外部服务的 worker

这一页告诉你从克隆仓库到本机跑起一个 worker 需要什么。BlockX 是一个 Go 工作区，`python/` 目录放运行时与审计器，`pyhost/` 放嵌入 CPython 的 Executor 宿主。整体架构见 [架构总览](/zh/architecture/overview)，目录分层见 [仓库结构](/zh/architecture/repository-layout)。

## 前置依赖

| 依赖 | 版本 / 说明 | 来源 |
| - | - | - |
| Go | `go.mod` 声明 `go 1.26.4`；`Dockerfile` 用 `golang:1.26-bookworm` 构建 | `go.mod`、`Dockerfile` |
| C 工具链（CGO） | `cmd/worker` 与 `cmd/bundle_worker` 依赖 `github.com/duckdb/duckdb-go-bindings`，必须 `CGO_ENABLED=1`（默认值）并有 `gcc` 或 `clang`。coordinator、bundle\_coordinator、syncinvoker 可以 `CGO_ENABLED=0` | `go.mod`、`Dockerfile`、`internal/duckdb/borrowed/` |
| Python | CPython `3.14` 或更高版本，包含嵌入开发文件 | `python/pyproject.toml`、`pyhost/Makefile` |
| `uv` | 管理 `python/.venv/`；E2E 测试和本地 worker 都从这个 venv 拉起 executor | `AGENTS.md`、`internal/testutil/paths.go` |
| `protoc` + `protoc-gen-go` + `protoc-gen-go-grpc` | 只在改 `.proto` 后重新生成时需要；生成物已提交 | `Makefile` |
| GitHub 私有仓库访问 | `go.mod` 依赖 `github.com/Chaintable/*` 私有模块，`pyproject.toml` 依赖 `blockx-py`、`blockdb-py` 等私有 git 包。本地需要能通过 git 访问这些仓库 | `go.mod`、`python/pyproject.toml`、`.github/workflows/test.yml` |

外部服务在本地开发中都有替身，不需要先搭 etcd、BlockDB、Meta 或链节点：

| 生产依赖 | 本地替代 |
| - | - |
| etcd | 设 `ETCD_ENDPOINTS=none` 跳过注册（`internal/worker/app/app.go` 的 `registerEtcdPublisherAfterReady` 对空值或 `none` 直接返回）。E2E 用 `internal/testutil/etcd.go` 的 `StartEmbeddedEtcd` 在进程内起 `go.etcd.io/etcd/server/v3/embed` |
| BlockDB | 设 `STUB_BLOCKDB_DELAY_MS`（`0` 合法）时 worker 用 `internal/worker/devstub` 的 `DelayedBackendAdapter`；不设任何 `BLOCKDB_*_ADDR` 也不设该变量时退回 `StubBackendAdapter`（`internal/worker/app/app.go` 的 `blockDBStubDelayFromEnv` 与 `blockdbBackend`）。`cmd/blockdb_grpc_stub` 是 benchmark 用的独立 gRPC stub 进程；测试里还有 `internal/testutil/blockdb.go` 的 `StartMockFunctionBlockDB` |
| Meta（logical-types） | 不设 `META_ADDR` 时用本地 stub，可用 `STUB_LOGICAL_TYPES_SCHEMAS` 喂 schema（`app.go` 的 `stubLogicalTypesSchemas`） |
| Node RPC | 不设 `NODE_RPC_ENDPOINT` 时用 dev stub（`docs/deploy.md` §9.2） |
| Function code 存储 | 未配置 `FUNCTION_CODE_REDIS_URL` 且 BlockDB table read/subscribe 地址不全时，`internal/worker/app/function_code.go` 的 `setupFunctionCodeView` 选 `devstub.MemoryFunctionCodeStore`；`FUNC_DEBUG=true` 可强制 devstub |
| Usage Kafka | `BLOCKX_USAGE_DISABLED=true`。不设时 worker 启动探测 broker 失败会拒绝启动（fail-closed） |
| 能力后端示例 | `internal/sdk/localtestservice` 是进程内的示范能力后端，`examples/local_test_service/` 演示如何用 blockx-py 打到本地 worker |

## 快速开始

按照[快速开始](/zh/quickstart)准备依赖、构建 Worker 与 Executor 宿主，并提交本地任务。启动命令集中维护在那里。

修改 EVM 运行时桥接时，可先运行：

```bash theme={null}
cd python
.venv/bin/python -m unittest tests.test_runtime_leafage tests.test_noderpc_bridge
```

接入真实链数据前，先明确[链与区块上下文](/zh/concepts/evm-computation)。完整测试分层见[测试](/zh/development/testing)。

## Makefile 目标

| 目标 | 作用 |
| - | - |
| `make` / `make all` | 等同 `make test` |
| `make fmt` | 检查格式：`go fmt ./...` 有输出即失败并列出 `gofmt -s -l .` |
| `make vet` | `go vet ./...` |
| `make test` | 先 `fmt`、`vet`，再对除 `cmd/syncinvoker` 外的所有含测试包跑 `go test -coverprofile=coverage.out -short -timeout $(TEST_TIMEOUT)`（默认 `600s`），最后把 `cmd/syncinvoker/*_test.go` 逐文件跑（`SYNCINVOKER_TEST_TIMEOUT` 默认 `3m`），合并覆盖率并打印 total 与 top 10 包 |
| `make test-cover` | `go test ./... -v -cover -short` |
| `make test-bench` | `go test ./... -bench=. -benchmem -short` |
| `make proto` | 依次跑 `proto-blockdb`、`proto-meta`、`proto-localtestservice`，再生成 `api/grpc/{worker,bundleworker,coordinator,bundlecoordinator,syncinvoker}` 的 pb |
| `make proto-blockdb` / `proto-meta` / `proto-localtestservice` | 分别生成 `internal/sdk/{blockdb,meta,localtestservice}/gen/` |
| `make deps` | `go mod download` + `go mod tidy` |
| `make clean` | 删除 `coverage.out`、`coverage.html`，并 `go clean -cache -testcache` |
| `make build` | 执行 `go build ./bin/...`。构建二进制用 `go build ./cmd/...`，或按 `Dockerfile` 逐个 `go build -o` |

## 环境变量与配置文件

worker 的加载顺序在 `internal/worker/app/app.go` 里：`DefaultWorkerFullConfig()` → profile 的 `TuneDefaults` → `WORKER_CONFIG` 指向的 JSON 文件（`LoadWorkerFullConfigFrom`）→ 环境变量覆盖。JSON 字段名与 `WorkerFullConfig` 的 `json` tag 一致，示例见 blockx 仓库 `docs/deploy.md` §9.3。coordinator 只读环境变量，没有配置文件（`internal/coordinator/app/config.go` 的 `LoadCoordinatorRuntimeConfigFromEnv`）。

本地开发最常碰到的变量：

| 变量 | 默认值 | 说明 |
| - | - | - |
| `WORKER_LISTEN` / `WORKER_ADDR` | `:8081` / 自动探测出口 IP | 监听地址与注册到 etcd 的对外地址 |
| `ETCD_ENDPOINTS` | `localhost:2379` | worker 与 coordinator 共用；worker 侧 `none` 表示不注册，coordinator 侧必须非空（`Validate` 报 `etcdEndpoints must not be empty`） |
| `EXECUTOR_COUNT` | `8` | Python executor 子进程数 |
| `PYTHON_BIN` / `PYTHONPATH` | `python3` / — | executor 解释器与模块搜索路径；本地指向 `python/.venv/bin/python` 与 `python/` |
| `TASK_SLOTS` | `50`（`core.DefaultConfig`） | 单 worker 最大并发 task |
| `TASK_DEADLINE_MS` | `300000` | 用户未指定时的 task 超时 |
| `EXECUTOR_SPAWN_MODE` | `process` | `sandbox` 走 containerd 沙箱，本地开发保持 `process` |
| `BLOCKX_USAGE_DISABLED` | `false` | 本地必须设 `true`，否则启动时探测 Kafka |
| `STUB_BLOCKDB_DELAY_MS` / `META_ADDR` / `NODE_RPC_ENDPOINT` | — | 控制 BlockDB / Meta / NodeRPC 是走真实端点还是 stub |
| `COORDINATOR_LISTEN` | `:8080` | coordinator gRPC 监听地址 |
| `COORDINATOR_REGISTRY_PREFIXES` | profile 默认 | coordinator watch 的 etcd 前缀，逗号分隔 |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | — | 不设则关闭 trace 导出 |

完整表（含 AIMD、gRPC keepalive、Iceberg cache 等几十项）见 blockx 仓库 `docs/deploy.md` §9；`deploy/env/worker.env.example` 是 EC2 部署用的 env 文件样例，包含大量宿主机脚本才读的键，不适合直接拿来本地跑。

## 代码生成

改了 `.proto` 后重新生成 Go 绑定：

```bash theme={null}
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
make proto          # 全部
make proto-blockdb  # 只生成 internal/sdk/blockdb/gen/
```

`Makefile` 会把 `$(go env GOBIN)`（或 `$(go env GOPATH)/bin`）加进 `PATH`，所以插件装在默认位置即可。proto 源文件位置：`api/grpc/*/` 与 `internal/sdk/{blockdb,meta,localtestservice}/proto/`。生成结果要一起提交。

## 常见问题

<AccordionGroup>
  <Accordion title="E2E 全部显示 SKIP，提示 Python dependencies not available">
    先安装 `python/pyproject.toml` 固定的依赖，并确认 Executor 宿主可以构建。共享进程测试环境由 `internal/testutil/pyhost.go` 准备。
  </Accordion>

  <Accordion title="go build ./cmd/worker 报 build constraints exclude all Go files in duckdb-go-bindings/lib">
    这是 `CGO_ENABLED=0` 的症状。worker 与 bundle\_worker 必须开 CGO，并且机器上要有 C 编译器。首次链接较慢属正常。
  </Accordion>

  <Accordion title="worker 启动直接退出：invalid worker config">
    看错误串：`icebergNamespace must not be empty` 补 `ICEBERG_NAMESPACE`；`plugin BlockTableWriteHandler requires blockdb.batchWriteAddr`（仅 `cmd/bundle_worker`）补 `BLOCKDB_BATCH_WRITE_ADDR`；usage 相关错误补 `BLOCKX_USAGE_DISABLED=true`。
  </Accordion>

  <Accordion title="worker 起来了但 Coordinator 看不到它">
    检查 `ETCD_ENDPOINTS` 是否指向同一个 etcd，以及 `WORKER_ADDR` 是否是 Coordinator 可达的地址；容器网络隔离时必须显式设置（`docs/deploy.md` §7）。本地单机调试可以直接 `ETCD_ENDPOINTS=none` 绕过 Coordinator。
  </Accordion>

  <Accordion title="Python executor 子进程没起来">
    检查 `EXECUTOR_HOST_BIN` 是否指向已构建的宿主，以及 `PYTHONPATH` 是否包含运行时与虚拟环境依赖。启动步骤见[快速开始](/zh/quickstart)。
  </Accordion>

  <Accordion title="go mod download 拉不到 github.com/Chaintable/*">
    这些是私有模块。配置 git 凭据（例如 `git config --global url."git@github.com:".insteadOf "https://github.com/"`），并设 `GOPRIVATE=github.com/Chaintable/*`。Python 侧的 `blockx-py` 等也是私有 git 依赖，同样需要凭据。
  </Accordion>
</AccordionGroup>

## 相关文档

* [测试](/zh/development/testing)：各测试层的命令、耗时与前置条件。
* [贡献流程](/zh/development/contributing)：提交与 PR 约定。
* [仓库结构](/zh/architecture/repository-layout)：`cmd/`、`internal/`、`api/`、`python/` 各放什么。
* [部署概览](/zh/development/deployment)：镜像构建与 EC2 运行形态。
* blockx 仓库 `docs/deploy.md`：完整环境变量表、Docker 启动示例与常见问题。
* blockx 仓库 `AGENTS.md`：构建、测试与协作约定的权威清单。


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.