Skip to content

genlayerlabs/tls-twitter-bounty

Repository files navigation

TLS Notary Twitter Marketplace

Bounty marketplace for Twitter API requests on GenLayer. Post a request, someone fulfills it with a cryptographic proof (TLS Notary or zkTLS), validators verify, response stored on-chain.

Two proving approaches available:

  • TLS Notary - MPC-based, fast (~seconds), requires trusted notary
  • zkTLS - Zero-knowledge, trustless, slower (~minutes, needs GPU)

See COMPARISON.md for detailed analysis.

Why

Twitter keeps changing who can access their API. This creates a workaround: a marketplace where anyone can request Twitter data and anyone with API access can fulfill those requests. Responses are cryptographically verified using TLS Notary, so you don't have to trust the fulfiller.

The response gets stored on-chain. Other protocols (like Rally) can then consume this verified data.

How It Works

  1. User posts bounty: "I'll pay X for /2/users/by/username/elonmusk"
  2. Worker sees request, calls Twitter API through TLS Notary
  3. Worker submits proof, claims bounty
  4. Verified response stored on-chain

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                              GenLayer                                        β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚   TwitterMarketplace   β”‚     β”‚         TLS Verifier (WASM)            β”‚  β”‚
β”‚  β”‚      (Python)          │────▢│                                        β”‚  β”‚
β”‚  β”‚                        β”‚     β”‚  - Parses TLS Notary presentation      β”‚  β”‚
β”‚  β”‚  - Request storage     β”‚     β”‚  - Validates notary signature          β”‚  β”‚
β”‚  β”‚  - Bounty management   β”‚     β”‚  - Extracts server name, response      β”‚  β”‚
β”‚  β”‚  - Response validation β”‚     β”‚  - Returns verification result         β”‚  β”‚
β”‚  β”‚  - Response storage    β”‚     β”‚                                        β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
        β–²                                           β–²
        β”‚ create_request()                          β”‚ proof bytes
        β”‚ get_response()                            β”‚
        β”‚                                           β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”                         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚     User      β”‚                         β”‚     Worker      β”‚
β”‚               β”‚                         β”‚  (tls-prover)   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                         β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                   β”‚
                                                   β”‚ TLS Notary MPC
                                                   β–Ό
                                          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                          β”‚ Notary Server  β”‚
                                          β”‚                β”‚
                                          β”‚ Witnesses TLS  β”‚
                                          β”‚ Signs proof    β”‚
                                          β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                  β”‚
                                                  β–Ό
                                          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                          β”‚  Twitter API   β”‚
                                          β”‚ api.twitter.comβ”‚
                                          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Notary Server

Third party that participates in the TLS session via MPC (multi-party computation). The notary:

  • Cooperates with the prover to complete the TLS handshake
  • Never sees the plaintext (only encrypted data + MPC shares)
  • Signs a commitment attesting "this ciphertext came from api.twitter.com"

This creates a portable proof: anyone can verify the signature without re-running the TLS session.

Components

tls-prover/ - Worker (Rust)

Monitors the marketplace contract for unfulfilled requests. For each request:

  1. Makes HTTP request to Twitter API through TLS Notary
  2. Uses tlsn crate for MPC-TLS protocol
  3. Generates Presentation proof with selective disclosure
  4. Submits proof to marketplace contract

Stack: Rust, tlsn, tokio, serde

Build:

cd tls-prover
cargo build --release

tls-verifier-wasm/ - Verifier Contract (Rust/WASM)

Stateless contract that verifies TLS Notary proofs on-chain.

Input: Serialized proof bytes Output: JSON with valid, server_name, response_body, connection_time, notary_key_hex

The verifier:

  1. Parses the proof format (custom binary, not full Presentation)
  2. Validates the notary's signature over the commitment
  3. Extracts revealed data (server name, HTTP response)
  4. Returns structured result

Stack: Rust, compiled to wasm32-wasip1, runs in GenVM

Build:

