Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions sdk/sdk-go/InMemoryConfigStore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package self

import (
"context"

)

// GetActionIdFunc is a function type for custom action ID generation
type GetActionIdFunc func(ctx context.Context, userIdentifier string, userDefinedData string) (string, error)

// InMemoryConfigStore provides an in-memory implementation of ConfigStore with custom action ID logic
type InMemoryConfigStore struct {
configs map[string]VerificationConfig
getActionIdFunc GetActionIdFunc
}

// Compile-time check to ensure InMemoryConfigStore implements ConfigStore interface
var _ ConfigStore = (*InMemoryConfigStore)(nil)

// NewInMemoryConfigStore creates a new instance of InMemoryConfigStore
func NewInMemoryConfigStore(getActionIdFunc GetActionIdFunc) *InMemoryConfigStore {
return &InMemoryConfigStore{
configs: make(map[string]VerificationConfig),
getActionIdFunc: getActionIdFunc,
}
}

// GetActionId uses the custom function to generate action IDs
func (store *InMemoryConfigStore) GetActionId(ctx context.Context, userIdentifier string, userDefinedData string) (string, error) {
return store.getActionIdFunc(ctx, userIdentifier, userDefinedData)
}
Comment on lines +29 to +31
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add nil check for getActionIdFunc

The GetActionId method could panic if getActionIdFunc is nil. Consider adding a defensive check.

 // GetActionId uses the custom function to generate action IDs
 func (store *InMemoryConfigStore) GetActionId(ctx context.Context, userIdentifier string, userDefinedData string) (string, error) {
+    if store.getActionIdFunc == nil {
+        return "", fmt.Errorf("getActionIdFunc is not configured")
+    }
     return store.getActionIdFunc(ctx, userIdentifier, userDefinedData)
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (store *InMemoryConfigStore) GetActionId(ctx context.Context, userIdentifier string, userDefinedData string) (string, error) {
return store.getActionIdFunc(ctx, userIdentifier, userDefinedData)
}
// GetActionId uses the custom function to generate action IDs
func (store *InMemoryConfigStore) GetActionId(ctx context.Context, userIdentifier string, userDefinedData string) (string, error) {
if store.getActionIdFunc == nil {
return "", fmt.Errorf("getActionIdFunc is not configured")
}
return store.getActionIdFunc(ctx, userIdentifier, userDefinedData)
}
🤖 Prompt for AI Agents
In sdk/sdk-go/InMemoryConfigStore.go around lines 29 to 31, the GetActionId
method calls store.getActionIdFunc without checking for nil which can cause a
panic; add a defensive nil check that returns a clear error when getActionIdFunc
is nil (e.g., fmt.Errorf or a package-specific error) and only call
store.getActionIdFunc when it is non-nil, preserving the original return
signature of (string, error).


// SetConfig stores a configuration with the given ID
// Returns true if the configuration was newly created, false if it was updated
func (store *InMemoryConfigStore) SetConfig(ctx context.Context, id string, config VerificationConfig) (bool, error) {
_, existed := store.configs[id]
store.configs[id] = config
return !existed, nil
}
Comment on lines +12 to +39
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

InMemoryConfigStore lacks thread safety

The InMemoryConfigStore accesses and modifies the configs map without synchronization, which will cause race conditions in concurrent environments. This is particularly important for a reusable SDK component.

Add a mutex for thread-safe operations:

+import (
+    "context"
+    "sync"
+)

 // InMemoryConfigStore provides an in-memory implementation of ConfigStore with custom action ID logic
 type InMemoryConfigStore struct {
     configs         map[string]VerificationConfig
     getActionIdFunc GetActionIdFunc
+    mu              sync.RWMutex
 }

 // SetConfig stores a configuration with the given ID
 func (store *InMemoryConfigStore) SetConfig(ctx context.Context, id string, config VerificationConfig) (bool, error) {
+    store.mu.Lock()
+    defer store.mu.Unlock()
     _, existed := store.configs[id]
     store.configs[id] = config
     return !existed, nil
 }

 // GetConfig retrieves a configuration by ID
 func (store *InMemoryConfigStore) GetConfig(ctx context.Context, id string) (VerificationConfig, error) {
+    store.mu.RLock()
+    defer store.mu.RUnlock()
     config, exists := store.configs[id]
     if !exists {
         return VerificationConfig{}, nil
     }
     return config, nil
 }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In sdk/sdk-go/InMemoryConfigStore.go around lines 12 to 39, the configs map is
accessed without synchronization causing race conditions; add a sync.RWMutex
field to InMemoryConfigStore, use store.mu.Lock()/defer store.mu.Unlock() around
writes in SetConfig (or store.mu.RLock()/defer store.mu.RUnlock() for future
reads), keep GetActionId untouched if the provided function is safe, and ensure
NewInMemoryConfigStore returns the struct with the mutex (zero value is fine) so
all map accesses are protected.


// GetConfig retrieves a configuration by ID
func (store *InMemoryConfigStore) GetConfig(ctx context.Context, id string) (VerificationConfig, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix indentation issue

There's an incorrect indentation at the beginning of this line.

-	func (store *InMemoryConfigStore) GetConfig(ctx context.Context, id string) (VerificationConfig, error) {
+func (store *InMemoryConfigStore) GetConfig(ctx context.Context, id string) (VerificationConfig, error) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (store *InMemoryConfigStore) GetConfig(ctx context.Context, id string) (VerificationConfig, error) {
func (store *InMemoryConfigStore) GetConfig(ctx context.Context, id string) (VerificationConfig, error) {
🤖 Prompt for AI Agents
In sdk/sdk-go/InMemoryConfigStore.go around line 42, the function signature line
for GetConfig has incorrect leading indentation; remove the extra leading
spaces/tabs so the line starts at the file's expected indentation level (align
with surrounding methods), ensuring consistent formatting across the file.

config, exists := store.configs[id]
if !exists {
return VerificationConfig{}, nil
}
return config, nil
}
294 changes: 294 additions & 0 deletions sdk/sdk-go/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,294 @@
# Self Protocol Go SDK

A Go SDK for integrating with the Self protocol for privacy-preserving identity verification using zero-knowledge proofs and passport/ID card attestations.

## Installation

```bash
go get github.com/selfxyz/self/sdk/sdk-go@main
```

## Quick Start

### Basic Usage

```go
package main

import (
"context"
"fmt"
"log"

"github.com/selfxyz/self/sdk/sdk-go"
"github.com/selfxyz/self/sdk/sdk-go/common"
)

func main() {
// Create a verification configuration
config := self.VerificationConfig{
MinimumAge: &[]int{18}[0], // Require 18+ years
ExcludedCountries: []common.Country3LetterCode{common.USA}, // Exclude USA
Ofac: &[]bool{false}[0], // Allow OFAC flagged individuals
}

// Create a config store
configStore := self.NewDefaultConfigStore(config)

// Define allowed attestation types
allowedIds := map[self.AttestationId]bool{
self.Passport: true,
self.EUCard: true,
}

// Initialize the verifier
verifier, err := self.NewBackendVerifier(
"my-app-scope", // Your application scope
"https://my-app.com", // Your application endpoint
false, // Use mainnet (true for testnet)
allowedIds, // Allowed attestation types
configStore, // Configuration storage
self.UserIDTypeHex, // User identifier type
)
if err != nil {
log.Fatal(err)
}

// Verify a proof (these would come from your frontend)
ctx := context.Background()
result, err := verifier.Verify(
ctx,
"1", // Attestation ID (passport)
proof, // Zero-knowledge proof from frontend
publicSignals, // Public signals from frontend
userContextData, // User context data from frontend
)
if err != nil {
log.Printf("Verification failed: %v", err)
return
}

// Check verification results
if result.IsValidDetails.IsValid {
fmt.Printf("✅ Verification successful!\n")
fmt.Printf("User ID: %s\n", result.UserData.UserIdentifier)
fmt.Printf("Age verification: %v\n", result.IsValidDetails.IsMinimumAgeValid)
fmt.Printf("OFAC verification: %v\n", result.IsValidDetails.IsOfacValid)
fmt.Printf("Nationality: %s\n", result.DiscloseOutput.Nationality)
} else {
fmt.Printf("❌ Verification failed\n")
}
}
```

## Configuration

### Verification Config

The `VerificationConfig` struct allows you to specify verification requirements:

```go
type VerificationConfig struct {
MinimumAge *int // Minimum age requirement (nil to disable)
ExcludedCountries []common.Country3LetterCode // Countries to exclude
Ofac *bool // OFAC compliance (nil to ignore)
}
```

### Config Storage

Implement the `ConfigStore` interface for custom configuration management:

```go
type ConfigStore interface {
GetConfig(ctx context.Context, id string) (VerificationConfig, error)
SetConfig(ctx context.Context, id string, config VerificationConfig) (bool, error)
GetActionId(ctx context.Context, userIdentifier string, actionId string) (string, error)
}
```

For simple use cases, use `DefaultConfigStore`:

```go
configStore := self.NewDefaultConfigStore(config)
```

For more complex scenarios, implement your own:

```go
type DatabaseConfigStore struct {
db *sql.DB
}

func (d *DatabaseConfigStore) GetConfig(ctx context.Context, id string) (self.VerificationConfig, error) {
// Your database logic here
}

func (d *DatabaseConfigStore) SetConfig(ctx context.Context, id string, config self.VerificationConfig) (bool, error) {
// Your database logic here
}

func (d *DatabaseConfigStore) GetActionId(ctx context.Context, userIdentifier string, actionId string) (string, error) {
// Your database logic here
}
```

## Attestation Types

The SDK supports two attestation types:

- `self.Passport`: Traditional passport verification
- `self.EUCard`: European ID card verification

## Network Configuration

### Mainnet (Production)
```go
verifier, err := self.NewBackendVerifier(
scope, endpoint, false, // mockPassport = false for mainnet
allowedIds, configStore, userIdType,
)
```

### Testnet (Development)
```go
verifier, err := self.NewBackendVerifier(
scope, endpoint, true, // mockPassport = true for testnet
allowedIds, configStore, userIdType,
)
```

## User Identifier Types

Choose how user identifiers are formatted:

```go
self.UserIDTypeHex // Hex format: 0x1234...
self.UserIDTypeUUID // UUID format: 12345678-1234-1234-1234-123456789abc
```

## Country Codes

Use 3-letter ISO country codes for exclusions:

```go
import "github.com/self/sdk/common"

excludedCountries := []common.Country3LetterCode{
common.USA, // United States
common.RUS, // Russia
common.CHN, // China
}
```

## Error Handling

The SDK provides detailed error information through `ConfigMismatchError`:

```go
result, err := verifier.Verify(ctx, attestationId, proof, signals, contextData)
if err != nil {
if configErr, ok := err.(*self.ConfigMismatchError); ok {
fmt.Println("Configuration issues:")
for _, issue := range configErr.Issues {
fmt.Printf("- %s: %s\n", issue.Type, issue.Message)
}
} else {
fmt.Printf("Other error: %v\n", err)
}
return
}
```

## Verification Result

The `VerificationResult` contains comprehensive verification information:

```go
type VerificationResult struct {
AttestationId AttestationId // Type of attestation verified
IsValidDetails IsValidDetails // Validation status details
ForbiddenCountriesList []string // List of forbidden countries
DiscloseOutput GenericDiscloseOutput // Disclosed identity information
UserData UserData // User-specific data
}

type IsValidDetails struct {
IsValid bool // Overall proof validity
IsMinimumAgeValid bool // Age requirement met
IsOfacValid bool // OFAC compliance
}

type GenericDiscloseOutput struct {
Nullifier string // Unique nullifier for this proof
IssuingState string // Country that issued the document
Name string // Full name
IdNumber string // Document ID number
Nationality string // Nationality
DateOfBirth string // Date of birth
Gender string // Gender
ExpiryDate string // Document expiry date
MinimumAge string // Minimum age disclosed
Ofac []bool // OFAC check results
}
```

## Examples

### Age Verification (18+)

```go
config := self.VerificationConfig{
MinimumAge: &[]int{18}[0],
}
```

### Country Restrictions

```go
config := self.VerificationConfig{
ExcludedCountries: []common.Country3LetterCode{
common.USA,
common.RUS,
common.IRN,
},
}
```

### OFAC Compliance

```go
config := self.VerificationConfig{
Ofac: &[]bool{true}[0], // Require OFAC compliance
}
```

### Combined Requirements

```go
config := self.VerificationConfig{
MinimumAge: &[]int{21}[0],
ExcludedCountries: []common.Country3LetterCode{common.USA},
Ofac: &[]bool{true}[0],
}
```

## Contributing

1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## Support

For support and questions:

- Create an issue in this repository
- Check the [Self Protocol documentation](https://docs.self.id)
- Join our [Discord community](https://discord.gg/worldcoin)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Update Discord link to point to Self Protocol's community

The Discord link currently points to Worldcoin's community. This should be updated to point to the Self Protocol's official Discord server.

-- Join our [Discord community](https://discord.gg/worldcoin)
+- Join our [Discord community](https://discord.gg/self-protocol)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- Join our [Discord community](https://discord.gg/worldcoin)
- Join our [Discord community](https://discord.gg/self-protocol)
🤖 Prompt for AI Agents
In sdk/sdk-go/README.md around line 293, the Discord link currently points to
Worldcoin's community; update the markdown so the link target and link text (if
needed) reference Self Protocol's official Discord server by replacing the
existing URL with the Self Protocol Discord invite URL (and ensure the link text
still reads appropriately, e.g., "Join our Discord community"). Verify the new
invite URL is correct and that the markdown renders properly.

Loading
Loading