# Porting

> Guides Go→Rust porting for Pluto. Invoke when asked to port, implement parity for, or translate a Go component.

- Skill: `nethermindeth/porting` (Agent Skill)
- Install (CLI): `npx skillmds@latest add nethermindeth/porting`
- Raw SKILL.md: https://api.skillmd.com/api/skills/nethermindeth/porting/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: nethermindeth (https://skillmd.com/u/nethermindeth)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/nethermindeth/porting

---


## Pre-flight

Before writing any code:

1. **Charon location:** See `charon-guide` for codebase location, version, and structure. Verify once per session if needed.
2. Record the Go reference in the plan: `charon/<path>:<line> (v1.7.1)`
3. **Do not proceed without an approved plan.**

---

## Step 1 — Read Go source

**Architecture context:** See `charon-guide` skill for workflow components, design patterns, and package structure.

For each file in scope:
- What does it do? Inputs, outputs, defaults.
- What are the failure modes and error strings? (copy exact strings)
- What are the user-visible side effects? (stdout, files written, exit codes)
- Trace the main logic flow top-to-bottom.

Do not guess. If behavior is unclear, ask.

For DKG, sync, reshare, FetchDefinition, or peer-indexed broadcast code, still use Charon v1.7.1 as the porting baseline, but load `.claude/skills/pluto-review/references/trail-of-bits-charon-v2-audit.md` and apply those fixes where v1.7.1 behavior is known vulnerable.

---

## Step 2 — Identify missing dependencies

List Go imports and map each to its Rust equivalent:

| Go import | Rust crate/module | Status |
|---|---|---|
| `encoding/json` | `serde_json` | available |
| `crypto/sha256` | `sha2` | available |
| `some/go/pkg` | ??? | **missing — needs decision** |

Flag anything without a clear mapping before continuing.

---

## Step 3 — Inventory surface area

List every function/type to port, in the same order as the Go source:

| Item | Go file:line | Complexity | Notes |
|---|---|---|---|
| `FooCmd` | `cmd/foo.go:12` | Low | CLI entrypoint |
| `parseBar` | `cmd/foo.go:45` | Medium | custom encoding |
| `BazType` | `pkg/baz/baz.go:8` | High | shared with DKG |

Complexity: **Low** = straightforward translation / **Medium** = non-trivial logic or encoding / **High** = protocol-level, crypto, or shared invariants.

---

## Step 4 — Write the plan

For each item in the inventory:

```
### `parse_bar` (charon/cmd/foo.go:45)

Behavior:
  - Accepts hex-encoded 32-byte key, returns decoded [u8; 32]
  - Returns error "invalid key: <hex>" on bad input (match string exactly)

Rust target: `pluto/crates/core/src/foo.rs`

Edge cases:
  - Empty string → error, not panic
  - Odd-length hex → error from hex::decode, wrap in ModuleError

Invariants:
  - Output length always 32 bytes
  - Error string must match Go for CLI parity
```

Do not begin implementing until this plan is approved.

---

## Step 5 — Implement

Follow `rust-style` skill conventions and AGENTS.md golden rules:
- Match Go error strings exactly (critical for functional equivalence)
- Match Go behavior exactly (defaults, edge cases, validation)
- **Implementation approach can differ:** Use idiomatic Rust patterns and data structures when they preserve the same functional behavior. Direct 1:1 translation is not required—functional equivalence is.

Examples where different implementation is acceptable:
- Go's `defer` → Rust RAII (Drop trait)
- Go's `sync.WaitGroup` → Rust `tokio::task::JoinSet`
- Go's `context.Context` → Rust `CancellationToken` + structured concurrency
- Go's mutex-guarded maps → Rust `DashMap` or channels
- Go's manual error wrapping → Rust `#[from]` derive

Keep Go file open alongside. After each function, verify behavior matches before moving on.

### Comments to avoid in ported code

Do **not** write Go-cross-reference comments in the Rust source:

- `// Mirrors X` / `// Mirror of Go's X`
- `// Equivalent to Go's X`
- `// Ports charon/<path> (vX.Y.Z)`
- `// Placeholder for go.eth2v1.Foo`
- Inline `// foo — router.go:108` cross-refs on routes/fields/methods
- `// router.go:NN` line-number anchors anywhere

Reasons:

- The Go file and version live in the PR description, commit message, and the porting plan — not in code.
- Line-number anchors rot the moment Charon refactors.
- Cross-reference prose is noise that crowds out the actual *why*-comments that matter.

Doc comments should describe what the Rust item does and any non-obvious *why* (constraint, invariant, edge case). If you need to record the Go origin for your own bookkeeping while porting, do it in the plan or PR description, never the source.

---

## Step 6 — Tests

For each ported item:
- Translate Go tests directly; keep the same test name where possible
- For encoding/hashing: generate Go test vectors and hardcode as Rust fixtures
- Use `#[test_case]` for parameterized cases
- Use `#[tokio::test]` for async

Minimum bar: every error path exercised, every Go test translated.

---

## Step 7 — Parity review

Before marking work done, verify functional equivalence with Go implementation:
- CLI flags and arguments match exactly
- Error messages match exactly (critical for user-facing output)
- Wire formats and encodings are identical
- Exit codes match for all error paths
- File outputs (lock files, configs, etc.) are compatible

Use `pluto-review` skill for structured parity review format. Any deviations must be documented with justification.

---

## Type Mappings (Go → Rust)

| Go | Rust |
| --- | --- |
| `string` | `String` / `&str` |
| `[]byte` | `Vec<u8>` / `&[u8]` |
| `int64` / `uint64` | `i64` / `u64` |
| `map[K]V` | `HashMap<K, V>` |
| `[]T` | `Vec<T>` |
| `*T` (nullable) | `Option<T>` |
| `error` | `Result<T, E>` |
| `go func()` | `tokio::spawn()` |
| `chan T` | `tokio::sync::mpsc` |