cd tls-verifier-wasm
cargo build --release --target wasm32-wasip1
wasm-opt -O3 --disable-reference-types \
    target/wasm32-wasip1/release/tls_verifier_wasm.wasm \
    -o verifier.wasm

Note: --disable-reference-types required - GenVM doesn't support WASM reference types.

tweet-bounty/ - Marketplace Contract (Python)

Stateful contract managing the bounty marketplace.

Storage:

  • requests: TreeMap[str, str] - request_id β†’ endpoint
  • bounties: TreeMap[str, u256] - request_id β†’ amount
  • responses: TreeMap[str, str] - request_id β†’ verified response JSON
  • fulfilled: TreeMap[str, bool] - request_id β†’ fulfilled

Methods:

  • create_request(endpoint: str) - Post bounty for an endpoint (payable)
  • submit_proof(request_id: str, proof: bytes) - Submit proof, claim bounty
  • get_pending_requests() - List unfulfilled requests (for workers)
  • get_response(request_id: str) - Get stored response

Response Validation: Rejects rate limits (HTTP 429, error codes 88/89) and auth errors (401/403, codes 32/64/215). Accepts "not found" errors (codes 34/50/144) as valid responses.

Stack: Python, GenLayer SDK (genlayer-js), runs in GenVM

zktls-prover/ - zkTLS Worker (Rust)

Alternative prover using zero-knowledge proofs instead of MPC.

  1. Makes standard TLS connection to Twitter API
  2. Records TLS session using RecordableStream
  3. Generates ZK proof via SP1/RISC0
  4. Submits proof to marketplace contract

Stack: Rust wrapper around zkTLS

Build:

# Build zkTLS CLI (requires SP1)
cd zktls-repo
RISC0_SKIP_BUILD_KERNELS=1 cargo build --release -F sp1-backend

# Build wrapper
cd ../zktls-prover
cargo build --release

zktls-verifier-wasm/ - zkTLS Verifier (Rust/WASM)

Validates zkTLS proof output format.

Note: This verifier validates the proof format, not the full ZK proof. Full Groth16 verification requires specialized libraries not easily portable to WASM.

Build:

cd zktls-verifier-wasm
cargo build --release --target wasm32-wasip1
wasm-opt -O3 --disable-reference-types --enable-bulk-memory \
    target/wasm32-wasip1/release/zktls_verifier_wasm.wasm \
    -o zktls_verifier.wasm

Data Flow

1. User calls create_request("/2/users/by/username/elonmusk") with 0.001 ETH

2. Worker polls get_pending_requests(), sees new request

3. Worker runs TLS Notary flow:
   - Connects to notary server (WebSocket)
   - Starts MPC-TLS session with api.twitter.com
   - Sends GET /2/users/by/username/elonmusk
   - Receives response, generates proof
   - Serializes proof to bytes

4. Worker calls submit_proof(request_id, proof_bytes)

5. Marketplace contract:
   - Calls WASM verifier with proof bytes
   - Verifier returns {valid: true, server_name: "api.twitter.com", response_body: "..."}
   - Contract validates server_name contains "twitter.com" or "x.com"
   - Contract validates response isn't rate limited
   - Contract stores response on-chain
   - Worker receives bounty

6. Anyone can call get_response(request_id) to read verified data

GenVM Integration

The verifier runs as a WASM contract in GenVM. Key technical details:

Calldata Encoding: GenVM uses a custom binary format (not ABI):

  • ULEB128 for integers and lengths
  • Type tags: MAP(6), ARR(5), BYTES(3), STR(4), PINT(1), SPECIAL(0)
  • Map keys are raw strings (length + bytes), not TYPE_STR

WASM I/O:

  • Input: ExtendedMessage map from stdin
  • Output: JSON string to stdout

Cross-Contract Calls:

verifier = gl.get_contract_at(self.verifier_address)
result = verifier.emit().verify(proof_bytes)  # calls WASM contract

