Skip to content

Commit bb152df

Browse files
fix: use bls signature in telemetry api for the operator (#1489)
Co-authored-by: Uriel Mihura <[email protected]>
1 parent 86692fc commit bb152df

File tree

10 files changed

+214
-82
lines changed

10 files changed

+214
-82
lines changed

Makefile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1064,7 +1064,7 @@ docker_logs_batcher:
10641064

10651065
__TELEMETRY__:
10661066
# Collector, Jaeger and Elixir API
1067-
telemetry_full_start: open_telemetry_start telemetry_start
1067+
telemetry_full_start: telemetry_compile_bls_verifier open_telemetry_start telemetry_start
10681068

10691069
# Collector and Jaeger
10701070
open_telemetry_start: ## Run open telemetry services using telemetry-docker-compose.yaml
@@ -1108,6 +1108,10 @@ telemetry_create_env:
11081108
@cd telemetry_api && \
11091109
cp .env.dev .env
11101110

1111+
telemetry_compile_bls_verifier:
1112+
@cd telemetry_api/priv && \
1113+
go build ../bls_verifier/bls_verify.go
1114+
11111115
setup_local_aligned_all:
11121116
tmux kill-session -t aligned_layer || true
11131117
tmux new-session -d -s aligned_layer

operator/pkg/operator.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -646,13 +646,12 @@ func (o *Operator) SendTelemetryData(ctx *cli.Context) error {
646646
hash.Write([]byte(ctx.App.Version))
647647

648648
// get hash
649-
version := hash.Sum(nil)
649+
var version [32]byte // All zeroed initially
650+
copy(version[:], hash.Sum(nil))
650651

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

673672
body := map[string]interface{}{
674-
"version": ctx.App.Version,
675-
"signature": signature,
676673
"eth_rpc_url": ethRpcUrl,
677674
"eth_rpc_url_fallback": ethRpcUrlFallback,
678675
"eth_ws_url": ethWsUrl,
679676
"eth_ws_url_fallback": ethWsUrlFallback,
677+
"address": o.Address,
678+
"version": ctx.App.Version,
679+
"signature": signature.Bytes(),
680+
"pub_key_g2": public_key_g2.Bytes(),
680681
}
681682

682683
bodyBuffer := new(bytes.Buffer)

telemetry_api/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,6 @@ telemetry_api-*.tar
2727

2828
# Elixir lsp server
2929
.elixir_ls
30+
31+
# Binaries
32+
priv/bls_verify
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package main
2+
3+
import (
4+
"encoding/hex"
5+
"flag"
6+
"log"
7+
"os"
8+
9+
bls "github.com/Layr-Labs/eigensdk-go/crypto/bls"
10+
)
11+
12+
func main() {
13+
signatureArg := flag.String("signature", "", "BLS signature bytes")
14+
publicKeyG1X := flag.String("public-key-g1-x", "", "BLS public key on g1 affine x coord")
15+
publicKeyG1Y := flag.String("public-key-g1-y", "", "BLS public key on g1 affine y coord")
16+
publicKeyG2Arg := flag.String("public-key-g2", "", "BLS public key on g2")
17+
messageArg := flag.String("message", "", "Hex-encoded message")
18+
19+
flag.Parse()
20+
21+
if *signatureArg == "" || *publicKeyG1X == "" || *publicKeyG1Y == "" || *publicKeyG2Arg == "" || *messageArg == "" {
22+
log.Fatalf("All arguments (signature, publickey g1 hash, publickey g2, and messagehash) are required")
23+
}
24+
25+
signature, err := hex.DecodeString(*signatureArg)
26+
if err != nil {
27+
log.Fatalf("Failed to decode signature: %v", err)
28+
}
29+
30+
var pubkeyG1PointsBytes [2][]byte
31+
xBytes, err := hex.DecodeString(*publicKeyG1X)
32+
if err != nil {
33+
log.Fatalf("Failed to decode G1 X: %v", err)
34+
}
35+
yBytes, err := hex.DecodeString(*publicKeyG1Y)
36+
if err != nil {
37+
log.Fatalf("Failed to decode G1 Y: %v", err)
38+
}
39+
pubkeyG1PointsBytes[0] = xBytes
40+
pubkeyG1PointsBytes[1] = yBytes
41+
42+
pubkeyG2Bytes, err := hex.DecodeString(*publicKeyG2Arg)
43+
if err != nil {
44+
log.Fatalf("Failed to decode pubkey: %v", err)
45+
}
46+
47+
messageHash, err := hex.DecodeString(*messageArg)
48+
if err != nil {
49+
log.Fatalf("Failed to decode message hash: %v", err)
50+
}
51+
52+
isValid, err := verifySignature(signature, pubkeyG1PointsBytes, pubkeyG2Bytes, messageHash)
53+
if err != nil {
54+
log.Fatalf("Error during verification: %v", err)
55+
}
56+
57+
if isValid {
58+
os.Exit(0)
59+
} else {
60+
os.Exit(1)
61+
}
62+
}
63+
64+
func verifySignature(signature []byte, pubkeyG1PointsBytes [2][]byte, pubkeyG2Bytes []byte, message []byte) (bool, error) {
65+
pubkeyG1 := bls.NewZeroG1Point()
66+
pubkeyG1.X.SetBytes(pubkeyG1PointsBytes[0])
67+
pubkeyG1.Y.SetBytes(pubkeyG1PointsBytes[1])
68+
69+
pubkeyG2 := bls.NewZeroG2Point()
70+
_, err := pubkeyG2.SetBytes(pubkeyG2Bytes)
71+
if err != nil {
72+
return false, err
73+
}
74+
75+
var messageBytes [32]byte
76+
copy(messageBytes[:], message[:])
77+
78+
sign := bls.NewZeroSignature()
79+
_, err = sign.SetBytes(signature)
80+
if err != nil {
81+
return false, err
82+
}
83+
84+
// verify the equivalence between the points in the generators
85+
valid, err := pubkeyG1.VerifyEquivalence(pubkeyG2)
86+
if err != nil || !valid {
87+
return false, err
88+
}
89+
90+
return sign.Verify(pubkeyG2, messageBytes)
91+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
defmodule BLSSignatureVerifier do
2+
def verify(signature, {pubkey_g1_x, pubkey_g1_y}, bls_pubkey_g2, message) do
3+
pubkey_g1_x = <<pubkey_g1_x::unsigned-big-integer-size(256)>>
4+
pubkey_g1_y = <<pubkey_g1_y::unsigned-big-integer-size(256)>>
5+
6+
args = [
7+
"--signature",
8+
Base.encode16(:binary.list_to_bin(signature)),
9+
"--public-key-g1-x",
10+
Base.encode16(pubkey_g1_x),
11+
"--public-key-g1-y",
12+
Base.encode16(pubkey_g1_y),
13+
"--public-key-g2",
14+
Base.encode16(:binary.list_to_bin(bls_pubkey_g2)),
15+
"--message",
16+
Base.encode16(message)
17+
]
18+
19+
binary_path = Path.join(:code.priv_dir(:telemetry_api), "bls_verify")
20+
{output, exit_code} = System.cmd(binary_path, args)
21+
22+
case exit_code do
23+
0 -> {:ok, "Valid"}
24+
1 -> {:error, "Invalid signature"}
25+
_ -> {:error, "Verification failed: #{output}"}
26+
end
27+
end
28+
end
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
defmodule BLSApkRegistry do
2+
require Logger
3+
4+
@aligned_config_file System.get_env("ALIGNED_CONFIG_FILE")
5+
6+
config_file_path =
7+
case @aligned_config_file do
8+
nil -> raise("ALIGNED_CONFIG_FILE not set in .env")
9+
file -> file
10+
end
11+
12+
{status, config_json_string} = File.read(config_file_path)
13+
14+
case status do
15+
:ok ->
16+
Logger.debug("Aligned deployment file read successfully")
17+
18+
:error ->
19+
raise(
20+
"Config file not read successfully, make sure your .env is correctly created, and make sure Eigenlayer config file is correctly stored"
21+
)
22+
end
23+
24+
@contract_address Jason.decode!(config_json_string)
25+
|> Map.get("addresses")
26+
|> Map.get("blsApkRegistry")
27+
28+
use Ethers.Contract,
29+
abi_file: "priv/abi/IBLSApkRegistry.json",
30+
default_address: @contract_address
31+
32+
def get_bls_apk_registry_address() do
33+
@contract_address
34+
end
35+
36+
def get_operator_bls_pubkey(operator_address) do
37+
case BLSApkRegistry.get_registered_pubkey(operator_address)
38+
|> Ethers.call() do
39+
{:ok, data} ->
40+
{:ok, data}
41+
42+
error ->
43+
{:error, error}
44+
end
45+
end
46+
end

telemetry_api/lib/telemetry_api/operators.ex

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -135,24 +135,32 @@ defmodule TelemetryApi.Operators do
135135
136136
## Examples
137137
138-
iex> update_operator(some_version, some_signature, %{field: value})
138+
iex> update_operator(address, some_version, some_signature, pubkey_g2, %{field: value})
139139
{:ok, %Ecto.Changeset{}}
140140
141-
iex> update_operator(some_version, invalid_signature, %{field: value})
141+
iex> update_operator(address, some_version, invalid_signature, pubkey_g2, %{field: value})
142142
{:error, "Some status", "Some message"}
143143
144144
"""
145-
def update_operator(version, signature, changes) do
146-
with {:ok, address} <- SignatureVerifier.recover_address(version, signature) do
147-
address = "0x" <> address
148-
case Repo.get(Operator, address) do
149-
nil ->
150-
{:error, :bad_request,
151-
"Provided address does not correspond to any registered operator"}
152-
153-
operator ->
154-
update_operator(operator, changes)
155-
end
145+
def update_operator(address, version, signature, pubkey_g2, changes) do
146+
message_hash = ExKeccak.hash_256(version)
147+
148+
case Repo.get(Operator, address) do
149+
nil ->
150+
{:error, :bad_request, "Provided address does not correspond to any registered operator"}
151+
152+
operator ->
153+
case BLSApkRegistry.get_operator_bls_pubkey(address) do
154+
{:ok, [pubkey_g1_points, _]} ->
155+
case BLSSignatureVerifier.verify(signature, pubkey_g1_points, pubkey_g2, message_hash) do
156+
{:ok, _} ->
157+
update_operator(operator, changes)
158+
{:error, _} ->
159+
{:error, :unauthorized, "Signature verification failed"}
160+
end
161+
{:error, _} ->
162+
{:error, :not_found, "Failed to retrieve public key for the operator"}
163+
end
156164
end
157165
end
158166

telemetry_api/lib/telemetry_api/signature_verifier.ex

Lines changed: 0 additions & 59 deletions
This file was deleted.

telemetry_api/lib/telemetry_api_web/controllers/operator_controller.ex

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,17 @@ defmodule TelemetryApiWeb.OperatorController do
1111
render(conn, :index, operators: operators)
1212
end
1313

14-
def create_or_update(conn, %{"version" => version, "signature" => signature} = attrs) do
15-
with {:ok, %Operator{} = operator} <- Operators.update_operator(version, signature, attrs) do
14+
def create_or_update(
15+
conn,
16+
%{
17+
"address" => address,
18+
"version" => version,
19+
"signature" => signature,
20+
"pub_key_g2" => pub_key_g2
21+
} = attrs
22+
) do
23+
with {:ok, %Operator{} = operator} <-
24+
Operators.update_operator(address, version, signature, pub_key_g2, attrs) do
1625
conn
1726
|> put_status(:created)
1827
|> put_resp_header("location", ~p"/api/operators/#{operator}")

telemetry_api/priv/abi/IBLSApkRegistry.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)