Skip to content
Open
Show file tree
Hide file tree
Changes from 6 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
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ require (
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/yusufpapurcu/wmi v1.2.3 // indirect
golang.org/x/crypto v0.23.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sync v0.7.0
golang.org/x/sys v0.20.0 // indirect
golang.org/x/text v0.15.0 // indirect
rsc.io/tmplfunc v0.0.3 // indirect
Expand Down
8 changes: 7 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func main() {
var modes []string
var cfg config.Config
if len(os.Args[1:]) == 0 {
cfg := config.LoadConfig()
cfg = config.LoadConfig()
log.Println(cfg.Mode)
mode := cfg.Mode

Expand Down Expand Up @@ -65,6 +65,12 @@ func main() {
runCollector()
wg.Done()
}()
case "decryption-monitor":
wg.Add(1)
go func() {
tests.RunDecryptionMonitor(cfg)
wg.Done()
}()
default:
log.Printf("Unknown mode: %s", m)
}
Expand Down
5 changes: 5 additions & 0 deletions template.env
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,8 @@ NODE_URL="https://erpc.chiado.staging.shutter.network"
WAIT_TX_TIMEOUT=10
TEST_DURATION=1

#DECRYPTION MONITOR
SHUTTER_API="http://shutter-api.shutter.network/api"
API_REQUEST_INTERVAL="60"
SHUTTER_REGISTRY_CALLER_ADDRESS="0x228DefCF37Da29475F0EE2B9E4dfAeDc3b0746bc"
DEC_KEY_WAIT_INTERVAL="20"
258 changes: 258 additions & 0 deletions tests/decryptionmonitor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
package tests

import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"net/url"
"os"
"os/signal"
"strconv"
"sync"
"sync/atomic"
"syscall"
"time"

"github.com/ethereum/go-ethereum/ethclient"
"github.com/shutter-network/nethermind-tests/config"
)

type RegisterIdentityRequest struct {
DecryptionTimestamp int64 `json:"decryptionTimestamp"`
IdentityPrefix string `json:"identityPrefix"`
}

type DecryptionStats struct {
totalDecryptions atomic.Int64
validDecryptions atomic.Int64
invalidDecryptions atomic.Int64
}

var (
stats DecryptionStats
stopChan = make(chan os.Signal, 1)
)

type GetDataForEncryptionResponse struct {
Eon uint64 `json:"eon"`
Identity string `json:"identity"`
IdentityPrefix string `json:"identity_prefix"`
EonKey string `json:"eon_key"`
EpochID string `json:"epoch_id"`
}

type ErrorResponse struct {
Description string `json:"description,omitempty"`
Metadata string `json:"metadata,omitempty"`
StatusCode int `json:"statusCode"`
}

func RunDecryptionMonitor(cfg config.Config) {
baseURL := os.Getenv("SHUTTER_API")
seconds, err := strconv.Atoi(os.Getenv("API_REQUEST_INTERVAL"))
if err != nil {
log.Fatalf("incorrect api request interval %s", err)
return
}
interval := time.Duration(seconds) * time.Second
address := os.Getenv("SHUTTER_REGISTRY_CALLER_ADDRESS")

log.Printf("Starting performance monitoring\n")
log.Printf("Base URL: %s\n", baseURL)
log.Printf("Interval: %v\n\n", interval)

// Setup graceful shutdown
signal.Notify(stopChan, syscall.SIGINT, syscall.SIGTERM)

var wg sync.WaitGroup

client, err := ethclient.Dial(cfg.NodeURL)
if err != nil {
log.Fatalf("Failed to connect to the Ethereum client: %v", err)
return
}
// Start monitoring in separate goroutine
go func() {
runFlow(client, baseURL, address, &wg)

ticker := time.NewTicker(interval)
defer ticker.Stop()

for {
select {
case <-ticker.C:
runFlow(client, baseURL, address, &wg)
case <-stopChan:
return
}
}
}()

// Wait for interrupt signal
<-stopChan
log.Printf("Shutting down...\n")

// Wait for all decryption requests to complete
wg.Wait()

// Print final statistics
printStatistics()
}

func printStatistics() {
log.Printf("\n=== Final Decryption Statistics ===\n")
log.Printf("Total decryption attempts: %d\n", stats.totalDecryptions.Load())
log.Printf("Successful decryptions: %d\n", stats.validDecryptions.Load())
log.Printf("Failed decryptions: %d\n", stats.invalidDecryptions.Load())

total := stats.totalDecryptions.Load()
if total > 0 {
successRate := float64(stats.validDecryptions.Load()) / float64(total) * 100
log.Printf("Success rate: %.2f%%\n", successRate)
}
}

func runFlow(client *ethclient.Client, baseURL, address string, wg *sync.WaitGroup) {
timestamp := time.Now().Format("2006-01-02 15:04:05")
log.Printf("\n=== Performance Check at %s ===\n", timestamp)

encryptionData, err := getDataForEncryption(baseURL, address, "")
if err != nil {
log.Fatalf("error in get data for encryption endpoint %s", err)
return
}
block, err := client.BlockByNumber(context.Background(), nil)
if err != nil {
log.Fatalf("error from rpc while requesting for block %s", err)
return
}
decryptionTimestamp := block.Header().Time + 10
registerReq := RegisterIdentityRequest{
DecryptionTimestamp: int64(decryptionTimestamp),
IdentityPrefix: encryptionData["message"].IdentityPrefix,
}

err = registerIdentity(baseURL, registerReq)
if err != nil {
log.Fatalf("error encountered while registering identity %s", err)
return
}

// Launch decryption key request in separate goroutine
wg.Add(1)
go func(identity string) {
defer wg.Done()

seconds, err := strconv.Atoi(os.Getenv("DEC_KEY_WAIT_INTERVAL"))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Could you please update this to be in ms? Thank you!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is in accordance with gnosis chain timestamps, so not sure what is the reason for us to change it to ms.

if err != nil {
log.Fatalf("incorrect decryption key wait interval %s", err)
return
}

time.Sleep(time.Duration(seconds) * time.Second)
stats.totalDecryptions.Add(1)
err = getDecryptionKey(baseURL, identity)
if err != nil {
log.Fatalf("error encountered while getting decryption key%s", err)
stats.invalidDecryptions.Add(1)
} else {
stats.validDecryptions.Add(1)
}
}(encryptionData["message"].Identity)
}

func getDataForEncryption(baseURL, address, identityPrefix string) (map[string]GetDataForEncryptionResponse, error) {
params := url.Values{}
params.Add("address", address)
if identityPrefix != "" {
params.Add("identityPrefix", identityPrefix)
}

url := fmt.Sprintf("%s/get_data_for_encryption?%s", baseURL, params.Encode())
resp, err := http.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}

if resp.StatusCode != http.StatusOK {
var errorResp ErrorResponse
if err := json.Unmarshal(body, &errorResp); err != nil {
return nil, fmt.Errorf("failed to parse error response: %v", err)
}
return nil, err

}

var response map[string]GetDataForEncryptionResponse

if err := json.Unmarshal(body, &response); err != nil {
return nil, fmt.Errorf("failed to parse response: %v", err)
}

return response, nil
}