Prerequisites

  • Rust with wasm32-wasip1 target: rustup target add wasm32-wasip1
  • wasm-opt (binaryen): brew install binaryen / apt install binaryen
  • Node.js 18+: for deployment scripts
  • GenLayer Studio: Docker-based development environment
  • TLS Notary server: for generating proofs (or use public notary)
  • Twitter API key: Bearer token with v2 API access (workers only)

Setup

# Clone
git clone https://github.com/genlayerlabs/tls-twitter-bounty
cd tls-twitter-bounty

# Install Node dependencies
npm install

# Build WASM verifier
cd tls-verifier-wasm
cargo build --release --target wasm32-wasip1
wasm-opt -O3 --disable-reference-types \
    target/wasm32-wasip1/release/tls_verifier_wasm.wasm \
    -o verifier.wasm

# Build worker
cd ../tls-prover
cargo build --release

# Start GenLayer (requires genlayer-studio installed separately)
genlayer up

Deployment

From project root:

# Deploy WASM verifier
node deploy_wasm.mjs tls-verifier-wasm/verifier.wasm
# Note VERIFIER_ADDRESS

# Deploy marketplace contract
node deploy_bounty.mjs <VERIFIER_ADDRESS>
# Note BOUNTY_ADDRESS

Running Worker

cd tls-prover

# One-off mode (fulfill one request, exit)
cargo run --release -- \
    --bounty-contract <BOUNTY_ADDRESS> \
    --notary-host <NOTARY_URL> \
    --twitter-bearer <BEARER_TOKEN> \
    --one-off

# Watch mode (continuously monitor and fulfill)
cargo run --release -- \
    --bounty-contract <BOUNTY_ADDRESS> \
    --notary-host <NOTARY_URL> \
    --twitter-bearer <BEARER_TOKEN> \
    --watch

Supported Endpoints

Any Twitter API v2 endpoint:

  • /2/tweets/:id - Get tweet by ID
  • /2/users/:id - Get user by ID
  • /2/users/by/username/:username - Get user by username
  • /2/tweets/search/recent?query=... - Search tweets
  • etc.

Benchmarking

Compare TLS Notary vs zkTLS:

# Run benchmark (uses httpbin.org by default)
node benchmark/benchmark.mjs --mode mock

# With Twitter API
TWITTER_BEARER_TOKEN=xxx node benchmark/benchmark.mjs --mode mock

Results saved to benchmark/benchmark_results.json.

Directory Structure

tls-twitter-bounty/
β”œβ”€β”€ tls-prover/           # TLS Notary worker (Rust)
β”œβ”€β”€ tls-verifier-wasm/    # TLS Notary verifier (WASM)
β”œβ”€β”€ zktls-prover/         # zkTLS worker wrapper (Rust)
β”œβ”€β”€ zktls-verifier-wasm/  # zkTLS verifier (WASM)
β”œβ”€β”€ zktls-repo/           # zkTLS library (git clone)
β”œβ”€β”€ tweet-bounty/         # Marketplace contract (Python)
β”œβ”€β”€ benchmark/            # Comparison benchmarks
β”œβ”€β”€ deploy_wasm.mjs       # WASM deployment script
β”œβ”€β”€ deploy_bounty.mjs     # Bounty contract deployment
└── COMPARISON.md         # Detailed comparison

GenVM Compatibility Notes

WASM contracts must be compiled with specific constraints for GenVM:

  • No float-to-int saturation instructions (SATURATING_FLOAT_TO_INT disabled)
  • No reference types (use --disable-reference-types in wasm-opt)
  • Bulk memory operations supported
  • Return values via gl_call with calldata-encoded {"Return": value}

Open Tasks

zkTLS full verification: The current zkTLS WASM verifier only validates the output format. Full Groth16 ZK proof verification requires implementing pairing operations, which is complex in WASM.

License

MIT - see LICENSE

About

TLS Notary Twitter Marketplace - bounty system for verified Twitter API responses

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors