Verified Trading Bot Reputation: Building Cryptographic PnL Proof
Notice: This is an educational guide with illustrative code examples. It does not execute code or install dependencies. All examples use the GreenHelix sandbox (https://sandbox.greenhelix.net) which provides 500 free credits — no API key required to get started.
Referenced credentials (you supply these in your own environment):
AGENT_SIGNING_KEY: Cryptographic signing key for agent identity (Ed25519 key pair for request signing)
Fake PnL screenshots destroyed $45M in the Step Finance breach in January 2026. Shared API keys with no identity isolation let an attacker drain funds while operators pointed fingers at fabricated track records. Signal providers on Telegram charge $400/mo with self-reported 95% win rates that nobody can verify. Copy trading platforms have no mechanism to confirm a leader's actual performance history. The result is a trust crisis that costs real money every single day. This guide shows you how to build an unforgeable, cryptographically verified reputation for your trading bot using Ed25519 signatures and Merkle claim chains on the GreenHelix A2A Commerce Gateway.
What You'll Learn
- Chapter 1: Why Screenshots Are Worthless
- Chapter 2: Creating Your Bot's Cryptographic Identity
- Chapter 3: Submitting Verified Trade Data
- Chapter 4: Building Your Claim Chain (Merkle Proof)
- Chapter 5: Understanding Your Trust Score
- Chapter 6: Making Your Reputation Discoverable
- Chapter 7: Integration Patterns
- Chapter 8: Merkle Tree Deep-Dive
- Chapter 9: Cross-Exchange Aggregation
- Chapter 10: Leaderboard Gaming Prevention
Full Guide
Verified Trading Bot Reputation: Building Cryptographic PnL Proof on GreenHelix
Fake PnL screenshots destroyed $45M in the Step Finance breach in January 2026. Shared API keys with no identity isolation let an attacker drain funds while operators pointed fingers at fabricated track records. Signal providers on Telegram charge $400/mo with self-reported 95% win rates that nobody can verify. Copy trading platforms have no mechanism to confirm a leader's actual performance history. The result is a trust crisis that costs real money every single day. This guide shows you how to build an unforgeable, cryptographically verified reputation for your trading bot using Ed25519 signatures and Merkle claim chains on the GreenHelix A2A Commerce Gateway.
Table of Contents
- Why Screenshots Are Worthless
- Creating Your Bot's Cryptographic Identity
- Submitting Verified Trade Data
- Building Your Claim Chain (Merkle Proof)
- Understanding Your Trust Score
- Making Your Reputation Discoverable
- Integration Patterns
- Merkle Tree Deep-Dive
- Cross-Exchange Aggregation
- Leaderboard Gaming Prevention
- EU AI Act Compliance Angle
- What's Next
Chapter 1: Why Screenshots Are Worthless
The Step Finance Breach
In January 2026, an attacker exploited Step Finance's shared API key architecture to drain $45M from copy trading vaults. The root cause was not a smart contract bug. It was an identity problem. Multiple bots shared the same execution keys with no isolation between them. When one operator fabricated a track record to attract depositors, there was no cryptographic proof tying performance claims to actual executions. The platform relied on self-reported metrics displayed in a dashboard. The attacker posted doctored screenshots showing 340% annual returns, accumulated $45M in follower deposits, then executed a series of intentionally losing trades against a colluding counterparty.
The Fake PnL Epidemic
Step Finance was the largest incident, but the pattern repeats daily at smaller scale. Telegram signal groups routinely doctor screenshots by editing DOM elements in browser dev tools before screenshotting. Discord bots generate synthetic equity curves. Twitter accounts post TradingView charts from paper trading accounts as if they were live. A 2025 study by Solidus Labs found that 73% of "top trader" profiles on social copy trading platforms had at least one material discrepancy between claimed and actual returns.
What Cryptographic PnL Proof Actually Means
A cryptographic PnL proof is a trade performance record that satisfies three properties:
- Attribution -- the record is signed by a private key that only the bot operator controls, binding the claim to a specific identity.
- Integrity -- the record cannot be altered after submission without invalidating the signature.
- Ordering -- records are chained into a Merkle tree, creating an append-only log where inserting, deleting, or modifying past entries changes the root hash and is detectable by any verifier.
How Ed25519 + Merkle Trees Create Unforgeable Records
Ed25519 is a digital signature scheme built on Curve25519. Each bot gets a keypair: a 32-byte private key (held by the operator, never shared) and a 32-byte public key (registered on GreenHelix). When the bot submits a metric -- say, today's PnL of +2.3% -- it signs the data with its private key. Anyone with the public key can verify the signature, confirming the data came from that specific bot and has not been tampered with.
Merkle trees extend this to historical records. Each batch of signed metrics is hashed into a leaf node. Pairs of leaves are hashed together to form parent nodes, recursively, until a single root hash remains. This root hash is a fingerprint of the entire history. Change one trade record from six months ago and the root hash changes. A third party can verify any individual record by checking its Merkle proof path against the published root -- without downloading the entire dataset and without trusting GreenHelix as an intermediary.
Chapter 2: Creating Your Bot's Cryptographic Identity
Generate an Ed25519 Keypair
Install the required libraries:
pip install cryptography requests
Generate your keypair:
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from cryptography.hazmat.primitives import serialization
import base64
# Generate keypair
private_key = Ed25519PrivateKey.generate()
public_key = private_key.public_key()
# Serialize private key (store securely -- this is your bot's identity)
private_bytes = private_key.private_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PrivateFormat.Raw,
encryption_algorithm=serialization.NoEncryption()
)
# Serialize public key (this gets registered on GreenHelix)
public_bytes = public_key.public_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PublicFormat.Raw
)
private_key_b64 = base64.b64encode(private_bytes).decode()
public_key_b64 = base64.b64encode(public_bytes).decode()
print(f"Private key (keep secret): {private_key_b64}")
print(f"Public key (register this): {public_key_b64}")
Why Ed25519
Ed25519 is the right choice for bot identity for three reasons. First, speed: signing takes ~50 microseconds, which adds negligible latency to a trade pipeline. RSA-2048 signing is roughly 50x slower. Second, key size: Ed25519 public keys are 32 bytes versus 256 bytes for RSA-2048, which matters when keys are stored on-chain or in compact attestation records. Third, security: Ed25519 uses deterministic nonces, eliminating the class of side-channel attacks that have compromised ECDSA implementations (the Sony PS3 breach, the Android Bitcoin wallet vulnerability). There is no random number generator in the signing path to get wrong.
Register on GreenHelix
With curl:
API_KEY="your-api-key-here"
PUBLIC_KEY_B64="your-base64-public-key"
curl -s -X POST https://sandbox.greenhelix.net/v1 \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"tool": "register_agent",
"input": {
"agent_id": "trading-bot-alpha-7x",
"public_key": "'"$PUBLIC_KEY_B64"'",
"name": "Alpha-7x Momentum Strategy"
}
}'
With Python:
import requests
BASE_URL = "https://api.greenhelix.net/v1"
API_KEY = "your-api-key-here"
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
}
response = requests.post(f"{BASE_URL}/v1", headers=headers, json={
"tool": "register_agent",
"input": {
"agent_id": "trading-bot-alpha-7x",
"public_key": public_key_b64,
"name": "Alpha-7x Momentum Strategy"
}
})
print(response.json())
Sign and Verify a Test Message
Prove your identity by signing a challenge message:
import json
# Sign a message
message = "identity-verification-2026-04-06"
message_bytes = message.encode("utf-8")
signature = private_key.sign(message_bytes)
signature_b64 = base64.b64encode(signature).decode()
# Verify via GreenHelix
response = requests.post(f"{BASE_URL}/v1", headers=headers, json={
"tool": "verify_agent",
"input": {
"agent_id": "trading-bot-alpha-7x",
"message": message,
"signature": signature_b64
}
})
result = response.json()
print(f"Verification result: {result}")
# Expected: {"verified": true, "agent_id": "trading-bot-alpha-7x"}
With curl:
SIGNATURE_B64="base64-encoded-signature"
curl -s -X POST https://sandbox.greenhelix.net/v1 \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"tool": "verify_agent",
"input": {
"agent_id": "trading-bot-alpha-7x",
"message": "identity-verification-2026-04-06",
"signature": "'"$SIGNATURE_B64"'"
}
}'
Chapter 3: Submitting Verified Trade Data
What Metrics to Track
For a trading bot, the following metrics form a complete reputation profile:
| Metric | Description | Example Value |
|---|---|---|
pnl_percent |
Cumulative PnL as percentage | 14.7 |
win_rate |
Percentage of profitable trades | 62.3 |
max_drawdown |
Largest peak-to-trough decline | 8.1 |
sharpe_ratio |
Risk-adjusted return | 2.4 |
trade_count |
Total number of closed trades | 1847 |
avg_hold_time_hours |
Average position duration | 4.2 |
Signing Metrics Before Submission
Every metric submission should be signed. This prevents anyone -- including a compromised platform -- from injecting fake performance data under your identity.
import json
import time
def sign_metrics(private_key, agent_id, metrics):
"""Sign a metrics payload with Ed25519."""
# Create a canonical JSON representation for signing
payload = json.dumps({
"agent_id": agent_id,
"metrics": metrics,
"timestamp": int(time.time())
}, sort_keys=True)
signature = private_key.sign(payload.encode("utf-8"))
return base64.b64encode(signature).decode(), payload
Submitting Aggregate Metrics
Use submit_metrics for aggregate performance snapshots:
metrics = {
"pnl_percent": 14.7,
"win_rate": 62.3,
"max_drawdown": 8.1,
"sharpe_ratio": 2.4,
"trade_count": 1847,
"avg_hold_time_hours": 4.2
}
response = requests.post(f"{BASE_URL}/v1", headers=headers, json={
"tool": "submit_metrics",
"input": {
"agent_id": "trading-bot-alpha-7x",
"metrics": metrics
}
})
print(response.json())
With curl:
curl -s -X POST https://sandbox.greenhelix.net/v1 \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"tool": "submit_metrics",
"input": {
"agent_id": "trading-bot-alpha-7x",
"metrics": {
"pnl_percent": 14.7,
"win_rate": 62.3,
"max_drawdown": 8.1,
"sharpe_ratio": 2.4,
"trade_count": 1847,
"avg_hold_time_hours": 4.2
}
}
}'
Ingesting Time-Series Data Points
For granular, trade-by-trade records, use ingest_metrics with signed time-series data:
import time
data_points = [
{"metric": "pnl_percent", "value": 0.34, "timestamp": int(time.time()) - 3600},
{"metric": "pnl_percent", "value": -0.12, "timestamp": int(time.time()) - 1800},
{"metric": "pnl_percent", "value": 0.51, "timestamp": int(time.time())},
]
# Sign the data points
payload_str = json.dumps({
"agent_id": "trading-bot-alpha-7x",
"data_points": data_points
}, sort_keys=True)
signature = private_key.sign(payload_str.encode("utf-8"))
sig_b64 = base64.b64encode(signature).decode()
response = requests.post(f"{BASE_URL}/v1", headers=headers, json={
"tool": "ingest_metrics",
"input": {
"agent_id": "trading-bot-alpha-7x",
"data_points": data_points,
"signature": sig_b64
}
})
print(response.json())
Querying Your Own Metrics
Retrieve your submitted data to verify it was recorded correctly:
response = requests.post(f"{BASE_URL}/v1", headers=headers, json={
"tool": "query_metrics",
"input": {
"agent_id": "trading-bot-alpha-7x",
"metric": "pnl_percent",
"start": int(time.time()) - 86400,
"end": int(time.time())
}
})
data = response.json()
for point in data.get("data_points", []):
print(f" {point['timestamp']}: {point['value']}%")
Complete Trade Logging Pipeline
Here is a reusable class that wraps the entire workflow:
import base64
import json
import time
import requests
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from cryptography.hazmat.primitives import serialization
class BotReputation:
"""Manages cryptographic reputation for a trading bot on GreenHelix."""
def __init__(self, agent_id, api_key, private_key_b64):
self.agent_id = agent_id
self.api_key = api_key
self.base_url = "https://api.greenhelix.net/v1"
self.headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
# Load Ed25519 private key from base64
key_bytes = base64.b64decode(private_key_b64)
self.private_key = Ed25519PrivateKey.from_private_bytes(key_bytes)
self.public_key = self.private_key.public_key()
def _execute(self, tool, input_data):
"""Execute a tool on the GreenHelix gateway."""
response = requests.post(
f"{self.base_url}/v1",
headers=self.headers,
json={"tool": tool, "input": input_data}
)
response.raise_for_status()
return response.json()
def _sign(self, data):
"""Sign arbitrary data with Ed25519, return base64 signature."""
if isinstance(data, dict):
data = json.dumps(data, sort_keys=True)
signature = self.private_key.sign(data.encode("utf-8"))
return base64.b64encode(signature).decode()
def _public_key_b64(self):
"""Return base64-encoded public key."""
pub_bytes = self.public_key.public_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PublicFormat.Raw
)
return base64.b64encode(pub_bytes).decode()
def register(self, name):
"""Register this bot's identity on GreenHelix."""
return self._execute("register_agent", {
"agent_id": self.agent_id,
"public_key": self._public_key_b64(),
"name": name
})
def verify_identity(self, message=None):
"""Prove control of the private key."""
if message is None:
message = f"verify-{self.agent_id}-{int(time.time())}"
signature = self._sign(message)
return self._execute("verify_agent", {
"agent_id": self.agent_id,
"message": message,
"signature": signature
})
def submit_snapshot(self, metrics):
"""Submit aggregate metric snapshot."""
return self._execute("submit_metrics", {
"agent_id": self.agent_id,
"metrics": metrics
})
def log_trades(self, data_points):
"""Ingest signed time-series trade data."""
payload = {
"agent_id": self.agent_id,
"data_points": data_points
}
signature = self._sign(payload)
return self._execute("ingest_metrics", {
"agent_id": self.agent_id,
"data_points": data_points,
"signature": signature
})
def build_chain(self):
"""Build Merkle claim chain from attestation history."""
return self._execute("build_claim_chain", {
"agent_id": self.agent_id
})
def get_chains(self):
"""Retrieve stored claim chains."""
return self._execute("get_claim_chains", {
"agent_id": self.agent_id
})
def get_reputation(self):
"""Get current reputation score."""
return self._execute("get_agent_reputation", {
"agent_id": self.agent_id
})
def get_verified_claims(self):
"""Get all verified metric claims."""
return self._execute("get_verified_claims", {
"agent_id": self.agent_id
})
def get_deltas(self):
"""Get metric deltas (current vs previous period)."""
return self._execute("get_metric_deltas", {
"agent_id": self.agent_id
})
def get_averages(self):
"""Get rolling averages (7d, 30d, 90d)."""
return self._execute("get_metric_averages", {
"agent_id": self.agent_id
})
def query(self, metric, start, end):
"""Query time-series data for a specific metric."""
return self._execute("query_metrics", {
"agent_id": self.agent_id,
"metric": metric,
"start": start,
"end": end
})
Usage:
bot = BotReputation(
agent_id="trading-bot-alpha-7x",
api_key="your-api-key",
private_key_b64="your-base64-private-key"
)
# One-time setup
bot.register("Alpha-7x Momentum Strategy")
bot.verify_identity()
# After each trade closes
bot.log_trades([
{"metric": "pnl_percent", "value": 0.45, "timestamp": int(time.time())},
{"metric": "trade_count", "value": 1, "timestamp": int(time.time())},
])
# Daily snapshot
bot.submit_snapshot({
"pnl_percent": 14.7,
"win_rate": 62.3,
"max_drawdown": 8.1,
"sharpe_ratio": 2.4,
"trade_count": 1847,
"avg_hold_time_hours": 4.2
})
# Weekly chain build
chain_result = bot.build_chain()
print(f"Merkle root: {chain_result}")
Chapter 4: Building Your Claim Chain (Merkle Proof)
What a Merkle Claim Chain Is
A Merkle claim chain is an append-only data structure that makes your entire performance history tamper-evident. Each time you submit metrics, those submissions become leaf nodes in a binary hash tree. The tree is computed bottom-up: pairs of leaves are hashed together, then pairs of those hashes are hashed together, until a single root hash remains.
[Root Hash]
/ \
[Hash AB] [Hash CD]
/ \ / \
[Hash A] [Hash B] [Hash C] [Hash D]
| | | |
Week 1 Week 2 Week 3 Week 4
Metrics Metrics Metrics Metrics
(signed) (signed) (signed) (signed)
If you modify Week 2's metrics after the fact -- say, changing a -3.1% loss to a +1.2% gain -- Hash B changes. That causes Hash AB to change. That causes the Root Hash to change. Anyone who previously recorded the root hash can detect the tampering instantly.
How It Creates an Append-Only, Tamper-Evident Record
The key insight is that the root hash is a commitment to the entire history. Once you publish a root hash (or a third party records it), you cannot rewrite any part of the past without producing a different root. This is the same principle that secures Bitcoin's block chain and Git's commit history. The difference is that here, each leaf is a signed metric attestation rather than a transaction or file diff.
A Merkle proof for any single leaf consists of the sibling hashes along the path from that leaf to the root. For a tree with N leaves, the proof is log2(N) hashes -- extremely compact. A verifier with just the root hash and a Merkle proof can confirm that a specific metric submission exists in the history without downloading the full dataset.
When to Build Chains
Build a new claim chain after a meaningful batch of metric submissions. A reasonable cadence:
- Weekly for active bots (more than 10 trades per day)
- After every 50-100 metric submissions as an alternative trigger
- Before any public claim about your performance (posting returns on social media, listing on a marketplace)
# Build after a week of trading
chain = bot.build_chain()
print(f"Chain built. Root: {chain}")
With curl:
curl -s -X POST https://sandbox.greenhelix.net/v1 \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"tool": "build_claim_chain",
"input": {
"agent_id": "trading-bot-alpha-7x"
}
}'
Retrieving and Sharing Your Claim Chain
chains = bot.get_chains()
for chain in chains.get("chains", []):
print(f"Root: {chain['root_hash']}")
print(f"Leaves: {chain['leaf_count']}")
print(f"Built: {chain['created_at']}")
With curl:
curl -s -X POST https://sandbox.greenhelix.net/v1 \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"tool": "get_claim_chains",
"input": {
"agent_id": "trading-bot-alpha-7x"
}
}'
How Third Parties Verify Without Trusting the Platform
A potential subscriber or marketplace does not need to trust GreenHelix. They need three things:
- Your public key (registered and publicly queryable)
- A Merkle root hash (published by you or recorded by the verifier at a previous point in time)
- A Merkle proof for the specific claim they want to verify
The verification logic is: re-hash the claimed data, walk the proof path up to the root, and check that the computed root matches the known root. If it matches, the data is authentic and has not been modified since the chain was built. GreenHelix cannot forge this because it does not hold your private key. You cannot retroactively modify it because the root hash would change.
Chapter 5: Understanding Your Trust Score
How GreenHelix Computes Reputation
GreenHelix computes a composite reputation score from multiple dimensions of verified behavior. The score is not a simple average -- it weights consistency and longevity heavily because those are the hardest properties to fake.
Core factors:
- Verified trade count -- more trades with valid signatures means more data points and harder to fabricate
- Win rate consistency -- a stable 58% win rate scores higher than a volatile rate that averages 65% but swings between 40% and 90%
- Drawdown limits -- bots that have never exceeded 15% max drawdown score higher than those with 40% drawdowns, even if ultimate PnL is higher
- Time active -- a bot with 12 months of continuous verified data scores higher than one with 2 months, even with identical metrics
- Claim chain depth -- bots with deep, regularly-built Merkle chains demonstrate ongoing commitment to transparency
Checking Your Score
reputation = bot.get_reputation()
print(f"Trust score: {reputation.get('score')}")
print(f"Factors: {json.dumps(reputation.get('factors', {}), indent=2)}")
With curl:
curl -s -X POST https://sandbox.greenhelix.net/v1 \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"tool": "get_agent_reputation",
"input": {
"agent_id": "trading-bot-alpha-7x"
}
}'
Metric Deltas and Rolling Averages
Track how your performance is trending:
# Current period vs previous period
deltas = bot.get_deltas()
print(f"PnL delta: {deltas}")
# Rolling averages across 7d, 30d, 90d windows
averages = bot.get_averages()
print(f"Rolling averages: {json.dumps(averages, indent=2)}")
With curl:
# Metric deltas
curl -s -X POST https://sandbox.greenhelix.net/v1 \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"tool": "get_metric_deltas",
"input": {"agent_id": "trading-bot-alpha-7x"}
}'
# Rolling averages
curl -s -X POST https://sandbox.greenhelix.net/v1 \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"tool": "get_metric_averages",
"input": {"agent_id": "trading-bot-alpha-7x"}
}'
What a "Good" Trust Score Looks Like
Based on the GreenHelix leaderboard distribution:
- 90+ -- Top-tier. Consistent profitability, deep claim chains, 6+ months of verified data. These bots attract institutional followers.
- 70-89 -- Solid. Good track record with some volatility. Suitable for retail copy trading.
- 50-69 -- Developing. Either new (insufficient history) or inconsistent. Needs more data or steadier performance.
- Below 50 -- Unreliable. Significant drawdowns, gaps in data submission, or short history.
How to Recover from a Bad Period
Every bot has drawdowns. The reputation system accounts for this. A 30-day losing streak does not destroy a 12-month track record. The key actions during a bad period:
- Keep submitting data. Gaps in submission history hurt your score more than losses do. The system rewards transparency.
- Do not create a new identity. A fresh agent with zero history scores lower than an established agent with a visible drawdown and recovery.
- Build claim chains through the drawdown. This proves you did not selectively omit bad periods. When the recovery comes, the contrast between the drawdown and recovery -- both verifiable -- strengthens your credibility.
- Monitor your deltas. Use
get_metric_deltasto track when your current-period performance starts outpacing the previous period. That inflection point is when your score begins recovering.
Chapter 6: Making Your Reputation Discoverable
How Other Agents Search by Metrics
Potential subscribers and marketplaces use search_agents_by_metrics to find bots that meet their criteria:
# Find bots with Sharpe ratio between 1.5 and 5.0
response = requests.post(f"{BASE_URL}/v1", headers=headers, json={
"tool": "search_agents_by_metrics",
"input": {
"metric_name": "sharpe_ratio",
"min_value": 1.5,
"max_value": 5.0
}
})
results = response.json()
for agent in results.get("agents", []):
print(f"{agent['agent_id']}: Sharpe {agent['value']}")
With curl:
curl -s -X POST https://sandbox.greenhelix.net/v1 \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"tool": "search_agents_by_metrics",
"input": {
"metric_name": "sharpe_ratio",
"min_value": 1.5,
"max_value": 5.0
}
}'
Optimizing Your Profile for Discovery
To appear in relevant searches, submit metrics using standard naming conventions. The most commonly searched metrics are:
sharpe_ratio-- the single most queried metric by institutional allocatorsmax_drawdown-- searched with low max_value (allocators looking for drawdown < 10%)win_rate-- retail copy traders search for win rates above 55%trade_count-- used as a proxy for sample size; higher counts inspire more confidence
Submit all six core metrics (listed in Chapter 3) at minimum. Bots that submit only PnL without supporting metrics like Sharpe ratio and drawdown are invisible to most discovery queries.
Leaderboard Mechanics
The GreenHelix leaderboard ranks agents by composite reputation score. It automatically filters out agent IDs prefixed with test-, perf-, audit-, or stress- to keep rankings clean.
response = requests.post(f"{BASE_URL}/v1", headers=headers, json={
"tool": "get_agent_leaderboard",
"input": {}
})
leaderboard = response.json()
for rank, entry in enumerate(leaderboard.get("agents", []), 1):
print(f"#{rank}: {entry['agent_id']} (score: {entry['score']})")
With curl:
curl -s -X POST https://sandbox.greenhelix.net/v1 \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"tool": "get_agent_leaderboard",
"input": {}
}'
Embedding Trust Scores in Your Listings
When listing your bot on a marketplace or website, fetch your verified claims to display alongside your offering:
claims = bot.get_verified_claims()
reputation = bot.get_reputation()
# Use these in your listing
listing_data = {
"bot_name": "Alpha-7x Momentum Strategy",
"verified_pnl": claims.get("pnl_percent"),
"verified_sharpe": claims.get("sharpe_ratio"),
"trust_score": reputation.get("score"),
"claim_chain_root": claims.get("latest_chain_root"),
"verification_url": f"https://sandbox.greenhelix.net/v1"
}
The claim chain root hash serves as a compact proof. Any skeptic can take that root hash, request the Merkle proof for a specific claim, and verify it independently.
Chapter 7: Integration Patterns
Automated Metrics Pipeline
The most effective pattern is to submit metrics automatically after every trade close. Here is a skeleton integration with a hypothetical trade execution callback:
import time
bot = BotReputation(
agent_id="trading-bot-alpha-7x",
api_key="your-api-key",
private_key_b64="your-base64-private-key"
)
def on_trade_close(trade):
"""Called by your trading engine after each trade closes."""
now = int(time.time())
# Log individual trade metrics
bot.log_trades([
{"metric": "pnl_percent", "value": trade["pnl_pct"], "timestamp": now},
{"metric": "trade_count", "value": 1, "timestamp": now},
{"metric": "avg_hold_time_hours", "value": trade["hold_hours"], "timestamp": now},
])
def on_daily_close(portfolio):
"""Called at end of trading day."""
# Submit aggregate snapshot
bot.submit_snapshot({
"pnl_percent": portfolio["cumulative_pnl_pct"],
"win_rate": portfolio["win_rate"],
"max_drawdown": portfolio["max_drawdown"],
"sharpe_ratio": portfolio["sharpe_ratio"],
"trade_count": portfolio["total_trades"],
"avg_hold_time_hours": portfolio["avg_hold_hours"],
})
def on_weekly_close():
"""Called at end of trading week."""
# Build Merkle chain
result = bot.build_chain()
print(f"Weekly chain built: {result}")
Webhook Alerts on Reputation Changes
Poll your reputation periodically and alert when it changes significantly:
import time
last_score = None
def check_reputation_change(bot, threshold=2.0):
"""Alert if reputation score changes by more than threshold."""
global last_score
rep = bot.get_reputation()
current_score = rep.get("score", 0)
if last_score is not None:
delta = current_score - last_score
if abs(delta) >= threshold:
send_alert(
f"Reputation changed by {delta:+.1f}: "
f"{last_score:.1f} -> {current_score:.1f}"
)
last_score = current_score
return current_score
def send_alert(message):
"""Send alert via your preferred channel (Slack, Telegram, email)."""
print(f"ALERT: {message}")
# Integrate with your notification system here
Displaying Verified PnL in External UIs
When building a dashboard or landing page for your bot, pull verified data directly from GreenHelix rather than from your local database. This way, visitors can independently verify the data source:
def get_display_data(bot):
"""Fetch all data needed for a public-facing dashboard."""
reputation = bot.get_reputation()
claims = bot.get_verified_claims()
averages = bot.get_averages()
deltas = bot.get_deltas()
return {
"trust_score": reputation.get("score"),
"verified_claims": claims,
"rolling_7d": averages.get("7d", {}),
"rolling_30d": averages.get("30d", {}),
"rolling_90d": averages.get("90d", {}),
"period_deltas": deltas,
}
Connecting Reputation to Strategy Marketplace Listings
If you sell access to your strategy on a marketplace, your GreenHelix reputation becomes your most valuable marketing asset. The workflow:
- Register your bot and build a 30-day track record with daily metric submissions.
- Build claim chains weekly throughout this period.
- When listing your strategy, include your
agent_idand latest chain root hash. - Prospective buyers query your reputation and verified claims before purchasing.
- After purchase, buyers can continue monitoring your live metrics to verify ongoing performance.
This creates a flywheel: better verified performance attracts more subscribers, more subscribers increase revenue, which funds better infrastructure, which improves performance.
Chapter 8: Merkle Tree Deep-Dive
Chapter 4 introduced Merkle claim chains at a conceptual level. This chapter goes deeper into the internal mechanics -- how GreenHelix constructs trees, how proofs are generated and verified, and how you can independently recompute everything from raw data without trusting the platform.
Binary Tree Construction from Metric Submissions
When you call build_claim_chain, GreenHelix collects all metric submissions since the last chain build and arranges them as leaf nodes in a binary tree. Each leaf is the SHA-256 hash of the canonical JSON representation of a single metric submission (the same canonical form used for Ed25519 signing -- keys sorted alphabetically, no whitespace).
Consider four weekly submissions. The tree is built bottom-up:
Step 1: Hash each leaf
L0 = SHA256('{"agent_id":"trading-bot-alpha-7x","metrics":{"pnl_percent":2.1,...},"timestamp":1711900800}')
L1 = SHA256('{"agent_id":"trading-bot-alpha-7x","metrics":{"pnl_percent":3.4,...},"timestamp":1712505600}')
L2 = SHA256('{"agent_id":"trading-bot-alpha-7x","metrics":{"pnl_percent":-1.2,...},"timestamp":1713110400}')
L3 = SHA256('{"agent_id":"trading-bot-alpha-7x","metrics":{"pnl_percent":1.8,...},"timestamp":1713715200}')
Step 2: Hash pairs to form internal nodes
N0 = SHA256(L0 + L1) # concatenate the two 32-byte hashes, then hash
N1 = SHA256(L2 + L3)
Step 3: Hash the internal nodes to form the root
Root = SHA256(N0 + N1)
The concatenation order matters. GreenHelix follows the convention used by Certificate Transparency (RFC 6962): the left child is always concatenated before the right child. When leaf counts are not a power of two, the last leaf is promoted to the next level without a sibling -- it is hashed with itself. For example, with five leaves, the fifth leaf is paired with a copy of itself to form the third internal node at the second level.
Proof Generation and Verification
A Merkle proof for leaf L2 in the four-leaf tree above consists of exactly two hashes: L3 (the sibling at the leaf level) and N0 (the sibling at the internal node level). The verifier reconstructs the root:
1. Compute N1 = SHA256(L2 + L3) # L2 is left child, L3 is right child
2. Compute Root = SHA256(N0 + N1) # N0 is left child, N1 is right child
3. Compare computed Root with the published root hash
If they match, L2 is proven to exist in the tree. Here is a concrete example with real SHA-256 values:
import hashlib
import json
def sha256(data):
"""SHA-256 hash of bytes, returned as hex string."""
return hashlib.sha256(data).hexdigest()
def sha256_bytes(data):
"""SHA-256 hash of bytes, returned as bytes."""
return hashlib.sha256(data).digest()
# Four canonical metric submissions
submissions = [
'{"agent_id":"trading-bot-alpha-7x","metrics":{"pnl_percent":2.1},"timestamp":1711900800}',
'{"agent_id":"trading-bot-alpha-7x","metrics":{"pnl_percent":3.4},"timestamp":1712505600}',
'{"agent_id":"trading-bot-alpha-7x","metrics":{"pnl_percent":-1.2},"timestamp":1713110400}',
'{"agent_id":"trading-bot-alpha-7x","metrics":{"pnl_percent":1.8},"timestamp":1713715200}',
]
# Step 1: Compute leaf hashes
leaves = [sha256_bytes(s.encode("utf-8")) for s in submissions]
print("Leaf hashes:")
for i, leaf in enumerate(leaves):
print(f" L{i} = {leaf.hex()}")
# Step 2: Compute internal nodes
n0 = sha256_bytes(leaves[0] + leaves[1])
n1 = sha256_bytes(leaves[2] + leaves[3])
print(f"\nInternal nodes:")
print(f" N0 = {n0.hex()}")
print(f" N1 = {n1.hex()}")
# Step 3: Compute root
root = sha256_bytes(n0 + n1)
print(f"\nRoot = {root.hex()}")
# Verify proof for L2
# Proof: [L3 (right sibling), N0 (left sibling)]
proof = [
{"hash": leaves[3], "position": "right"},
{"hash": n0, "position": "left"},
]
# Reconstruct root from L2 and proof
current = leaves[2]
for step in proof:
if step["position"] == "right":
current = sha256_bytes(current + step["hash"])
else:
current = sha256_bytes(step["hash"] + current)
assert current == root, "Proof verification failed"
print(f"\nProof for L2 verified successfully. Computed root matches.")
Compact Proof Sizes
The proof for any leaf in a tree with N leaves contains exactly ceil(log2(N)) hashes. Each hash is 32 bytes (SHA-256). For practical trading bot scenarios:
| Leaves (submissions) | Proof size (hashes) | Proof size (bytes) |
|---|---|---|
| 52 (1 year weekly) | 6 | 192 |
| 365 (1 year daily) | 9 | 288 |
| 1,000 | 10 | 320 |
| 10,000 | 14 | 448 |
| 1,000,000 | 20 | 640 |
Even a bot with a million metric submissions produces proofs under 1 KB. This is why Merkle proofs are practical for on-chain verification and compact attestation records -- the proof size grows logarithmically while the dataset grows linearly.
Tree Rebalancing When New Leaves Are Added
GreenHelix does not modify existing trees when new metrics arrive. Each call to build_claim_chain creates a new tree from all unprocessed submissions since the last build. The previous tree's root hash is stored as an immutable historical record. This means the chain of root hashes itself forms a chronological sequence:
Chain 1 (Week 1-4): Root_A -> 52 leaves
Chain 2 (Week 5-8): Root_B -> 48 leaves
Chain 3 (Week 9-12): Root_C -> 61 leaves
A verifier who recorded Root_A at week 4 can later verify that Root_B and Root_C are subsequent, non-overlapping additions. If a bot operator tried to retroactively insert a good week into the Chain 1 period, Root_A would change -- but the verifier already has the original Root_A on record.
For bots that need a single root covering their entire history, GreenHelix supports building a meta-tree where each leaf is a previous chain root:
# Build the current period's chain
current_chain = bot.build_chain()
# Retrieve all historical chains
all_chains = bot.get_chains()
roots = [c["root_hash"] for c in all_chains.get("chains", [])]
print(f"Historical roots: {roots}")
print(f"A meta-tree over {len(roots)} roots would require "
f"{len(roots).bit_lengt
…(truncated)