func registerIdentity(baseURL string, req RegisterIdentityRequest) error {
jsonData, err := json.Marshal(req)
if err != nil {
return fmt.Errorf("failed to marshal request: %v", err)
}

resp, err := http.Post(
baseURL+"/register_identity",
"application/json",
bytes.NewBuffer(jsonData),
)
if err != nil {
return fmt.Errorf("request failed: %v", err)
}
defer resp.Body.Close()

body, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("failed to read response: %v", err)
}

if resp.StatusCode != http.StatusOK {
var errorResp ErrorResponse
if err := json.Unmarshal(body, &errorResp); err != nil {
return fmt.Errorf("failed to parse error response: %v", err)
}
return fmt.Errorf("response error %v", errorResp.Description)
}

return nil
}

func getDecryptionKey(baseURL, identity string) error {
params := url.Values{}
params.Add("identity", identity)

url := fmt.Sprintf("%s/get_decryption_key?%s", baseURL, params.Encode())
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("failed to read response: %v", err)
}
if resp.StatusCode != http.StatusOK {
var errorResp ErrorResponse
if err := json.Unmarshal(body, &errorResp); err != nil {
return fmt.Errorf("failed to parse error response, %v", err)
}
return fmt.Errorf("failed to parse error response, %s", errorResp.Description)
}
return nil
}