Skip to content

Commit

Permalink
✨ Add HTTP client for EthProofs API (#5)
Browse files Browse the repository at this point in the history
* ✨ Add HTTP client for EthProofs API

* 🚸 Add example
  • Loading branch information
nmvalera authored Feb 6, 2025
1 parent f80c61f commit a83245a
Show file tree
Hide file tree
Showing 14 changed files with 1,076 additions and 0 deletions.
194 changes: 194 additions & 0 deletions ethproofs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
# EthProofs Client

The **EthProofs Client** is a Go package that provides a clean interface for interacting with the EthProofs API, enabling management of proving operations, clusters, and proof submissions.

## Overview

This package provides:
- A strongly-typed client interface for all EthProofs API endpoints
- HTTP implementation using Azure's autorest for robust HTTP client management
- Mock implementation for testing using uber-go/mock
- Comprehensive example usage and tests

## Installation

```sh
go get github.com/kkrt-labs/go-utils/ethproofs
```

## Authentication

All API endpoints require authentication using an API key. You can obtain one by:
1. Joining the EthProofs Slack workspace
2. Requesting an API key from the team (contact Elias)

## Usage

### Creating a Client

```go
import (
"github.com/kkrt-labs/kakarot-controller/go-utils/ethproofs"
ethproofshttp "github.com/kkrt-labs/kakarot-controller/go-utils/ethproofs/client/http"
)

// Create client with configuration
client, err := ethproofshttp.NewClient(&ethproofshttp.Config{
Addr: "https://staging--ethproofs.netlify.app/api/v0",
APIKey: "your-api-key",
})
if err != nil {
log.Fatal(err)
}
```

### Managing Clusters

```go
// Create a cluster
cluster, err := client.CreateCluster(context.Background(), &ethproofs.CreateClusterRequest{
Nickname: "test-cluster",
Description: "Test cluster for proving operations",
Hardware: "RISC-V Prover",
CycleType: "SP1",
ProofType: "Groth16",
Configuration: []ethproofs.ClusterConfig{
{
InstanceType: "t3.small",
InstanceCount: 1,
},
},
})

// List all clusters
clusters, err := client.ListClusters(context.Background())
```

### Managing Single Machines

```go
// Create a single machine
machine, err := client.CreateMachine(context.Background(), &ethproofs.CreateMachineRequest{
Nickname: "test-machine",
Description: "Single machine for proving",
Hardware: "RISC-V Prover",
CycleType: "SP1",
ProofType: "Groth16",
InstanceType: "t3.small",
})
```

### Proof Lifecycle

```go
// 1. Queue a proof
queuedProof, err := client.QueueProof(context.Background(), &ethproofs.QueueProofRequest{
BlockNumber: 12345,
ClusterID: cluster.ID,
})

// 2. Start proving
startedProof, err := client.StartProving(context.Background(), &ethproofs.StartProvingRequest{
BlockNumber: 12345,
ClusterID: cluster.ID,
})

// 3. Submit completed proof
provingCycles := int64(1000000)
submittedProof, err := client.SubmitProof(context.Background(), &ethproofs.SubmitProofRequest{
BlockNumber: 12345,
ClusterID: cluster.ID,
ProvingTime: 60000, // milliseconds
ProvingCycles: &provingCycles,
Proof: "base64_encoded_proof_data",
VerifierID: "test-verifier",
})
```

## Testing

### Prerequisites

1. Install required tools:
```bash
# Install uber-go/mock
make mockgen-install
```

### Running Tests

```bash
# Run all tests
go test ./ethproofs/...

# Run with verbose output
go test -v ./ethproofs/...

# Run with coverage
go test -cover ./ethproofs/...

# Generate coverage report
go test -coverprofile=coverage.out ./ethproofs/...
go tool cover -html=coverage.out

# Run specific domain tests
go test -v ./ethproofs/... -run TestCreateCluster
go test -v ./ethproofs/... -run TestListAWSPricing
```

### Using Mock Client in Tests

```go
import (
"testing"
"go.uber.org/mock/gomock"
"github.com/kkrt-labs/kakarot-controller/ethproofs/mock"
)

func TestYourFunction(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

mockClient := mock.NewMockClient(ctrl)

// Set up expectations
mockClient.EXPECT().
CreateCluster(gomock.Any(), gomock.Any()).
Return(&ethproofs.CreateClusterResponse{ID: 123}, nil)

// Use mockClient in your tests
}
```

## Directory Structure

```
src/ethproofs/
└── client/
├── README.md # This file
├── client.go # Main interface definition
├── http/
│ ├── client.go # HTTP implementation using autorest
│ ├── config.go # Client configuration
│ ├── clusters.go # Clusters endpoint implementation
│ ├── clusters_test.go
│ ├── proofs.go # Proofs endpoint implementation
│ ├── proofs_test.go
│ ├── machine.go # Single machine endpoint implementation
│ ├── machine_test.go
│ ├── aws.go # AWS pricing endpoint implementation
│ └── aws_test.go
└── mock/
└── client.go # Generated mock client
```

## API Documentation

For detailed API documentation, visit:
- [EthProofs API Documentation](https://staging--ethproofs.netlify.app/api.html)
- [EthProofs App Preview](https://staging--ethproofs.netlify.app/)
- [EthProofs Repository](https://github.com/ethproofs/ethproofs)

## Contributing

Interested in contributing? Check out our [Contributing Guidelines](../../CONTRIBUTING.md) to get started!
115 changes: 115 additions & 0 deletions ethproofs/client/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package ethproofs

import (
"context"
)

// Package ethproofs provides a Go client for the EthProofs API.
//
// For more information about EthProofs, visit:
// - API Documentation: https://staging--ethproofs.netlify.app/api.html
// - App Preview: https://staging--ethproofs.netlify.app/
// - Repository: https://github.com/ethproofs/ethproofs
//
//go:generate mockgen -source client.go -destination mock/client.go -package mock Client

// Client defines the interface for interacting with the EthProofs API
type Client interface {
// Clusters
CreateCluster(ctx context.Context, req *CreateClusterRequest) (*CreateClusterResponse, error)
ListClusters(ctx context.Context) ([]Cluster, error)

// Single Machine
CreateMachine(ctx context.Context, req *CreateMachineRequest) (*CreateMachineResponse, error)

// Proofs
QueueProof(ctx context.Context, req *QueueProofRequest) (*ProofResponse, error)
StartProving(ctx context.Context, req *StartProvingRequest) (*ProofResponse, error)
SubmitProof(ctx context.Context, req *SubmitProofRequest) (*ProofResponse, error)

// AWS Pricing
ListAWSPricing(ctx context.Context) ([]AWSInstance, error)
}

// Request/Response types for Clusters
type CreateClusterRequest struct {
Nickname string `json:"nickname"`
Description string `json:"description,omitempty"`
Hardware string `json:"hardware,omitempty"`
CycleType string `json:"cycle_type,omitempty"`
ProofType string `json:"proof_type,omitempty"`
Configuration []ClusterConfig `json:"configuration"`
}

type ClusterConfig struct {
InstanceType string `json:"instance_type"`
InstanceCount int64 `json:"instance_count"`
}

type CreateClusterResponse struct {
ID int64 `json:"id"`
}

type ListClustersResponse []Cluster

type Cluster struct {
ID int64 `json:"id"`
Nickname string `json:"nickname"`
Description string `json:"description"`
Hardware string `json:"hardware"`
CycleType string `json:"cycle_type"`
ProofType string `json:"proof_type"`
ClusterConfiguration []ClusterConfig `json:"cluster_configuration"`
}

// Request/Response types for Single Machine
type CreateMachineRequest struct {
Nickname string `json:"nickname"`
Description string `json:"description,omitempty"`
Hardware string `json:"hardware,omitempty"`
CycleType string `json:"cycle_type,omitempty"`
ProofType string `json:"proof_type,omitempty"`
InstanceType string `json:"instance_type"`
}

type CreateMachineResponse struct {
ID int64 `json:"id"`
}

// Request/Response types for Proofs
type QueueProofRequest struct {
BlockNumber int64 `json:"block_number"`
ClusterID int64 `json:"cluster_id"`
}

type StartProvingRequest struct {
BlockNumber int64 `json:"block_number"`
ClusterID int64 `json:"cluster_id"`
}

type SubmitProofRequest struct {
BlockNumber int64 `json:"block_number"`
ClusterID int64 `json:"cluster_id"`
ProvingTime int64 `json:"proving_time"`
ProvingCycles *int64 `json:"proving_cycles,omitempty"`
Proof string `json:"proof"`
VerifierID string `json:"verifier_id,omitempty"`
}

type ProofResponse struct {
ProofID int64 `json:"proof_id"`
}

// Request/Response types for AWS Pricing
type ListAWSPricingResponse = []AWSInstance

type AWSInstance struct {
ID int64 `json:"id"`
InstanceType string `json:"instance_type"`
Region string `json:"region"`
HourlyPrice float64 `json:"hourly_price"`
InstanceMemory float64 `json:"instance_memory"`
VCPU int64 `json:"vcpu"`
InstanceStorage string `json:"instance_storage"`
CreatedAt string `json:"created_at"`
}
17 changes: 17 additions & 0 deletions ethproofs/client/http/aws.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package http

import (
"context"
"net/http"

ethproofs "github.com/kkrt-labs/go-utils/ethproofs/client"
)

func (c *Client) ListAWSPricing(ctx context.Context) ([]ethproofs.AWSInstance, error) {
var resp []ethproofs.AWSInstance
if err := c.do(ctx, http.MethodGet, "/aws-pricing-list", nil, &resp); err != nil {
return nil, err
}

return resp, nil
}
44 changes: 44 additions & 0 deletions ethproofs/client/http/aws_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package http

import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"

ethproofs "github.com/kkrt-labs/go-utils/ethproofs/client"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestListAWSPricing(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "/aws-pricing-list", r.URL.Path)
assert.Equal(t, "Bearer test-key", r.Header.Get("Authorization"))

instances := []ethproofs.AWSInstance{
{
ID: 1,
InstanceType: "t3.small",
HourlyPrice: 0.5,
InstanceMemory: 2.0,
VCPU: 2,
},
}
err := json.NewEncoder(w).Encode(instances)
require.NoError(t, err)
}))
defer server.Close()

client, err := NewClient(&Config{
Addr: server.URL,
APIKey: "test-key",
})
require.NoError(t, err)

resp, err := client.ListAWSPricing(context.Background())
require.NoError(t, err)
require.Len(t, resp, 1)
assert.Equal(t, "t3.small", resp[0].InstanceType)
}
Loading

0 comments on commit a83245a

Please sign in to comment.