Skip to content

Anti-Slashing Protection

Configure EIP-3076 signing-history backends and import/export validator history safely

Ethereum validators can be slashed — permanently penalized and forcibly exited — for signing conflicting blocks or attestations. EIP-3076 defines a slashing protection interchange format for carrying that signing history across validator clients and signers.

Containment Chamber implements EIP-3076 slashing protection with pluggable backends. Block proposals and attestations are checked and recorded before the BLS signature is produced. If a request would result in a slashable message, the signer returns HTTP 412 and refuses to sign. Backend or integrity failures fail closed, usually as HTTP 503.

direction: down
req: "HTTP Request"
auth: "Auth Check"
sem: "Semaphore"
gvr: "GVR Validation"
key: "Key Loaded?"
root: "Compute Signing Root"
as: "Anti-Slashing\ncheck_and_update"
bls: "BLS Sign"
ok: "HTTP 200\n{ signature: 0x... }"
missing: "HTTP 404\nKey not found"
rejected: "HTTP 412\nSlashing violation"
wrongnet: "HTTP 400\nNetwork mismatch"
req.style.fill: "#FFF6EF"
req.style.stroke: "#D35F0A"
req.style.font-color: "#170206"
auth.style.fill: "#FFF6EF"
auth.style.stroke: "#D35F0A"
auth.style.font-color: "#170206"
sem.style.fill: "#FFF6EF"
sem.style.stroke: "#D35F0A"
sem.style.font-color: "#170206"
gvr.style.fill: "#FEEC8C"
gvr.style.stroke: "#D35F0A"
gvr.style.font-color: "#170206"
key.style.fill: "#FEEC8C"
key.style.stroke: "#D35F0A"
key.style.font-color: "#170206"
root.style.fill: "#FEEC8C"
root.style.stroke: "#D35F0A"
root.style.font-color: "#170206"
as.style.fill: "#FEEC8C"
as.style.stroke: "#D35F0A"
as.style.font-color: "#170206"
bls.style.fill: "#CAF2E6"
bls.style.stroke: "#13A477"
bls.style.font-color: "#170206"
ok.style.fill: "#CAF2E6"
ok.style.stroke: "#13A477"
ok.style.font-color: "#170206"
missing.style.fill: "#FEE2E2"
missing.style.stroke: "#B42318"
missing.style.font-color: "#170206"
rejected.style.fill: "#FEE2E2"
rejected.style.stroke: "#B42318"
rejected.style.font-color: "#170206"
wrongnet.style.fill: "#FEE2E2"
wrongnet.style.stroke: "#B42318"
wrongnet.style.font-color: "#170206"
req -> auth: "POST /api/v1/eth2/sign/{pubkey}"
auth -> sem: "Token validated"
sem -> gvr: "Permit acquired"
gvr -> wrongnet: "Wrong GVR" {style.stroke: "#B42318"}
gvr -> key: "Network verified"
key -> missing: "Not loaded" {style.stroke: "#B42318"}
key -> root: "Loaded"
root -> as: "Signing root"
as -> rejected: "Slashing violation" {style.stroke: "#B42318"}
as -> bls: "Safe to sign" {style.stroke: "#13A477"}
bls -> ok
Backend Multi-Instance Slashing Protection Recommended
PostgreSQL ✅ Complete ✅ Production
DynamoDB ✅ Hybrid ✅ Production at scale / AWS
SQLite ✅ Complete Dev / single instance
Noop ❌ None ⚠️ Testing only
anti_slashing:
backend: postgres
url: "postgresql://user:password@localhost:5432/slashing?sslmode=require"
pool_size: 64 # connection pool size
force_ipv4: false # set true if IPv6 causes issues

PostgreSQL is the default production choice for most deployments. It supports multiple signer instances sharing the same database and provides complete-mode surround vote detection.

TLS is enabled by default. Append ?sslmode=disable to the URL to disable it.

Slashing-protection history is imported before runtime key imports make a key signable:

  • POST /eth/v1/keystores accepts slashing_protection in the Web3Signer-compatible Key Manager API.
  • POST /api/v1/chamber/keys accepts slashing_protection in the Chamber key-management API.

Both paths validate the interchange data against the signer’s configured genesis validators root before writing to the anti-slashing backend. Invalid interchange data rejects the import before keys are inserted.

Deleting keys exports slashing-protection history:

  • DELETE /eth/v1/keystores returns the EIP-3076 interchange payload required by the Web3Signer API.
  • DELETE /api/v1/chamber/keys includes slashing_protection when at least one key was deleted and export succeeds.

SQL backends export stored block and attestation records. DynamoDB Hybrid mode exports watermark-derived synthetic entries so importing into another signer preserves the correct lower bounds.

You can configure the anti-slashing backend via environment variables using the CONTAINMENT_ prefix with __ for nesting:

Terminal window
CONTAINMENT_ANTI_SLASHING__BACKEND=postgres
CONTAINMENT_ANTI_SLASHING__URL="postgresql://user:password@localhost:5432/slashing"
CONTAINMENT_ANTI_SLASHING__POOL_SIZE=64
CONTAINMENT_ANTI_SLASHING__FORCE_IPV4=false

For DynamoDB:

Terminal window
CONTAINMENT_ANTI_SLASHING__BACKEND=dynamodb
CONTAINMENT_ANTI_SLASHING__TABLE=slashing-protection
CONTAINMENT_ANTI_SLASHING__MAX_CONCURRENT_WRITES=256

For quick testing, you can also set the backend via CLI flags:

Terminal window
# SQLite via CLI
containment-chamber server \
--anti-slashing-backend sqlite \
--anti-slashing-sqlite-path ./slashing.sqlite \
--key-sources-filesystem-paths ./keystores/raw
# PostgreSQL via CLI
containment-chamber server \
--anti-slashing-backend postgres \
--anti-slashing-postgres-url "postgresql://user:pass@localhost/slashing" \
--anti-slashing-postgres-pool-size 64 \
--key-sources-filesystem-paths ./keystores/raw
# DynamoDB via CLI
containment-chamber server \
--anti-slashing-backend dynamodb \
--anti-slashing-dynamodb-table slashing-protection \
--anti-slashing-dynamodb-max-concurrent-writes 256 \
--key-sources-filesystem-paths ./keystores/raw
  • Backend at Scale — PostgreSQL pool sizing and DynamoDB scaling guidance
  • AWS IAM Permissions — DynamoDB anti-slashing permissions and table separation
  • Upgrading — back up the slashing database before version bumps