build(agent): new-agents#a6e6ec iteration
This commit is contained in:
parent
309b97c3c7
commit
e37a251d0f
|
|
@ -0,0 +1,21 @@
|
|||
node_modules/
|
||||
.npmrc
|
||||
.env
|
||||
.env.*
|
||||
__tests__/
|
||||
coverage/
|
||||
.nyc_output/
|
||||
dist/
|
||||
build/
|
||||
.cache/
|
||||
*.log
|
||||
.DS_Store
|
||||
tmp/
|
||||
.tmp/
|
||||
__pycache__/
|
||||
*.pyc
|
||||
.venv/
|
||||
venv/
|
||||
*.egg-info/
|
||||
.pytest_cache/
|
||||
READY_TO_PUBLISH
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
# BeXProof Agent Guidelines
|
||||
|
||||
Overview
|
||||
- BeXProof is a modular verifier/enforcement stack for Best Execution in equity trading. The repository hosts a production-grade MVP intended for extension by follow-on agents.
|
||||
|
||||
Tech Stack (initial MVP)
|
||||
- Language: Python 3.9+
|
||||
- Core modules: policy DSL, verifiable logs, ledger, ZKP prototype, privacy toolkit, governance, adapters
|
||||
- API surface (optional later): FastAPI for internal services; wiring can be added in future tasks.
|
||||
|
||||
Testing & Build
|
||||
- Tests are written with pytest.
|
||||
- Packaging builds with setuptools via pyproject.toml/setup.py and python -m build.
|
||||
- The repository includes test.sh that runs tests and packaging checks.
|
||||
|
||||
How to contribute (high-level rules)
|
||||
- Add small, well-scoped changes; prefer minimal, correct edits.
|
||||
- Write tests for new functionality; ensure tests pass locally before proposing changes.
|
||||
- Update AGENTS.md with any new architectural decisions that future agents must honor.
|
||||
- Do not push to remote unless explicitly requested; the orchestration layer handles publishing steps.
|
||||
|
||||
Repository rules
|
||||
- Always start by examining the codebase with Glob and Grep (rg-based tooling).
|
||||
- Use non-destructive edits; avoid altering unrelated files.
|
||||
- Persist changes in this session; aim to complete a coherent feature chunk before moving to the next.
|
||||
|
||||
Checklist for new features
|
||||
- [ ] Design a cohesive public API for the feature
|
||||
- [ ] Implement minimal viable integration points
|
||||
- [ ] Add unit tests that exercise core logic
|
||||
- [ ] Ensure packaging/test script runs cleanly
|
||||
- [ ] Document how to deploy and test the feature locally
|
||||
21
README.md
21
README.md
|
|
@ -1,3 +1,20 @@
|
|||
# idea164-bexproof-verifiable-best
|
||||
# BeXProof: Verifiable Best Execution and Compliance Verifier (Python MVP)
|
||||
|
||||
Source logic for Idea #164
|
||||
BeXProof is a modular, open-source verifier and enforcement layer designed to accompany equity order routers and brokers. It guarantees and proves Best Execution while preserving data privacy, using a policy-driven DSL, verifiable routing logs, a ZKP-inspired proof substrate, and a tamper-evident ledger for auditable outcomes.
|
||||
|
||||
What you will find in this repository
|
||||
- A production-oriented Python MVP with a small, extensible architecture.
|
||||
- Core primitives: policy DSL, verifiable routing logs, ZKP prototype, auditable ledger, adapters, privacy-preserving statistics, and governance.
|
||||
- A test suite with basic unit tests for each primitive.
|
||||
- A packaging and publishing readiness plan (AGENTS.md, READY_TO_PUBLISH).
|
||||
|
||||
How to run locally
|
||||
- Install dependencies and run tests via the included test script (test.sh).
|
||||
- The MVP intentionally keeps crypto lightweight (HMAC-based signatures for demonstration) to enable fast iteration; replace with real cryptography when integrating into a production environment.
|
||||
|
||||
This repository is organized to be production-friendly and test-driven from the start.
|
||||
|
||||
Hooking into packaging
|
||||
- This package is prepared for Python packaging under the name `idea164_bexproof_verifiable_best` as per the publishing requirements.
|
||||
|
||||
Note: See AGENTS.md for architectural guidelines and how future agents should contribute.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
"""BeXProof Python package initializer."""
|
||||
from .logs import Signer, make_signed_log, verify_log
|
||||
from .policy import Policy, load_policy, evaluate_policy
|
||||
from .ledger import TamperProofLedger
|
||||
from .zkp import generate_proof
|
||||
from .adapters import BrokerGatewayAdapter, ExchangeGatewayAdapter
|
||||
from .privacy import laplace_noise, privacy_aggregate
|
||||
from .governance import Governance
|
||||
|
||||
__all__ = [
|
||||
"Signer",
|
||||
"make_signed_log",
|
||||
"verify_log",
|
||||
"Policy",
|
||||
"load_policy",
|
||||
"evaluate_policy",
|
||||
"TamperProofLedger",
|
||||
"generate_proof",
|
||||
"BrokerGatewayAdapter",
|
||||
"ExchangeGatewayAdapter",
|
||||
"laplace_noise",
|
||||
"privacy_aggregate",
|
||||
"Governance",
|
||||
]
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
"""Lightweight adapters for broker and exchange gateways.
|
||||
|
||||
These are minimal stubs that map internal decision signals to canonical signals
|
||||
exposed to external systems. They are intentionally small MVP implementations.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
from typing import Dict, Any
|
||||
|
||||
|
||||
class BrokerGatewayAdapter:
|
||||
def __init__(self, config: Dict[str, Any] = None):
|
||||
self.config = config or {}
|
||||
|
||||
def adapt_order(self, order_event: Dict[str, Any]) -> Dict[str, Any]:
|
||||
# Example mapping: extract a few key signals from an internal order event
|
||||
return {
|
||||
"type": "order_decision",
|
||||
"order_id": order_event.get("order_id"),
|
||||
"venue": order_event.get("venue"),
|
||||
"signal": order_event.get("signal", "unknown"),
|
||||
}
|
||||
|
||||
|
||||
class ExchangeGatewayAdapter:
|
||||
def __init__(self, config: Dict[str, Any] = None):
|
||||
self.config = config or {}
|
||||
|
||||
def adapt_execution(self, execution_event: Dict[str, Any]) -> Dict[str, Any]:
|
||||
return {
|
||||
"type": "execution_report",
|
||||
"order_id": execution_event.get("order_id"),
|
||||
"price": execution_event.get("price"),
|
||||
"latency_ms": execution_event.get("latency_ms"),
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
"""Governance primitives for BeXProof policy changes."""
|
||||
from __future__ import annotations
|
||||
from typing import Dict, Set
|
||||
|
||||
|
||||
class Governance:
|
||||
def __init__(self, signers: Set[str] = None, required: int = 2):
|
||||
self.signers = set(signers or {"alice", "bob", "carol"})
|
||||
self.required = max(1, min(len(self.signers), required))
|
||||
self.proposals: Dict[str, Dict] = {}
|
||||
self.approvals: Dict[str, Set[str]] = {}
|
||||
|
||||
def propose(self, policy_id: str, policy_text: str) -> None:
|
||||
self.proposals[policy_id] = {"policy": policy_text, "approved": False}
|
||||
self.approvals[policy_id] = set()
|
||||
|
||||
def approve(self, policy_id: str, signer: str) -> bool:
|
||||
if signer not in self.signers:
|
||||
return False
|
||||
if policy_id not in self.proposals:
|
||||
return False
|
||||
self.approvals[policy_id].add(signer)
|
||||
if len(self.approvals[policy_id]) >= self.required:
|
||||
self.proposals[policy_id]["approved"] = True
|
||||
return True
|
||||
|
||||
def is_approved(self, policy_id: str) -> bool:
|
||||
return bool(self.proposals.get(policy_id, {}).get("approved", False))
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
"""Tamper-evident ledger scaffold for BeXProof.
|
||||
|
||||
This ledger appends entries with a simple hash-chain to provide an auditable trail.
|
||||
It's a lightweight MVP; a production system would use a proper append-only store.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import json
|
||||
import time
|
||||
import hashlib
|
||||
from pathlib import Path
|
||||
from typing import Dict, Any
|
||||
|
||||
|
||||
class TamperProofLedger:
|
||||
def __init__(self, path: str = "bexproof_ledger.log"):
|
||||
self.path = Path(path)
|
||||
self.path.parent.mkdir(parents=True, exist_ok=True)
|
||||
self._last_hash = self._load_last_hash()
|
||||
|
||||
def _load_last_hash(self) -> str:
|
||||
if not self.path.exists():
|
||||
return "" # no previous hash
|
||||
# read last line to pull last hash if present
|
||||
last = None
|
||||
with self.path.open("r", encoding="utf-8") as f:
|
||||
for line in f:
|
||||
last = line
|
||||
if not last:
|
||||
return ""
|
||||
try:
|
||||
obj = json.loads(last)
|
||||
return obj.get("hash", "")
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
def _hash_entry(self, entry: Dict[str, Any], prev_hash: str) -> str:
|
||||
payload = json.dumps(entry, sort_keys=True).encode("utf-8")
|
||||
data = payload + prev_hash.encode("utf-8")
|
||||
return hashlib.sha256(data).hexdigest()
|
||||
|
||||
def append(self, entry: Dict[str, Any]) -> Dict[str, Any]:
|
||||
prev_hash = self._last_hash or ""
|
||||
h = self._hash_entry(entry, prev_hash)
|
||||
record = {
|
||||
"entry": entry,
|
||||
"timestamp": int(time.time() * 1000),
|
||||
"prev_hash": prev_hash,
|
||||
"hash": h,
|
||||
}
|
||||
with self.path.open("a", encoding="utf-8") as f:
|
||||
f.write(json.dumps(record, sort_keys=True) + "\n")
|
||||
self._last_hash = h
|
||||
return record
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
"""Verifiable routing logs with lightweight signing.
|
||||
|
||||
This module provides a Signer, log entry creator, and verifier. For demonstration,
|
||||
signatures use HMAC-SHA256 with a secret key. Replace with proper public-key crypto
|
||||
in production.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import json
|
||||
import time
|
||||
import hmac
|
||||
import hashlib
|
||||
|
||||
class Signer:
|
||||
def __init__(self, key: str):
|
||||
self.key = key.encode("utf-8")
|
||||
|
||||
def sign_payload(self, payload: dict) -> str:
|
||||
data = json.dumps(payload, sort_keys=True).encode("utf-8")
|
||||
return hmac.new(self.key, data, hashlib.sha256).hexdigest()
|
||||
|
||||
def make_signed_log(order_id: str, venue: str, price: float, latency_ms: int, signer: Signer, timestamp: int | None = None) -> dict:
|
||||
payload = {
|
||||
"order_id": order_id,
|
||||
"venue": venue,
|
||||
"price": price,
|
||||
"latency_ms": latency_ms,
|
||||
"timestamp": timestamp or int(time.time() * 1000),
|
||||
}
|
||||
signature = signer.sign_payload(payload)
|
||||
payload["signature"] = signature
|
||||
return payload
|
||||
|
||||
def verify_log(log: dict, signer: Signer) -> bool:
|
||||
if "signature" not in log:
|
||||
return False
|
||||
sig = log["signature"]
|
||||
payload = {k: v for k, v in log.items() if k != "signature"}
|
||||
expected = signer.sign_payload(payload)
|
||||
return hmac.compare_digest(sig, expected)
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
"""Simple policy DSL and evaluator for BeXProof.
|
||||
|
||||
This module provides a lightweight policy container and a tiny evaluator.
|
||||
Policies are represented as JSON-like strings for simplicity in this MVP.
|
||||
In production, replace with a proper DSL parser and validator.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import json
|
||||
from dataclasses import dataclass
|
||||
from typing import Dict, Any
|
||||
|
||||
|
||||
@dataclass
|
||||
class Policy:
|
||||
version: int
|
||||
rules: Dict[str, Any]
|
||||
|
||||
|
||||
def load_policy(policy_text: str) -> Policy:
|
||||
# Accept either JSON or Python-like dict string (with single quotes)
|
||||
data = None
|
||||
try:
|
||||
data = json.loads(policy_text)
|
||||
except Exception:
|
||||
# try Python-style dict string
|
||||
try:
|
||||
data = json.loads(policy_text.replace("'", '"'))
|
||||
except Exception:
|
||||
raise ValueError("Policy text is not valid JSON or Python-like dict string")
|
||||
if not isinstance(data, dict) or "version" not in data or "rules" not in data:
|
||||
raise ValueError("Policy must contain 'version' and 'rules' keys")
|
||||
return Policy(version=int(data["version"]), rules=data["rules"])
|
||||
|
||||
|
||||
def evaluate_policy(log: Dict[str, Any], policy: Policy) -> bool:
|
||||
# Minimal evaluation: all top-level rules keys are checked if present in log
|
||||
# This is a small MVP; in production, rules would be richer and more formal.
|
||||
for key, thresh in policy.rules.items():
|
||||
if key == "price_improvement_min":
|
||||
if log.get("price_improvement", 0) < float(thresh):
|
||||
return False
|
||||
elif key == "latency_budget_ms":
|
||||
if log.get("latency_ms", float("inf")) > int(thresh):
|
||||
return False
|
||||
elif key == "slippage_max":
|
||||
if log.get("slippage", float("inf")) > float(thresh):
|
||||
return False
|
||||
# additional rules can be added here
|
||||
return True
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
"""Privacy-preserving statistics utilities.
|
||||
|
||||
This module provides a simple differential-privacy-friendly aggregate function
|
||||
by adding Laplace noise to the sum. The actual privacy guarantees are simplified
|
||||
for MVP purposes and should be upgraded for production.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import random
|
||||
import math
|
||||
|
||||
|
||||
def laplace_noise(scale: float) -> float:
|
||||
# Inverse transform sampling for Laplace(0, scale)
|
||||
u = random.uniform(-0.5, 0.5)
|
||||
return scale * math.copysign(1.0, u) * math.log(1 - 2 * abs(u))
|
||||
|
||||
|
||||
def privacy_aggregate(values, epsilon: float) -> float:
|
||||
if not values:
|
||||
return 0.0
|
||||
true_sum = sum(values)
|
||||
# Simple Laplace mechanism; scale = 1/epsilon
|
||||
scale = 1.0 / max(epsilon, 1e-9)
|
||||
noise = laplace_noise(scale)
|
||||
return true_sum + noise
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
"""ZKP prototype for BeXProof.
|
||||
|
||||
This is a lightweight stand-in for a real ZKP back-end. It produces a compact
|
||||
certificate-like proof deterministically derived from the log entry and policy.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import json
|
||||
import hashlib
|
||||
from .policy import Policy
|
||||
|
||||
|
||||
def generate_proof(log_entry: dict, policy: Policy) -> dict:
|
||||
# Produce a deterministic, compact proof representation (string hash)
|
||||
seed = {
|
||||
"log": log_entry,
|
||||
"policy_version": policy.version,
|
||||
"policy_rules": policy.rules,
|
||||
}
|
||||
blob = json.dumps(seed, sort_keys=True).encode("utf-8")
|
||||
proof_hash = hashlib.sha256(blob).hexdigest()
|
||||
return {"proof": f"zkp-{proof_hash}"}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
[build-system]
|
||||
requires = ["setuptools>=61", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "idea164_bexproof_verifiable_best"
|
||||
version = "0.0.1"
|
||||
description = "BeXProof: Verifiable Best Execution and Compliance Verifier for Equity Trading (Python MVP)"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.9"
|
||||
license = { text = "MIT" }
|
||||
authors = [ { name = "OpenCode SWARM" } ]
|
||||
dependencies = [
|
||||
"setuptools>=61",
|
||||
"wheel",
|
||||
"pytest>=7",
|
||||
"cryptography>=3.4; python_version >= '3.9'",
|
||||
"pydantic>=1.9; python_version >= '3.9'",
|
||||
"typing-extensions>=3.7; python_version < '3.11'",
|
||||
"fastapi>=0.78; python_version >= '3.9'",
|
||||
"uvicorn[standard]" # for potential API server in future
|
||||
]
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
from setuptools import setup, find_packages
|
||||
|
||||
setup(
|
||||
name="idea164_bexproof_verifiable_best",
|
||||
version="0.0.1",
|
||||
description="BeXProof: Verifiable Best Execution and Compliance Verifier for Equity Trading (Python MVP)",
|
||||
packages=find_packages(exclude=("tests", "docs")),
|
||||
python_requires=">=3.9",
|
||||
include_package_data=True,
|
||||
)
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
echo "Installing package in editable mode..."
|
||||
python3 -m pip install -e .
|
||||
echo "Running tests..."
|
||||
pytest -q
|
||||
echo "Building package..."
|
||||
python3 -m build
|
||||
echo "All tests passed and package built."
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
def test_adapters_basic_mapping():
|
||||
from bexproof.adapters import BrokerGatewayAdapter, ExchangeGatewayAdapter
|
||||
b = BrokerGatewayAdapter()
|
||||
e = ExchangeGatewayAdapter()
|
||||
assert isinstance(b.adapt_order({"order_id": "ORD1"}), dict)
|
||||
assert isinstance(e.adapt_execution({"order_id": "ORD1"}), dict)
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
from bexproof.governance import Governance
|
||||
|
||||
|
||||
def test_governance_approvals():
|
||||
g = Governance(signers={"alice", "bob", "carol"}, required=2)
|
||||
g.propose("p1", "policy content")
|
||||
g.approve("p1", "alice")
|
||||
assert g.is_approved("p1") is False
|
||||
g.approve("p1", "bob")
|
||||
assert g.is_approved("p1") is True
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
import json
|
||||
from pathlib import Path
|
||||
from bexproof.ledger import TamperProofLedger
|
||||
|
||||
|
||||
def test_ledger_append_and_hash(tmp_path: Path):
|
||||
ledger_path = tmp_path / "ledger.log"
|
||||
ledger = TamperProofLedger(str(ledger_path))
|
||||
entry1 = {"order_id": "ORD1", "venue": "VENUE1"}
|
||||
rec1 = ledger.append(entry1)
|
||||
assert "hash" in rec1
|
||||
entry2 = {"order_id": "ORD2", "venue": "VENUE2"}
|
||||
rec2 = ledger.append(entry2)
|
||||
assert rec2["prev_hash"] == rec1["hash"]
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
from bexproof.logs import Signer, make_signed_log, verify_log
|
||||
|
||||
|
||||
def test_sign_and_verify():
|
||||
signer = Signer("supersecretkey")
|
||||
log = make_signed_log("ORD1", "VENUE1", 101.0, 3, signer)
|
||||
assert verify_log(log, signer) is True
|
||||
# Tamper detection
|
||||
log_bad = dict(log)
|
||||
log_bad["price"] = 999.0
|
||||
assert verify_log(log_bad, signer) is False
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
import json
|
||||
import time
|
||||
from bexproof.policy import Policy, load_policy, evaluate_policy
|
||||
|
||||
|
||||
def test_policy_load_and_eval_pass():
|
||||
policy_text = json.dumps({
|
||||
"version": 1,
|
||||
"rules": {
|
||||
"price_improvement_min": 0.001,
|
||||
"latency_budget_ms": 10,
|
||||
},
|
||||
})
|
||||
policy = load_policy(policy_text)
|
||||
log = {
|
||||
"order_id": "ORD1",
|
||||
"venue": "VENUE1",
|
||||
"price": 100.5,
|
||||
"latency_ms": 5,
|
||||
"price_improvement": 0.002,
|
||||
"timestamp": int(time.time() * 1000),
|
||||
}
|
||||
assert evaluate_policy(log, policy) is True
|
||||
|
||||
|
||||
def test_policy_eval_fail_due_to_latency():
|
||||
policy_text = json.dumps({
|
||||
"version": 1,
|
||||
"rules": {
|
||||
"latency_budget_ms": 2,
|
||||
},
|
||||
})
|
||||
policy = load_policy(policy_text)
|
||||
log = {
|
||||
"order_id": "ORD2",
|
||||
"venue": "VENUE1",
|
||||
"latency_ms": 5,
|
||||
"price_improvement": 0.0005,
|
||||
"timestamp": int(time.time() * 1000),
|
||||
}
|
||||
assert evaluate_policy(log, policy) is False
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
from bexproof.privacy import privacy_aggregate
|
||||
|
||||
|
||||
def test_privacy_aggregate_basic():
|
||||
values = [1.0, 2.0, 3.0, 4.0]
|
||||
result = privacy_aggregate(values, epsilon=1.0)
|
||||
# result should be within a plausible range around the true sum (10.0)
|
||||
assert isinstance(result, float)
|
||||
assert abs(result - 10.0) < 5.0 # allow some noise margin in MVP
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
import json
|
||||
from bexproof.policy import Policy
|
||||
from bexproof.zkp import generate_proof
|
||||
|
||||
|
||||
def test_generate_proof_consistency():
|
||||
policy = Policy(version=1, rules={"price_improvement_min": 0.001})
|
||||
log_entry = {"order_id": "ORD1", "venue": "VENUE1", "price": 100.0, "latency_ms": 5}
|
||||
proof = generate_proof(log_entry, policy)
|
||||
assert "proof" in proof
|
||||
# Re-generating with same inputs should yield same proof
|
||||
proof2 = generate_proof(log_entry, policy)
|
||||
assert proof == proof2
|
||||
Loading…
Reference in New Issue