Skip to content

Commit

Permalink
fix: use bls signature in telemetry api for the operator (#1489)
Browse files Browse the repository at this point in the history
Co-authored-by: Uriel Mihura <[email protected]>
  • Loading branch information
MarcosNicolau and uri-99 authored Nov 26, 2024
1 parent 86692fc commit bb152df
Show file tree
Hide file tree
Showing 10 changed files with 214 additions and 82 deletions.
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -1064,7 +1064,7 @@ docker_logs_batcher:

__TELEMETRY__:
# Collector, Jaeger and Elixir API
telemetry_full_start: open_telemetry_start telemetry_start
telemetry_full_start: telemetry_compile_bls_verifier open_telemetry_start telemetry_start

# Collector and Jaeger
open_telemetry_start: ## Run open telemetry services using telemetry-docker-compose.yaml
Expand Down Expand Up @@ -1108,6 +1108,10 @@ telemetry_create_env:
@cd telemetry_api && \
cp .env.dev .env

telemetry_compile_bls_verifier:
@cd telemetry_api/priv && \
go build ../bls_verifier/bls_verify.go

setup_local_aligned_all:
tmux kill-session -t aligned_layer || true
tmux new-session -d -s aligned_layer
Expand Down
15 changes: 8 additions & 7 deletions operator/pkg/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -646,13 +646,12 @@ func (o *Operator) SendTelemetryData(ctx *cli.Context) error {
hash.Write([]byte(ctx.App.Version))

// get hash
version := hash.Sum(nil)
var version [32]byte // All zeroed initially
copy(version[:], hash.Sum(nil))

// sign version
signature, err := crypto.Sign(version[:], o.Config.EcdsaConfig.PrivateKey)
if err != nil {
return err
}
signature := o.Config.BlsConfig.KeyPair.SignMessage(version)
public_key_g2 := o.Config.BlsConfig.KeyPair.GetPubKeyG2()
ethRpcUrl, err := BaseUrlOnly(o.Config.BaseConfig.EthRpcUrl)
if err != nil {
return err
Expand All @@ -671,12 +670,14 @@ func (o *Operator) SendTelemetryData(ctx *cli.Context) error {
}

body := map[string]interface{}{
"version": ctx.App.Version,
"signature": signature,
"eth_rpc_url": ethRpcUrl,
"eth_rpc_url_fallback": ethRpcUrlFallback,
"eth_ws_url": ethWsUrl,
"eth_ws_url_fallback": ethWsUrlFallback,
"address": o.Address,
"version": ctx.App.Version,
"signature": signature.Bytes(),
"pub_key_g2": public_key_g2.Bytes(),
}

bodyBuffer := new(bytes.Buffer)
Expand Down
3 changes: 3 additions & 0 deletions telemetry_api/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,6 @@ telemetry_api-*.tar

# Elixir lsp server
.elixir_ls

# Binaries
priv/bls_verify
91 changes: 91 additions & 0 deletions telemetry_api/bls_verifier/bls_verify.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package main

import (
"encoding/hex"
"flag"
"log"
"os"

bls "github.com/Layr-Labs/eigensdk-go/crypto/bls"
)

func main() {
signatureArg := flag.String("signature", "", "BLS signature bytes")
publicKeyG1X := flag.String("public-key-g1-x", "", "BLS public key on g1 affine x coord")
publicKeyG1Y := flag.String("public-key-g1-y", "", "BLS public key on g1 affine y coord")
publicKeyG2Arg := flag.String("public-key-g2", "", "BLS public key on g2")
messageArg := flag.String("message", "", "Hex-encoded message")

flag.Parse()

if *signatureArg == "" || *publicKeyG1X == "" || *publicKeyG1Y == "" || *publicKeyG2Arg == "" || *messageArg == "" {
log.Fatalf("All arguments (signature, publickey g1 hash, publickey g2, and messagehash) are required")
}

signature, err := hex.DecodeString(*signatureArg)
if err != nil {
log.Fatalf("Failed to decode signature: %v", err)
}

var pubkeyG1PointsBytes [2][]byte
xBytes, err := hex.DecodeString(*publicKeyG1X)
if err != nil {
log.Fatalf("Failed to decode G1 X: %v", err)
}
yBytes, err := hex.DecodeString(*publicKeyG1Y)
if err != nil {
log.Fatalf("Failed to decode G1 Y: %v", err)
}
pubkeyG1PointsBytes[0] = xBytes
pubkeyG1PointsBytes[1] = yBytes

pubkeyG2Bytes, err := hex.DecodeString(*publicKeyG2Arg)
if err != nil {
log.Fatalf("Failed to decode pubkey: %v", err)
}

messageHash, err := hex.DecodeString(*messageArg)
if err != nil {
log.Fatalf("Failed to decode message hash: %v", err)
}

isValid, err := verifySignature(signature, pubkeyG1PointsBytes, pubkeyG2Bytes, messageHash)
if err != nil {
log.Fatalf("Error during verification: %v", err)
}

if isValid {
os.Exit(0)
} else {
os.Exit(1)
}
}

func verifySignature(signature []byte, pubkeyG1PointsBytes [2][]byte, pubkeyG2Bytes []byte, message []byte) (bool, error) {
pubkeyG1 := bls.NewZeroG1Point()
pubkeyG1.X.SetBytes(pubkeyG1PointsBytes[0])
pubkeyG1.Y.SetBytes(pubkeyG1PointsBytes[1])

pubkeyG2 := bls.NewZeroG2Point()
_, err := pubkeyG2.SetBytes(pubkeyG2Bytes)
if err != nil {
return false, err
}

var messageBytes [32]byte
copy(messageBytes[:], message[:])

sign := bls.NewZeroSignature()
_, err = sign.SetBytes(signature)
if err != nil {
return false, err
}

// verify the equivalence between the points in the generators
valid, err := pubkeyG1.VerifyEquivalence(pubkeyG2)
if err != nil || !valid {
return false, err
}

return sign.Verify(pubkeyG2, messageBytes)
}
28 changes: 28 additions & 0 deletions telemetry_api/lib/telemetry_api/bls_signature_verifier.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
defmodule BLSSignatureVerifier do
def verify(signature, {pubkey_g1_x, pubkey_g1_y}, bls_pubkey_g2, message) do
pubkey_g1_x = <<pubkey_g1_x::unsigned-big-integer-size(256)>>
pubkey_g1_y = <<pubkey_g1_y::unsigned-big-integer-size(256)>>

args = [
"--signature",
Base.encode16(:binary.list_to_bin(signature)),
"--public-key-g1-x",
Base.encode16(pubkey_g1_x),
"--public-key-g1-y",
Base.encode16(pubkey_g1_y),
"--public-key-g2",
Base.encode16(:binary.list_to_bin(bls_pubkey_g2)),
"--message",
Base.encode16(message)
]

binary_path = Path.join(:code.priv_dir(:telemetry_api), "bls_verify")
{output, exit_code} = System.cmd(binary_path, args)

case exit_code do
0 -> {:ok, "Valid"}
1 -> {:error, "Invalid signature"}
_ -> {:error, "Verification failed: #{output}"}
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
defmodule BLSApkRegistry do
require Logger

@aligned_config_file System.get_env("ALIGNED_CONFIG_FILE")

config_file_path =
case @aligned_config_file do
nil -> raise("ALIGNED_CONFIG_FILE not set in .env")
file -> file
end

{status, config_json_string} = File.read(config_file_path)

case status do
:ok ->
Logger.debug("Aligned deployment file read successfully")

:error ->
raise(
"Config file not read successfully, make sure your .env is correctly created, and make sure Eigenlayer config file is correctly stored"
)
end

@contract_address Jason.decode!(config_json_string)
|> Map.get("addresses")
|> Map.get("blsApkRegistry")

use Ethers.Contract,
abi_file: "priv/abi/IBLSApkRegistry.json",
default_address: @contract_address

def get_bls_apk_registry_address() do
@contract_address
end

def get_operator_bls_pubkey(operator_address) do
case BLSApkRegistry.get_registered_pubkey(operator_address)
|> Ethers.call() do
{:ok, data} ->
{:ok, data}

error ->
{:error, error}
end
end
end
34 changes: 21 additions & 13 deletions telemetry_api/lib/telemetry_api/operators.ex
Original file line number Diff line number Diff line change
Expand Up @@ -135,24 +135,32 @@ defmodule TelemetryApi.Operators do
## Examples
iex> update_operator(some_version, some_signature, %{field: value})
iex> update_operator(address, some_version, some_signature, pubkey_g2, %{field: value})
{:ok, %Ecto.Changeset{}}
iex> update_operator(some_version, invalid_signature, %{field: value})
iex> update_operator(address, some_version, invalid_signature, pubkey_g2, %{field: value})
{:error, "Some status", "Some message"}
"""
def update_operator(version, signature, changes) do
with {:ok, address} <- SignatureVerifier.recover_address(version, signature) do
address = "0x" <> address
case Repo.get(Operator, address) do
nil ->
{:error, :bad_request,
"Provided address does not correspond to any registered operator"}

operator ->
update_operator(operator, changes)
end
def update_operator(address, version, signature, pubkey_g2, changes) do
message_hash = ExKeccak.hash_256(version)

case Repo.get(Operator, address) do
nil ->
{:error, :bad_request, "Provided address does not correspond to any registered operator"}

operator ->
case BLSApkRegistry.get_operator_bls_pubkey(address) do
{:ok, [pubkey_g1_points, _]} ->
case BLSSignatureVerifier.verify(signature, pubkey_g1_points, pubkey_g2, message_hash) do
{:ok, _} ->
update_operator(operator, changes)
{:error, _} ->
{:error, :unauthorized, "Signature verification failed"}
end
{:error, _} ->
{:error, :not_found, "Failed to retrieve public key for the operator"}
end
end
end

Expand Down
59 changes: 0 additions & 59 deletions telemetry_api/lib/telemetry_api/signature_verifier.ex

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,17 @@ defmodule TelemetryApiWeb.OperatorController do
render(conn, :index, operators: operators)
end

def create_or_update(conn, %{"version" => version, "signature" => signature} = attrs) do
with {:ok, %Operator{} = operator} <- Operators.update_operator(version, signature, attrs) do
def create_or_update(
conn,
%{
"address" => address,
"version" => version,
"signature" => signature,
"pub_key_g2" => pub_key_g2
} = attrs
) do
with {:ok, %Operator{} = operator} <-
Operators.update_operator(address, version, signature, pub_key_g2, attrs) do
conn
|> put_status(:created)
|> put_resp_header("location", ~p"/api/operators/#{operator}")
Expand Down
1 change: 1 addition & 0 deletions telemetry_api/priv/abi/IBLSApkRegistry.json

Large diffs are not rendered by default.

0 comments on commit bb152df

Please sign in to comment.