From 0936f5f5ed0c100ab5751e3d856595770438212b Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Wed, 4 Dec 2024 08:55:01 -0600 Subject: [PATCH 01/94] Enable TLS connections between the disperser and the DA nodes. Signed-off-by: Cody Littley --- .gitignore | 3 ++ disperser/auth/generate-cert.sh | 47 ++++++++++++++++++++++++++ disperser/auth/generate-private-key.sh | 5 +++ 3 files changed, 55 insertions(+) create mode 100755 disperser/auth/generate-cert.sh create mode 100755 disperser/auth/generate-private-key.sh diff --git a/.gitignore b/.gitignore index 40fe5f34c..b5043399c 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,6 @@ lightnode/docker/args.sh .vscode icicle/* + +# Just to make sure somebody doesn't accidentally commit the disperser's private TLS key. +eigenda-disperser-private.key diff --git a/disperser/auth/generate-cert.sh b/disperser/auth/generate-cert.sh new file mode 100755 index 000000000..a273cb6b7 --- /dev/null +++ b/disperser/auth/generate-cert.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash + +if [ -z "${1}" ]; then + echo "Usage: $0 " + exit 1 +fi + +# Used to pass options to the 'openssl req' command. +# It expects a human in the loop, but it's preferable to automate it. +options() { + # Country Name (2 letter code) [AU]: + echo 'US' + # State or Province Name (full name) [Some-State]: + echo 'Washington' + # Locality Name (eg, city) []: + echo 'Seattle' + # Organization Name (eg, company) [Internet Widgits Pty Ltd]: + echo 'Eigen Labs' + # Organizational Unit Name (eg, section) []: + echo 'EigenDA' + # Common Name (e.g. server FQDN or YOUR name) []: + echo 'disperser' + # Email Address []: + echo '.' + # A challenge password []: + echo '.' + # An optional company name []: + echo '.' +} + +# Generate a new certificate signing request. +options | \ + openssl req -new \ + -key "${1}" \ + -noenc \ + -out cert.csr + +# Self sign the certificate. +openssl x509 -req \ + -days 365 \ + -in cert.csr \ + -signkey "${1}" -out eigenda-disperser-public.crt + +# Clean up the certificate signing request. +rm cert.csr + +cowsay "This certificate will expire in one year. Ensure that a new one is made available to node operators before then." \ No newline at end of file diff --git a/disperser/auth/generate-private-key.sh b/disperser/auth/generate-private-key.sh new file mode 100755 index 000000000..e5e2f9c5e --- /dev/null +++ b/disperser/auth/generate-private-key.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +# This script generates a new private key for the disperser service. + +openssl genrsa -out eigenda-disperser-private.key 4096 \ No newline at end of file From d36424e6ece1505ec714f1dc00ce285ab42d1990 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Wed, 4 Dec 2024 09:15:37 -0600 Subject: [PATCH 02/94] Incremental progress. Signed-off-by: Cody Littley --- disperser/auth/generate-cert.sh | 14 +++++++++++++- disperser/auth/next-year.py | 10 ++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100755 disperser/auth/next-year.py diff --git a/disperser/auth/generate-cert.sh b/disperser/auth/generate-cert.sh index a273cb6b7..7ea6ac4c9 100755 --- a/disperser/auth/generate-cert.sh +++ b/disperser/auth/generate-cert.sh @@ -5,6 +5,15 @@ if [ -z "${1}" ]; then exit 1 fi +if ! command -v cowsay 2>&1 >/dev/null +then + echo "cowsay is not installed. Please install it ('brew install cowsay' or 'apt-get install cowsay')." + exit 1 +fi + +# The location where this script can be found. +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + # Used to pass options to the 'openssl req' command. # It expects a human in the loop, but it's preferable to automate it. options() { @@ -44,4 +53,7 @@ openssl x509 -req \ # Clean up the certificate signing request. rm cert.csr -cowsay "This certificate will expire in one year. Ensure that a new one is made available to node operators before then." \ No newline at end of file +NEXT_YEAR=$("${SCRIPT_DIR}"'/next-year.py') + +cowsay "This certificate will expire on ${NEXT_YEAR}. Ensure that a new one is made available to node operators before then." +cowsay "This certificate will expire on ${NEXT_YEAR}." >> eigenda-disperser-public.crt diff --git a/disperser/auth/next-year.py b/disperser/auth/next-year.py new file mode 100755 index 000000000..89ad8d7d4 --- /dev/null +++ b/disperser/auth/next-year.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python3 + +# This script prints the date one year from now. This can't be done in a shell script since the date +# command differs substantially between Linux and macOS. + +import datetime + +now = datetime.datetime.now() +nextYear = now.replace(year=now.year + 1) +print(nextYear.strftime('%Y-%m-%d')) From 342d5002e0e598781b6c6b7c618f78bf8ffd850b Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Wed, 4 Dec 2024 09:23:44 -0600 Subject: [PATCH 03/94] Moar cowsay Signed-off-by: Cody Littley --- disperser/auth/generate-cert.sh | 7 ++-- disperser/auth/test.crt | 40 ++++++++++++++++++++++ disperser/auth/test.key | 60 +++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 2 deletions(-) create mode 100644 disperser/auth/test.crt create mode 100644 disperser/auth/test.key diff --git a/disperser/auth/generate-cert.sh b/disperser/auth/generate-cert.sh index 7ea6ac4c9..e8afb944c 100755 --- a/disperser/auth/generate-cert.sh +++ b/disperser/auth/generate-cert.sh @@ -53,7 +53,10 @@ openssl x509 -req \ # Clean up the certificate signing request. rm cert.csr +# Document the expiration date of the certificate. NEXT_YEAR=$("${SCRIPT_DIR}"'/next-year.py') -cowsay "This certificate will expire on ${NEXT_YEAR}. Ensure that a new one is made available to node operators before then." -cowsay "This certificate will expire on ${NEXT_YEAR}." >> eigenda-disperser-public.crt +EXPIRATION_MESSAGE=$(cowsay "This certificate expires on ${NEXT_YEAR}.") +echo -e "${EXPIRATION_MESSAGE}\n$(cat eigenda-disperser-public.crt)" > eigenda-disperser-public.crt + +cowsay "This certificate will expire on ${NEXT_YEAR}. Ensure that a new one is made available to node operators before then." \ No newline at end of file diff --git a/disperser/auth/test.crt b/disperser/auth/test.crt new file mode 100644 index 000000000..2f6a5681e --- /dev/null +++ b/disperser/auth/test.crt @@ -0,0 +1,40 @@ + _________________________________________ +< This certificate expires on 2025-12-04. > + ----------------------------------------- + \ ^__^ + \ (oo)\_______ + (__)\ )\/\ + ||----w | + || || +-----BEGIN CERTIFICATE----- +MIIFjTCCA3WgAwIBAgIUdFJ0JieaGN6/fYtgyd37CYyL6uEwDQYJKoZIhvcNAQEL +BQAwbzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcM +B1NlYXR0bGUxEzARBgNVBAoMCkVpZ2VuIExhYnMxEDAOBgNVBAsMB0VpZ2VuREEx +EjAQBgNVBAMMCWRpc3BlcnNlcjAeFw0yNDEyMDQxNTIyMTRaFw0yNTEyMDQxNTIy +MTRaMG8xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQH +DAdTZWF0dGxlMRMwEQYDVQQKDApFaWdlbiBMYWJzMRAwDgYDVQQLDAdFaWdlbkRB +MRIwEAYDVQQDDAlkaXNwZXJzZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQDmkgwhq3n+B2K3Z16ZJ8SKuw6mgsIMUmRwrnkP9PORZujeiMe9wowf1dhy +P9kR8eYnGGksx4ZV4yUjZ4XRlPMP1pAj/jXTTexkbwnZ99RqmJjNFZYtFUVT+qAE +gJtBPRwH7sZlbGVy/HqrQMEPBS2Hg6YSPcfndbiwhnsJsHak59Pkg1tQLPmXwQq8 +nHkuSrp4LHuyOGltxuI33lke+4j3Zz51o1fqHOiANBDMy6UVSTpuT+7nzdrBpQF9 +na32/PO9LpMUoIH+IcRsbT70zIaOsqCqMk2hw8sfjdKapYPt+oMw1bsGKLMfMDsy +cdmUpMFhcLtkwxaUMMaxc5LM/e442NDO78wkA9BpvXFSshMIb6F0otE4caRlI9fG +GkwHlx5+QE3hQZXgMy1FjEjKl63YAnrq2R0x+uheqTLwTvjCqMp3gZUHjCdnawrz +JEj3lp3rU+6/QeV7LxAaNdo0Liq37qvesGgNOicdSuQ6MNZccLDx6h2rywhpjmhi +jrXL7GgH6bBhUTllNyF2PMsuBrSuyggUJ7TR/wM+zXIFcRwb4eWs5sFztTl+PuhI +lFjIBE3UpHf8XFdmSsnXTg7AjVYjqr/gXFn2/cdZGeLPDnBLMm/MbCgR4McO/udu +feRJM2QYd0gVxjXxYc4NJS2SNANuT9c0XTlh01bN8wULsvjJOQIDAQABoyEwHzAd +BgNVHQ4EFgQUUG2BAXYevOBW66TfOFnT2WIyg14wDQYJKoZIhvcNAQELBQADggIB +AA64nu3LzKgB6tMMzLYLJjTCbhkEJS6JOqhjH+m/jVztjEiSQCNmPtklq5Cgyl3U +VvE5oz/bAJVL0wJIFBD1t0F2UCCYq6RtFfDMAr4+kO2E6l3g/5rcq/hEcpLprrVU +mutWLioOX/0bp8ivrJB5PdSt/jrXfjeX+9vhOhE7ZkuWJhvafNEJ+eL/mpfexbbe +wzkHKPiJHZyeyuq9tShGO7bxK3YkFTqNxV/DAJp/CCtHkdOQtVHUK72MURuzld06 +rYY1FQcknD6dD8M/ePbgaWD6fwFc+zbj4D/Wfh4eHKIZ4y+hdCMO4rgcD0v5tssm +PH7IdT61bTuOUqA/zpT1mlH9VqCB/HPnkpAHGz5JgpFAequZ/xivQWszld2gjGep +jp9SXqPybBsUsMyYRphvqzsJaCGG+x9PUF3pvZ/VK+3FQYHjVUwqEcyFPf/5ht3A +VF8DXKNykg3+S0bpX9SNQt8mVo3DxsMIZpnGL6293fPW36qmkZKLquBGpLKK8sWE +2sVmM6SzWqCenV2PVVIj3hu9xap52vvU+81P1xiS7MgwavbfehY8oK0hByRq3ax/ +Fb6uEb2CRVRv9BHKuhoiTCug096c8iMBAMB6u4G4kDMijoKry+UeEJmzrKHKiVP9 +A3iWFWshXRyPkFOu1x+Y21963kmwsItbFrHBBJbOnKp2 +-----END CERTIFICATE----- diff --git a/disperser/auth/test.key b/disperser/auth/test.key new file mode 100644 index 000000000..649526fc1 --- /dev/null +++ b/disperser/auth/test.key @@ -0,0 +1,60 @@ + _______________________________________ +< this key is for testing purposes only > + --------------------------------------- + \ ^__^ + \ (oo)\_______ + (__)\ )\/\ + ||----w | + || || +-----BEGIN PRIVATE KEY----- +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 +esM9YhtyGK3Pc3Z4huJmGrpIoBqtSA== +-----END PRIVATE KEY----- From fd2ab69fe29b8bbae9db3cc20e228483f8810903 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Wed, 4 Dec 2024 09:24:44 -0600 Subject: [PATCH 04/94] formatting Signed-off-by: Cody Littley --- disperser/auth/generate-cert.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/disperser/auth/generate-cert.sh b/disperser/auth/generate-cert.sh index e8afb944c..1cbce6b9e 100755 --- a/disperser/auth/generate-cert.sh +++ b/disperser/auth/generate-cert.sh @@ -55,8 +55,6 @@ rm cert.csr # Document the expiration date of the certificate. NEXT_YEAR=$("${SCRIPT_DIR}"'/next-year.py') - EXPIRATION_MESSAGE=$(cowsay "This certificate expires on ${NEXT_YEAR}.") echo -e "${EXPIRATION_MESSAGE}\n$(cat eigenda-disperser-public.crt)" > eigenda-disperser-public.crt - -cowsay "This certificate will expire on ${NEXT_YEAR}. Ensure that a new one is made available to node operators before then." \ No newline at end of file +cowsay "This certificate expires on ${NEXT_YEAR}. Ensure that a new one is made available to node operators before then." \ No newline at end of file From e2e3fefe8f39207c6dcbbae19a478bb66b1f56a9 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Wed, 4 Dec 2024 10:58:15 -0600 Subject: [PATCH 05/94] Started working on unit test. Signed-off-by: Cody Littley --- disperser/auth/auth_test.go | 72 +++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 disperser/auth/auth_test.go diff --git a/disperser/auth/auth_test.go b/disperser/auth/auth_test.go new file mode 100644 index 000000000..5017b8449 --- /dev/null +++ b/disperser/auth/auth_test.go @@ -0,0 +1,72 @@ +package auth + +import ( + "context" + "fmt" + "github.com/Layr-Labs/eigenda/api/grpc/node/v2" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "net" + "testing" +) + +var _ v2.DispersalServer = (*mockDispersalServer)(nil) + +type mockDispersalServer struct { + v2.DispersalServer +} + +func (s *mockDispersalServer) StoreChunks(context.Context, *v2.StoreChunksRequest) (*v2.StoreChunksReply, error) { + fmt.Printf("called StoreChunks\n") + return nil, nil +} + +func (s *mockDispersalServer) NodeInfo(context.Context, *v2.NodeInfoRequest) (*v2.NodeInfoReply, error) { + fmt.Printf("called NodeInfo\n") + return nil, nil +} + +func buildClient(t *testing.T) v2.DispersalClient { + addr := "0.0.0.0:50051" + + options := make([]grpc.DialOption, 0) + //options = append(options, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{}))) + options = append(options, grpc.WithTransportCredentials(insecure.NewCredentials())) + + conn, err := grpc.NewClient(addr, options...) + require.NoError(t, err) + + return v2.NewDispersalClient(conn) +} + +func buildServer(t *testing.T) (v2.DispersalServer, *grpc.Server) { + dispersalServer := &mockDispersalServer{} + + options := make([]grpc.ServerOption, 0) + server := grpc.NewServer(options...) + v2.RegisterDispersalServer(server, dispersalServer) + + addr := "0.0.0.0:50051" + listener, err := net.Listen("tcp", addr) + require.NoError(t, err) + + go func() { + err = server.Serve(listener) + require.NoError(t, err) + }() + + return dispersalServer, server +} + +func TestServerWithTLS(t *testing.T) { + dispersalServer, server := buildServer(t) + defer server.Stop() + require.NotNil(t, dispersalServer) // TODO remove + + client := buildClient(t) + + response, err := client.NodeInfo(context.Background(), &v2.NodeInfoRequest{}) + require.NoError(t, err) + require.NotNil(t, response) +} From 43730956f7a5c3b81d642d35c35e13642f34b1b1 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Wed, 4 Dec 2024 13:11:57 -0600 Subject: [PATCH 06/94] Incremental progress. Signed-off-by: Cody Littley --- disperser/auth/auth_test.go | 48 +++++++++++++++++++++---- disperser/auth/generate-cert.sh | 14 +++++++- disperser/auth/test-disperser.crt | 40 +++++++++++++++++++++ disperser/auth/test-disperser.key | 52 +++++++++++++++++++++++++++ disperser/auth/test-node.crt | 40 +++++++++++++++++++++ disperser/auth/test-node.key | 52 +++++++++++++++++++++++++++ disperser/auth/test.crt | 40 --------------------- disperser/auth/test.key | 60 ------------------------------- 8 files changed, 238 insertions(+), 108 deletions(-) create mode 100644 disperser/auth/test-disperser.crt create mode 100644 disperser/auth/test-disperser.key create mode 100644 disperser/auth/test-node.crt create mode 100644 disperser/auth/test-node.key delete mode 100644 disperser/auth/test.crt delete mode 100644 disperser/auth/test.key diff --git a/disperser/auth/auth_test.go b/disperser/auth/auth_test.go index 5017b8449..96de6b1e7 100644 --- a/disperser/auth/auth_test.go +++ b/disperser/auth/auth_test.go @@ -2,15 +2,21 @@ package auth import ( "context" + "crypto/tls" + "crypto/x509" "fmt" "github.com/Layr-Labs/eigenda/api/grpc/node/v2" "github.com/stretchr/testify/require" "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/credentials" "net" + "os" "testing" ) +// The purpose of these tests are to verify that TLS key generation works as expected. +// TODO recreate keys each time a test is run + var _ v2.DispersalServer = (*mockDispersalServer)(nil) type mockDispersalServer struct { @@ -30,11 +36,23 @@ func (s *mockDispersalServer) NodeInfo(context.Context, *v2.NodeInfoRequest) (*v func buildClient(t *testing.T) v2.DispersalClient { addr := "0.0.0.0:50051" - options := make([]grpc.DialOption, 0) - //options = append(options, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{}))) - options = append(options, grpc.WithTransportCredentials(insecure.NewCredentials())) + cert, err := tls.LoadX509KeyPair("./test-disperser.crt", "./test-disperser.key") + require.NoError(t, err) - conn, err := grpc.NewClient(addr, options...) + nodeCert, err := os.ReadFile("./test-node.crt") + require.NoError(t, err) + certPool := x509.NewCertPool() + ok := certPool.AppendCertsFromPEM(nodeCert) + require.True(t, ok) + + creds := credentials.NewTLS(&tls.Config{ + Certificates: []tls.Certificate{cert}, + RootCAs: certPool, + //ServerName: "0.0.0.0", + //InsecureSkipVerify: true, + }) + + conn, err := grpc.NewClient(addr, grpc.WithTransportCredentials(creds)) require.NoError(t, err) return v2.NewDispersalClient(conn) @@ -43,8 +61,24 @@ func buildClient(t *testing.T) v2.DispersalClient { func buildServer(t *testing.T) (v2.DispersalServer, *grpc.Server) { dispersalServer := &mockDispersalServer{} - options := make([]grpc.ServerOption, 0) - server := grpc.NewServer(options...) + cert, err := tls.LoadX509KeyPair("./test-node.crt", "./test-node.key") + require.NoError(t, err) + + disperserCert, err := os.ReadFile("./test-disperser.crt") + require.NoError(t, err) + certPool := x509.NewCertPool() + certPool.AppendCertsFromPEM(disperserCert) + ok := certPool.AppendCertsFromPEM(disperserCert) + require.True(t, ok) + + creds := credentials.NewTLS(&tls.Config{ + Certificates: []tls.Certificate{cert}, + RootCAs: certPool, + ClientAuth: tls.RequireAndVerifyClientCert, // TODO commenting this makes things pass + //ServerName: "0.0.0.0", + }) + + server := grpc.NewServer(grpc.Creds(creds)) v2.RegisterDispersalServer(server, dispersalServer) addr := "0.0.0.0:50051" diff --git a/disperser/auth/generate-cert.sh b/disperser/auth/generate-cert.sh index 1cbce6b9e..cb45ce53b 100755 --- a/disperser/auth/generate-cert.sh +++ b/disperser/auth/generate-cert.sh @@ -28,7 +28,7 @@ options() { # Organizational Unit Name (eg, section) []: echo 'EigenDA' # Common Name (e.g. server FQDN or YOUR name) []: - echo 'disperser' + echo '.' # Email Address []: echo '.' # A challenge password []: @@ -42,14 +42,26 @@ options | \ openssl req -new \ -key "${1}" \ -noenc \ + -addext "subjectAltName = IP:0.0.0.0" \ -out cert.csr +if [ $? -ne 0 ]; then + echo "Failed to generate certificate signing request." + exit 1 +fi + # Self sign the certificate. openssl x509 -req \ -days 365 \ -in cert.csr \ + -copy_extensions=copyall \ -signkey "${1}" -out eigenda-disperser-public.crt +if [ $? -ne 0 ]; then + echo "Failed to generate certificate." + exit 1 +fi + # Clean up the certificate signing request. rm cert.csr diff --git a/disperser/auth/test-disperser.crt b/disperser/auth/test-disperser.crt new file mode 100644 index 000000000..af43313b9 --- /dev/null +++ b/disperser/auth/test-disperser.crt @@ -0,0 +1,40 @@ + _________________________________________ +< This certificate expires on 2025-12-04. > + ----------------------------------------- + \ ^__^ + \ (oo)\_______ + (__)\ )\/\ + ||----w | + || || +-----BEGIN CERTIFICATE----- +MIIFdjCCA16gAwIBAgIUK6diFhVTWqRt5ehQFZSwRkXxsO8wDQYJKoZIhvcNAQEL +BQAwWzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcM +B1NlYXR0bGUxEzARBgNVBAoMCkVpZ2VuIExhYnMxEDAOBgNVBAsMB0VpZ2VuREEw +HhcNMjQxMjA0MTkwNTQ0WhcNMjUxMjA0MTkwNTQ0WjBbMQswCQYDVQQGEwJVUzET +MBEGA1UECAwKV2FzaGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTETMBEGA1UECgwK +RWlnZW4gTGFiczEQMA4GA1UECwwHRWlnZW5EQTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBANPr2kpaJnVCha5VG8sfVm3ZVTSKJvX1/fKmSNjeQdRSRNPK +gIVWhQFwBL6ZYI9uQug/NUEvJyMQ8CsUOCEzRE9TKV41t30shf7SR900CEuFskHW +NXKiLmJUjtCc+K1d3+5zya99A+gwsO0KzADROUk/KcduAlJ/yZ4b0ZZF+rvZ7v36 ++efrJeJ6jARrEiY29CKc5oPAbSxQFTII/3XWENZR9igE+HzfC1DXRMqM35gbzpGC +mLoG+tEzNkysct4hHV6xjpK5tEEFmU7haMIZGbNVhncJT15gCF76zEZTMe2AZte3 +D/KkmfYcjjiLnKyF0jMEKk23h26gUKG3qik2st8hPmYmnipwfWJb433FuhFDKBXF +wMY82k+b0TpX8Pk6wmAWSsDz02xE1KETSTvJzQ6/s6uc0O5nrfj0e9M6wnkWb8EG +su35DarlvQFMFu45eYhGgvgEfedMGwiptpmul+VMCJtTxXQQy8ZHZgfLkXK0XJm8 +TiExj6ae8q+V3DDMDAKGwRBNlj+Enw8wZMQLgS/4QGhvVZWiWXnjtxUkZIjO/TBX +dd0Y5uTqTr8ynNg5JE/nywuwr9Us9DZdZm11AzLurrZ5zAt3yvzjFC3zSXv6xfVL ++X1kNV/Pje2EuPMr3LMxOmtY+jfixZEZXCDyk0u9nhfdJicMTLeg/uBFow3LAgMB +AAGjMjAwMA8GA1UdEQQIMAaHBAAAAAAwHQYDVR0OBBYEFNplMkMMZQckbsO1UNga +kHj2IqqEMA0GCSqGSIb3DQEBCwUAA4ICAQByff61bLfXbC80j7n7fQki4oU2XcXM +bT7W3Dc/s8IWbRKC5KGmXn4qkOeqxOSnXWIQavzebou6gbqJe7s3ulA8shDPIGNL +XwETMul1ikJEAnFMGrlWTYvBr2FYI+wcK/tDryyRRyUVV23GLbCHTDCb9tAG94mA +7TFHGEqDl+KCaxvedcqjdQJ96kHoVTd+wx0f//wCsUMEtnGfHtmclWBiuGYs/LIq +mlSrehc4PVazNw0AJHb01YN3OtWqfA5rE+7HEpVz8jc0JkJ1o4F/Ui9HcZ+UWVF7 +qWolCSLEe9/XQAbPd7PTdwFkRIMRvcfw8Aj3MdmnI6sdy7dmkcWgOfLe2HtXUv4G +DLlT6Zy6R3Wh7L+vcOI2SBF5W9JAiwrqlhFcSq+F+r8CyxtE8wkCGfgZYaf4gZu/ +/D32hxSyMo2WkEEmOKVcJ5pX2jnE3AL4yGwEkrLOAJxRqFm0t/xi/cdO8z0o1tbk +qawyZz2YX2VIXXTxlepcitBh/s1Y1JaETLbeNg7FSYtVLkJQcCHJxR51c1r7326z +htyovlK2foJYjfilfaWqVe2UkxUuhy77bX5N5mucZgOm/zdg/5LjJEBPgCFP3y9c +jZEG7k9RNSvPIR8bsVYy7lD9SlgnxBrZkwu4nl8AU1iKk4lvl+l6t3Uktdue9kIn +wV8xikDOZ/3kKw== +-----END CERTIFICATE----- diff --git a/disperser/auth/test-disperser.key b/disperser/auth/test-disperser.key new file mode 100644 index 000000000..b754feb3b --- /dev/null +++ b/disperser/auth/test-disperser.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +Hcp7KaKkfbbscYe86aArwdI/3e5ucg== +-----END PRIVATE KEY----- diff --git a/disperser/auth/test-node.crt b/disperser/auth/test-node.crt new file mode 100644 index 000000000..dfa7238cc --- /dev/null +++ b/disperser/auth/test-node.crt @@ -0,0 +1,40 @@ + _________________________________________ +< This certificate expires on 2025-12-04. > + ----------------------------------------- + \ ^__^ + \ (oo)\_______ + (__)\ )\/\ + ||----w | + || || +-----BEGIN CERTIFICATE----- +MIIFdjCCA16gAwIBAgIURmsPi7n6NYchkG9fkrYPnj+HJQYwDQYJKoZIhvcNAQEL +BQAwWzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcM +B1NlYXR0bGUxEzARBgNVBAoMCkVpZ2VuIExhYnMxEDAOBgNVBAsMB0VpZ2VuREEw +HhcNMjQxMjA0MTkwNjI1WhcNMjUxMjA0MTkwNjI1WjBbMQswCQYDVQQGEwJVUzET +MBEGA1UECAwKV2FzaGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTETMBEGA1UECgwK +RWlnZW4gTGFiczEQMA4GA1UECwwHRWlnZW5EQTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBALJnA78QyKdEdTQ/SVG4qlUfhXhi8vKq4oFBiCBT2zsU7fra +mIkp99LB1obPjx11uArfbFYE/36G/SzTOT/eVWvDnAbW7vxz7kd3ehzpaQvkwFZz +K6mJOsUScMRhmU4+91QVeYThOv6lmCsA8jEBfnkC19sflI1Aios8NAGGLmcFPXpk +/fzmSUBhlFgw/AKb2px3sgVOECRz1hQBlLc9/XmCLJEVmWfp4Hh+fRTSfXmLdXvC +WkzVz91kBKY56gkI1KDZnP4+hIxb7nSBmiFcUdOlMeWHqX7b5rKxNvs2SQUff9d2 +jy/hGiiCW9xwh28SCT9QSnK1mmGm5qCp2SFEpCcq/3ohf3YEAHHGKKDHUXY5DJJq +VUB1UdLpgdwx1iVm5BRWwjRbfmxcf1AgZcwUAwASU6li2HyHbi5rD4lsrDGfNS/M +0NXJG8tEG+OmfAJrRNTeV3pkXMoDS2bfWpvCjjBSrPrfIBl0xed/omRy6FYAual9 +IxGr1QrSkYj+Zv4puNlQBPWan9oM6xeuU+1LE3gJWRcIKE23/UAYh+kKmkv10hG3 +AyosvBlorB9SNi3l3CbNNXS8XiqFO6pGbZya5eFB1x7yHHrb3JrOer2iHe2Hhd+Y +5U4XgDXDQ+jhmv6PTxO/Ej/LLlyVLIRXNz2U+mVDmHzJU9bEbGKadxXIdo67AgMB +AAGjMjAwMA8GA1UdEQQIMAaHBAAAAAAwHQYDVR0OBBYEFD8lGujebczd7MIwTGHd +tHNCVhkFMA0GCSqGSIb3DQEBCwUAA4ICAQCWXU86yDoie4O4bcYDodadEmeh1dlS +a9uCQIRuFxrR8L4AsZ5+F0zQ4sLGUxhkaYK0lESsLiirEnGYMZfSnF+CwMglSsdE +gRj5tUSutoC9dBMTYp+7DXNZR9Ixx2qHOPufj4zX5+zMrElhHNSIFBwxzGfhpSxW +dXFvXJmj6gZB5JAqxj1EVl1VDdXlj/JEZcKJ25ImXh1VEnHp97sxLaVB21DlwkYC +agQGHCHizMmUbrHQw22TzTvE1+6ALmcaMbk8d3Q1tJmqh+6lPxIae/q46+rPCH2k +/LP3Okgbc1UVd4OplSvRmlV6k2+H39vAOieau8tGINvkuS2lxptu2T6W5DaZUwhA +1x5A1bDUWv8Uei/DH8T27782loLPt/RbrvW65117PgNv4MgWpFqPwFyIrka/bsw9 ++afgq9CpC1/KsT4hXr5Bl2IT5v9d6ggk1cEccQRzvfpWDGlfbgsLOH9L1+qvEt68 +vdV2l9TyVI7vTIMMoEuv9Ymgu4jMzEx4rNmwx29Bb3I3KF6c3uLyO8MOpuENMWE+ +Ul41FVgQuUfHahJ+LbaeN/CIwCO0R1twkf7ES4NPzWxOre47T71a40LO11dexAXl +DnDpVhfNCbVQom0W6WipbFMWATGBTHPys0lekmh6O1dspxxWxqZoHv5ZJNQugvi0 +PEsl3ch0zDd1Rg== +-----END CERTIFICATE----- diff --git a/disperser/auth/test-node.key b/disperser/auth/test-node.key new file mode 100644 index 000000000..f788818e3 --- /dev/null +++ b/disperser/auth/test-node.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +d/czU7IvbTeqQE0aHPnFNaWJfgKs2Fs= +-----END PRIVATE KEY----- diff --git a/disperser/auth/test.crt b/disperser/auth/test.crt deleted file mode 100644 index 2f6a5681e..000000000 --- a/disperser/auth/test.crt +++ /dev/null @@ -1,40 +0,0 @@ - _________________________________________ -< This certificate expires on 2025-12-04. > - ----------------------------------------- - \ ^__^ - \ (oo)\_______ - (__)\ )\/\ - ||----w | - || || ------BEGIN CERTIFICATE----- -MIIFjTCCA3WgAwIBAgIUdFJ0JieaGN6/fYtgyd37CYyL6uEwDQYJKoZIhvcNAQEL -BQAwbzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcM -B1NlYXR0bGUxEzARBgNVBAoMCkVpZ2VuIExhYnMxEDAOBgNVBAsMB0VpZ2VuREEx -EjAQBgNVBAMMCWRpc3BlcnNlcjAeFw0yNDEyMDQxNTIyMTRaFw0yNTEyMDQxNTIy -MTRaMG8xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQH -DAdTZWF0dGxlMRMwEQYDVQQKDApFaWdlbiBMYWJzMRAwDgYDVQQLDAdFaWdlbkRB -MRIwEAYDVQQDDAlkaXNwZXJzZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK -AoICAQDmkgwhq3n+B2K3Z16ZJ8SKuw6mgsIMUmRwrnkP9PORZujeiMe9wowf1dhy -P9kR8eYnGGksx4ZV4yUjZ4XRlPMP1pAj/jXTTexkbwnZ99RqmJjNFZYtFUVT+qAE -gJtBPRwH7sZlbGVy/HqrQMEPBS2Hg6YSPcfndbiwhnsJsHak59Pkg1tQLPmXwQq8 -nHkuSrp4LHuyOGltxuI33lke+4j3Zz51o1fqHOiANBDMy6UVSTpuT+7nzdrBpQF9 -na32/PO9LpMUoIH+IcRsbT70zIaOsqCqMk2hw8sfjdKapYPt+oMw1bsGKLMfMDsy -cdmUpMFhcLtkwxaUMMaxc5LM/e442NDO78wkA9BpvXFSshMIb6F0otE4caRlI9fG -GkwHlx5+QE3hQZXgMy1FjEjKl63YAnrq2R0x+uheqTLwTvjCqMp3gZUHjCdnawrz -JEj3lp3rU+6/QeV7LxAaNdo0Liq37qvesGgNOicdSuQ6MNZccLDx6h2rywhpjmhi -jrXL7GgH6bBhUTllNyF2PMsuBrSuyggUJ7TR/wM+zXIFcRwb4eWs5sFztTl+PuhI -lFjIBE3UpHf8XFdmSsnXTg7AjVYjqr/gXFn2/cdZGeLPDnBLMm/MbCgR4McO/udu -feRJM2QYd0gVxjXxYc4NJS2SNANuT9c0XTlh01bN8wULsvjJOQIDAQABoyEwHzAd -BgNVHQ4EFgQUUG2BAXYevOBW66TfOFnT2WIyg14wDQYJKoZIhvcNAQELBQADggIB -AA64nu3LzKgB6tMMzLYLJjTCbhkEJS6JOqhjH+m/jVztjEiSQCNmPtklq5Cgyl3U -VvE5oz/bAJVL0wJIFBD1t0F2UCCYq6RtFfDMAr4+kO2E6l3g/5rcq/hEcpLprrVU -mutWLioOX/0bp8ivrJB5PdSt/jrXfjeX+9vhOhE7ZkuWJhvafNEJ+eL/mpfexbbe -wzkHKPiJHZyeyuq9tShGO7bxK3YkFTqNxV/DAJp/CCtHkdOQtVHUK72MURuzld06 -rYY1FQcknD6dD8M/ePbgaWD6fwFc+zbj4D/Wfh4eHKIZ4y+hdCMO4rgcD0v5tssm -PH7IdT61bTuOUqA/zpT1mlH9VqCB/HPnkpAHGz5JgpFAequZ/xivQWszld2gjGep -jp9SXqPybBsUsMyYRphvqzsJaCGG+x9PUF3pvZ/VK+3FQYHjVUwqEcyFPf/5ht3A -VF8DXKNykg3+S0bpX9SNQt8mVo3DxsMIZpnGL6293fPW36qmkZKLquBGpLKK8sWE -2sVmM6SzWqCenV2PVVIj3hu9xap52vvU+81P1xiS7MgwavbfehY8oK0hByRq3ax/ -Fb6uEb2CRVRv9BHKuhoiTCug096c8iMBAMB6u4G4kDMijoKry+UeEJmzrKHKiVP9 -A3iWFWshXRyPkFOu1x+Y21963kmwsItbFrHBBJbOnKp2 ------END CERTIFICATE----- diff --git a/disperser/auth/test.key b/disperser/auth/test.key deleted file mode 100644 index 649526fc1..000000000 --- a/disperser/auth/test.key +++ /dev/null @@ -1,60 +0,0 @@ - _______________________________________ -< this key is for testing purposes only > - --------------------------------------- - \ ^__^ - \ (oo)\_______ - (__)\ )\/\ - ||----w | - || || ------BEGIN PRIVATE KEY----- -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 -esM9YhtyGK3Pc3Z4huJmGrpIoBqtSA== ------END PRIVATE KEY----- From df38980c6bcbc973119906f94f7a8de95f25b0ed Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Wed, 4 Dec 2024 13:24:02 -0600 Subject: [PATCH 07/94] It works now, kind of Signed-off-by: Cody Littley --- disperser/auth/auth_test.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/disperser/auth/auth_test.go b/disperser/auth/auth_test.go index 96de6b1e7..e3a70d758 100644 --- a/disperser/auth/auth_test.go +++ b/disperser/auth/auth_test.go @@ -67,14 +67,13 @@ func buildServer(t *testing.T) (v2.DispersalServer, *grpc.Server) { disperserCert, err := os.ReadFile("./test-disperser.crt") require.NoError(t, err) certPool := x509.NewCertPool() - certPool.AppendCertsFromPEM(disperserCert) ok := certPool.AppendCertsFromPEM(disperserCert) require.True(t, ok) creds := credentials.NewTLS(&tls.Config{ Certificates: []tls.Certificate{cert}, - RootCAs: certPool, - ClientAuth: tls.RequireAndVerifyClientCert, // TODO commenting this makes things pass + ClientCAs: certPool, + ClientAuth: tls.RequireAndVerifyClientCert, //ServerName: "0.0.0.0", }) From d54c7f7aaae0e2a3edc54616c28daca62c795213 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Thu, 5 Dec 2024 08:52:07 -0600 Subject: [PATCH 08/94] Incremental progress. Signed-off-by: Cody Littley --- disperser/auth/auth_test.go | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/disperser/auth/auth_test.go b/disperser/auth/auth_test.go index e3a70d758..c38b550e1 100644 --- a/disperser/auth/auth_test.go +++ b/disperser/auth/auth_test.go @@ -39,17 +39,17 @@ func buildClient(t *testing.T) v2.DispersalClient { cert, err := tls.LoadX509KeyPair("./test-disperser.crt", "./test-disperser.key") require.NoError(t, err) - nodeCert, err := os.ReadFile("./test-node.crt") - require.NoError(t, err) - certPool := x509.NewCertPool() - ok := certPool.AppendCertsFromPEM(nodeCert) - require.True(t, ok) + // This is what we'd enable if we wanted to verify the server's certificate + //nodeCert, err := os.ReadFile("./test-node.crt") + //require.NoError(t, err) + //certPool := x509.NewCertPool() + //ok := certPool.AppendCertsFromPEM(nodeCert) + //require.True(t, ok) creds := credentials.NewTLS(&tls.Config{ Certificates: []tls.Certificate{cert}, - RootCAs: certPool, - //ServerName: "0.0.0.0", - //InsecureSkipVerify: true, + //RootCAs: certPool, + InsecureSkipVerify: true, }) conn, err := grpc.NewClient(addr, grpc.WithTransportCredentials(creds)) @@ -74,7 +74,6 @@ func buildServer(t *testing.T) (v2.DispersalServer, *grpc.Server) { Certificates: []tls.Certificate{cert}, ClientCAs: certPool, ClientAuth: tls.RequireAndVerifyClientCert, - //ServerName: "0.0.0.0", }) server := grpc.NewServer(grpc.Creds(creds)) From 083d67d18520fbcbecbc274ac996102416ae3a30 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Fri, 6 Dec 2024 11:37:10 -0600 Subject: [PATCH 09/94] Start experimenting with ecdsa. Signed-off-by: Cody Littley --- .gitignore | 4 +- disperser/auth/authenticator.go | 74 ++++++++++++++++++++++++ disperser/auth/authenticator_test.go | 44 ++++++++++++++ disperser/auth/generate-cert.sh | 72 ----------------------- disperser/auth/generate-ecdsa-keypair.sh | 6 ++ disperser/auth/generate-private-key.sh | 5 -- disperser/auth/next-year.py | 10 ---- disperser/auth/test-disperser.crt | 40 ------------- disperser/auth/test-disperser.key | 52 ----------------- disperser/auth/test-node.crt | 40 ------------- disperser/auth/test-node.key | 52 ----------------- disperser/auth/test-private.pem | 16 +++++ disperser/auth/test-public.pem | 15 +++++ 13 files changed, 157 insertions(+), 273 deletions(-) create mode 100644 disperser/auth/authenticator.go create mode 100644 disperser/auth/authenticator_test.go delete mode 100755 disperser/auth/generate-cert.sh create mode 100755 disperser/auth/generate-ecdsa-keypair.sh delete mode 100755 disperser/auth/generate-private-key.sh delete mode 100755 disperser/auth/next-year.py delete mode 100644 disperser/auth/test-disperser.crt delete mode 100644 disperser/auth/test-disperser.key delete mode 100644 disperser/auth/test-node.crt delete mode 100644 disperser/auth/test-node.key create mode 100644 disperser/auth/test-private.pem create mode 100644 disperser/auth/test-public.pem diff --git a/.gitignore b/.gitignore index b5043399c..c49c17e81 100644 --- a/.gitignore +++ b/.gitignore @@ -19,5 +19,5 @@ lightnode/docker/args.sh icicle/* -# Just to make sure somebody doesn't accidentally commit the disperser's private TLS key. -eigenda-disperser-private.key +# Just to make sure somebody doesn't accidentally commit the disperser's private key. +eigenda-disperser-private.pem diff --git a/disperser/auth/authenticator.go b/disperser/auth/authenticator.go new file mode 100644 index 000000000..4cd6c8d02 --- /dev/null +++ b/disperser/auth/authenticator.go @@ -0,0 +1,74 @@ +package auth + +import ( + "crypto/ecdsa" + "crypto/x509" + "encoding/pem" + "fmt" + "io" + "os" +) + +// ReadPublicECDSAKeyFile reads a public ECDSA key from a .pem file. +func ReadPublicECDSAKeyFile(publicKeyFile string) (*ecdsa.PublicKey, error) { + file, err := os.Open(publicKeyFile) + if err != nil { + return nil, fmt.Errorf("error opening public key file: %w", err) + } + defer file.Close() + + bytes, err := io.ReadAll(file) + if err != nil { + return nil, fmt.Errorf("error reading public key file: %w", err) + } + + block, _ := pem.Decode(bytes) + if block == nil { + return nil, fmt.Errorf("no PEM data found in public key file") + } + + if block.Type != "PUBLIC KEY" { + return nil, fmt.Errorf("unexpected block type: %s", block.Type) + } + + genericPublicKey, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + return nil, fmt.Errorf("error parsing public key: %w", err) + } + + publicKey := genericPublicKey.(*ecdsa.PublicKey) + + return publicKey, nil +} + +// ReadPrivateECDSAKeyFile reads a private ECDSA key from a .pem file. +func ReadPrivateECDSAKeyFile(privateKeyFile string) (*ecdsa.PrivateKey, error) { + //publicKey, err := ReadPublicECDSAKeyFile(publicKeyFile) + + file, err := os.Open(privateKeyFile) + if err != nil { + return nil, fmt.Errorf("error opening private key file: %w", err) + } + defer file.Close() + + bytes, err := io.ReadAll(file) + if err != nil { + return nil, fmt.Errorf("error reading private key file: %w", err) + } + + block, _ := pem.Decode(bytes) + if block == nil { + return nil, fmt.Errorf("no PEM data found in private key file") + } + + if block.Type != "EC PRIVATE KEY" { + return nil, fmt.Errorf("unexpected block type: %s", block.Type) + } + + privateKey, err := x509.ParseECPrivateKey(block.Bytes) + if err != nil { + return nil, fmt.Errorf("error parsing private key: %w", err) + } + + return privateKey, nil +} diff --git a/disperser/auth/authenticator_test.go b/disperser/auth/authenticator_test.go new file mode 100644 index 000000000..8919f266d --- /dev/null +++ b/disperser/auth/authenticator_test.go @@ -0,0 +1,44 @@ +package auth + +import ( + "crypto/ecdsa" + tu "github.com/Layr-Labs/eigenda/common/testutils" + "github.com/aws/smithy-go/rand" + "github.com/stretchr/testify/require" + "testing" +) + +func TestReadingKeysFromFile(t *testing.T) { + tu.InitializeRandom() + + publicKey, err := ReadPublicECDSAKeyFile("./test-public.pem") + require.NoError(t, err) + require.NotNil(t, publicKey) + + privateKey, err := ReadPrivateECDSAKeyFile("./test-private.pem") + require.NoError(t, err) + require.NotNil(t, privateKey) + + bytesToSign := tu.RandomBytes(32) + + signature, err := ecdsa.SignASN1(rand.Reader, privateKey, bytesToSign) + require.NoError(t, err) + + isValid := ecdsa.VerifyASN1(publicKey, bytesToSign, signature) + require.True(t, isValid) + + // Change some bytes in the signature, it should be invalid now + signature2 := make([]byte, len(signature)) + copy(signature2, signature) + signature2[0] = signature2[0] + 1 + isValid = ecdsa.VerifyASN1(publicKey, bytesToSign, signature2) + require.False(t, isValid) + + // Change some bytes in the message, it should be invalid now + bytesToSign2 := make([]byte, len(bytesToSign)) + copy(bytesToSign2, bytesToSign) + bytesToSign2[0] = bytesToSign2[0] + 1 + isValid = ecdsa.VerifyASN1(publicKey, bytesToSign2, signature) + require.False(t, isValid) + +} diff --git a/disperser/auth/generate-cert.sh b/disperser/auth/generate-cert.sh deleted file mode 100755 index cb45ce53b..000000000 --- a/disperser/auth/generate-cert.sh +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env bash - -if [ -z "${1}" ]; then - echo "Usage: $0 " - exit 1 -fi - -if ! command -v cowsay 2>&1 >/dev/null -then - echo "cowsay is not installed. Please install it ('brew install cowsay' or 'apt-get install cowsay')." - exit 1 -fi - -# The location where this script can be found. -SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) - -# Used to pass options to the 'openssl req' command. -# It expects a human in the loop, but it's preferable to automate it. -options() { - # Country Name (2 letter code) [AU]: - echo 'US' - # State or Province Name (full name) [Some-State]: - echo 'Washington' - # Locality Name (eg, city) []: - echo 'Seattle' - # Organization Name (eg, company) [Internet Widgits Pty Ltd]: - echo 'Eigen Labs' - # Organizational Unit Name (eg, section) []: - echo 'EigenDA' - # Common Name (e.g. server FQDN or YOUR name) []: - echo '.' - # Email Address []: - echo '.' - # A challenge password []: - echo '.' - # An optional company name []: - echo '.' -} - -# Generate a new certificate signing request. -options | \ - openssl req -new \ - -key "${1}" \ - -noenc \ - -addext "subjectAltName = IP:0.0.0.0" \ - -out cert.csr - -if [ $? -ne 0 ]; then - echo "Failed to generate certificate signing request." - exit 1 -fi - -# Self sign the certificate. -openssl x509 -req \ - -days 365 \ - -in cert.csr \ - -copy_extensions=copyall \ - -signkey "${1}" -out eigenda-disperser-public.crt - -if [ $? -ne 0 ]; then - echo "Failed to generate certificate." - exit 1 -fi - -# Clean up the certificate signing request. -rm cert.csr - -# Document the expiration date of the certificate. -NEXT_YEAR=$("${SCRIPT_DIR}"'/next-year.py') -EXPIRATION_MESSAGE=$(cowsay "This certificate expires on ${NEXT_YEAR}.") -echo -e "${EXPIRATION_MESSAGE}\n$(cat eigenda-disperser-public.crt)" > eigenda-disperser-public.crt -cowsay "This certificate expires on ${NEXT_YEAR}. Ensure that a new one is made available to node operators before then." \ No newline at end of file diff --git a/disperser/auth/generate-ecdsa-keypair.sh b/disperser/auth/generate-ecdsa-keypair.sh new file mode 100755 index 000000000..1a017b655 --- /dev/null +++ b/disperser/auth/generate-ecdsa-keypair.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +# This script generates a new ECDSA keypair for the disperser service. + +openssl ecparam -name prime256v1 -genkey -noout -out eigenda-disperser-private.pem +openssl ec -in eigenda-disperser-private.pem -pubout -out eigenda-disperser-public.pem diff --git a/disperser/auth/generate-private-key.sh b/disperser/auth/generate-private-key.sh deleted file mode 100755 index e5e2f9c5e..000000000 --- a/disperser/auth/generate-private-key.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash - -# This script generates a new private key for the disperser service. - -openssl genrsa -out eigenda-disperser-private.key 4096 \ No newline at end of file diff --git a/disperser/auth/next-year.py b/disperser/auth/next-year.py deleted file mode 100755 index 89ad8d7d4..000000000 --- a/disperser/auth/next-year.py +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env python3 - -# This script prints the date one year from now. This can't be done in a shell script since the date -# command differs substantially between Linux and macOS. - -import datetime - -now = datetime.datetime.now() -nextYear = now.replace(year=now.year + 1) -print(nextYear.strftime('%Y-%m-%d')) diff --git a/disperser/auth/test-disperser.crt b/disperser/auth/test-disperser.crt deleted file mode 100644 index af43313b9..000000000 --- a/disperser/auth/test-disperser.crt +++ /dev/null @@ -1,40 +0,0 @@ - _________________________________________ -< This certificate expires on 2025-12-04. > - ----------------------------------------- - \ ^__^ - \ (oo)\_______ - (__)\ )\/\ - ||----w | - || || ------BEGIN CERTIFICATE----- -MIIFdjCCA16gAwIBAgIUK6diFhVTWqRt5ehQFZSwRkXxsO8wDQYJKoZIhvcNAQEL -BQAwWzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcM -B1NlYXR0bGUxEzARBgNVBAoMCkVpZ2VuIExhYnMxEDAOBgNVBAsMB0VpZ2VuREEw -HhcNMjQxMjA0MTkwNTQ0WhcNMjUxMjA0MTkwNTQ0WjBbMQswCQYDVQQGEwJVUzET -MBEGA1UECAwKV2FzaGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTETMBEGA1UECgwK -RWlnZW4gTGFiczEQMA4GA1UECwwHRWlnZW5EQTCCAiIwDQYJKoZIhvcNAQEBBQAD -ggIPADCCAgoCggIBANPr2kpaJnVCha5VG8sfVm3ZVTSKJvX1/fKmSNjeQdRSRNPK -gIVWhQFwBL6ZYI9uQug/NUEvJyMQ8CsUOCEzRE9TKV41t30shf7SR900CEuFskHW -NXKiLmJUjtCc+K1d3+5zya99A+gwsO0KzADROUk/KcduAlJ/yZ4b0ZZF+rvZ7v36 -+efrJeJ6jARrEiY29CKc5oPAbSxQFTII/3XWENZR9igE+HzfC1DXRMqM35gbzpGC -mLoG+tEzNkysct4hHV6xjpK5tEEFmU7haMIZGbNVhncJT15gCF76zEZTMe2AZte3 -D/KkmfYcjjiLnKyF0jMEKk23h26gUKG3qik2st8hPmYmnipwfWJb433FuhFDKBXF -wMY82k+b0TpX8Pk6wmAWSsDz02xE1KETSTvJzQ6/s6uc0O5nrfj0e9M6wnkWb8EG -su35DarlvQFMFu45eYhGgvgEfedMGwiptpmul+VMCJtTxXQQy8ZHZgfLkXK0XJm8 -TiExj6ae8q+V3DDMDAKGwRBNlj+Enw8wZMQLgS/4QGhvVZWiWXnjtxUkZIjO/TBX -dd0Y5uTqTr8ynNg5JE/nywuwr9Us9DZdZm11AzLurrZ5zAt3yvzjFC3zSXv6xfVL -+X1kNV/Pje2EuPMr3LMxOmtY+jfixZEZXCDyk0u9nhfdJicMTLeg/uBFow3LAgMB -AAGjMjAwMA8GA1UdEQQIMAaHBAAAAAAwHQYDVR0OBBYEFNplMkMMZQckbsO1UNga -kHj2IqqEMA0GCSqGSIb3DQEBCwUAA4ICAQByff61bLfXbC80j7n7fQki4oU2XcXM -bT7W3Dc/s8IWbRKC5KGmXn4qkOeqxOSnXWIQavzebou6gbqJe7s3ulA8shDPIGNL -XwETMul1ikJEAnFMGrlWTYvBr2FYI+wcK/tDryyRRyUVV23GLbCHTDCb9tAG94mA -7TFHGEqDl+KCaxvedcqjdQJ96kHoVTd+wx0f//wCsUMEtnGfHtmclWBiuGYs/LIq -mlSrehc4PVazNw0AJHb01YN3OtWqfA5rE+7HEpVz8jc0JkJ1o4F/Ui9HcZ+UWVF7 -qWolCSLEe9/XQAbPd7PTdwFkRIMRvcfw8Aj3MdmnI6sdy7dmkcWgOfLe2HtXUv4G -DLlT6Zy6R3Wh7L+vcOI2SBF5W9JAiwrqlhFcSq+F+r8CyxtE8wkCGfgZYaf4gZu/ -/D32hxSyMo2WkEEmOKVcJ5pX2jnE3AL4yGwEkrLOAJxRqFm0t/xi/cdO8z0o1tbk -qawyZz2YX2VIXXTxlepcitBh/s1Y1JaETLbeNg7FSYtVLkJQcCHJxR51c1r7326z -htyovlK2foJYjfilfaWqVe2UkxUuhy77bX5N5mucZgOm/zdg/5LjJEBPgCFP3y9c -jZEG7k9RNSvPIR8bsVYy7lD9SlgnxBrZkwu4nl8AU1iKk4lvl+l6t3Uktdue9kIn -wV8xikDOZ/3kKw== ------END CERTIFICATE----- diff --git a/disperser/auth/test-disperser.key b/disperser/auth/test-disperser.key deleted file mode 100644 index b754feb3b..000000000 --- a/disperser/auth/test-disperser.key +++ /dev/null @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -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 -Hcp7KaKkfbbscYe86aArwdI/3e5ucg== ------END PRIVATE KEY----- diff --git a/disperser/auth/test-node.crt b/disperser/auth/test-node.crt deleted file mode 100644 index dfa7238cc..000000000 --- a/disperser/auth/test-node.crt +++ /dev/null @@ -1,40 +0,0 @@ - _________________________________________ -< This certificate expires on 2025-12-04. > - ----------------------------------------- - \ ^__^ - \ (oo)\_______ - (__)\ )\/\ - ||----w | - || || ------BEGIN CERTIFICATE----- -MIIFdjCCA16gAwIBAgIURmsPi7n6NYchkG9fkrYPnj+HJQYwDQYJKoZIhvcNAQEL -BQAwWzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcM -B1NlYXR0bGUxEzARBgNVBAoMCkVpZ2VuIExhYnMxEDAOBgNVBAsMB0VpZ2VuREEw -HhcNMjQxMjA0MTkwNjI1WhcNMjUxMjA0MTkwNjI1WjBbMQswCQYDVQQGEwJVUzET -MBEGA1UECAwKV2FzaGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTETMBEGA1UECgwK -RWlnZW4gTGFiczEQMA4GA1UECwwHRWlnZW5EQTCCAiIwDQYJKoZIhvcNAQEBBQAD -ggIPADCCAgoCggIBALJnA78QyKdEdTQ/SVG4qlUfhXhi8vKq4oFBiCBT2zsU7fra -mIkp99LB1obPjx11uArfbFYE/36G/SzTOT/eVWvDnAbW7vxz7kd3ehzpaQvkwFZz -K6mJOsUScMRhmU4+91QVeYThOv6lmCsA8jEBfnkC19sflI1Aios8NAGGLmcFPXpk -/fzmSUBhlFgw/AKb2px3sgVOECRz1hQBlLc9/XmCLJEVmWfp4Hh+fRTSfXmLdXvC -WkzVz91kBKY56gkI1KDZnP4+hIxb7nSBmiFcUdOlMeWHqX7b5rKxNvs2SQUff9d2 -jy/hGiiCW9xwh28SCT9QSnK1mmGm5qCp2SFEpCcq/3ohf3YEAHHGKKDHUXY5DJJq -VUB1UdLpgdwx1iVm5BRWwjRbfmxcf1AgZcwUAwASU6li2HyHbi5rD4lsrDGfNS/M -0NXJG8tEG+OmfAJrRNTeV3pkXMoDS2bfWpvCjjBSrPrfIBl0xed/omRy6FYAual9 -IxGr1QrSkYj+Zv4puNlQBPWan9oM6xeuU+1LE3gJWRcIKE23/UAYh+kKmkv10hG3 -AyosvBlorB9SNi3l3CbNNXS8XiqFO6pGbZya5eFB1x7yHHrb3JrOer2iHe2Hhd+Y -5U4XgDXDQ+jhmv6PTxO/Ej/LLlyVLIRXNz2U+mVDmHzJU9bEbGKadxXIdo67AgMB -AAGjMjAwMA8GA1UdEQQIMAaHBAAAAAAwHQYDVR0OBBYEFD8lGujebczd7MIwTGHd -tHNCVhkFMA0GCSqGSIb3DQEBCwUAA4ICAQCWXU86yDoie4O4bcYDodadEmeh1dlS -a9uCQIRuFxrR8L4AsZ5+F0zQ4sLGUxhkaYK0lESsLiirEnGYMZfSnF+CwMglSsdE -gRj5tUSutoC9dBMTYp+7DXNZR9Ixx2qHOPufj4zX5+zMrElhHNSIFBwxzGfhpSxW -dXFvXJmj6gZB5JAqxj1EVl1VDdXlj/JEZcKJ25ImXh1VEnHp97sxLaVB21DlwkYC -agQGHCHizMmUbrHQw22TzTvE1+6ALmcaMbk8d3Q1tJmqh+6lPxIae/q46+rPCH2k -/LP3Okgbc1UVd4OplSvRmlV6k2+H39vAOieau8tGINvkuS2lxptu2T6W5DaZUwhA -1x5A1bDUWv8Uei/DH8T27782loLPt/RbrvW65117PgNv4MgWpFqPwFyIrka/bsw9 -+afgq9CpC1/KsT4hXr5Bl2IT5v9d6ggk1cEccQRzvfpWDGlfbgsLOH9L1+qvEt68 -vdV2l9TyVI7vTIMMoEuv9Ymgu4jMzEx4rNmwx29Bb3I3KF6c3uLyO8MOpuENMWE+ -Ul41FVgQuUfHahJ+LbaeN/CIwCO0R1twkf7ES4NPzWxOre47T71a40LO11dexAXl -DnDpVhfNCbVQom0W6WipbFMWATGBTHPys0lekmh6O1dspxxWxqZoHv5ZJNQugvi0 -PEsl3ch0zDd1Rg== ------END CERTIFICATE----- diff --git a/disperser/auth/test-node.key b/disperser/auth/test-node.key deleted file mode 100644 index f788818e3..000000000 --- a/disperser/auth/test-node.key +++ /dev/null @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -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 -d/czU7IvbTeqQE0aHPnFNaWJfgKs2Fs= ------END PRIVATE KEY----- diff --git a/disperser/auth/test-private.pem b/disperser/auth/test-private.pem new file mode 100644 index 000000000..cc5a69b24 --- /dev/null +++ b/disperser/auth/test-private.pem @@ -0,0 +1,16 @@ + ________________________________________ +/ This key is for testing purposes only, \ +| and is no way intended to be secret. | +| This private key corresponds to the | +\ public key test-public.pem. / + ---------------------------------------- + \ ^__^ + \ (oo)\_______ + (__)\ )\/\ + ||----w | + || || +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIOP8un9zVKyLMwI7JHRfL10ggWEAIJwQZD7hhrkTcGU5oAoGCCqGSM49 +AwEHoUQDQgAERrZQAbrxLSoXbOqKx/3bC/f8YDQ1uDVyJNAys4DH8AQeq59lhqYv +XEuSUlR1qgkRjllsA1wKedW3P2fkH1kbGw== +-----END EC PRIVATE KEY----- diff --git a/disperser/auth/test-public.pem b/disperser/auth/test-public.pem new file mode 100644 index 000000000..c7c656729 --- /dev/null +++ b/disperser/auth/test-public.pem @@ -0,0 +1,15 @@ + ________________________________________ +/ This key is for testing purposes only, \ +| and is no way intended to be secret. | +| This public key corresponds to the | +\ private key test-private.pem. / + ---------------------------------------- + \ ^__^ + \ (oo)\_______ + (__)\ )\/\ + ||----w | + || || +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERrZQAbrxLSoXbOqKx/3bC/f8YDQ1 +uDVyJNAys4DH8AQeq59lhqYvXEuSUlR1qgkRjllsA1wKedW3P2fkH1kbGw== +-----END PUBLIC KEY----- From 0a5c91a77f7bd28a2ccb7d6778870e2493da9454 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Tue, 10 Dec 2024 13:59:51 -0600 Subject: [PATCH 10/94] Authorize StoreChunks() requests. Signed-off-by: Cody Littley --- api/grpc/node/v2/node_v2.pb.go | 124 +++++++++++------- api/proto/node/v2/node_v2.proto | 28 ++++ .../auth/{authenticator.go => key_utils.go} | 0 ...uthenticator_test.go => key_utils_test.go} | 0 disperser/auth/request_signing.go | 85 ++++++++++++ 5 files changed, 193 insertions(+), 44 deletions(-) rename disperser/auth/{authenticator.go => key_utils.go} (100%) rename disperser/auth/{authenticator_test.go => key_utils_test.go} (100%) create mode 100644 disperser/auth/request_signing.go diff --git a/api/grpc/node/v2/node_v2.pb.go b/api/grpc/node/v2/node_v2.pb.go index 98c98ee40..7f75acfa9 100644 --- a/api/grpc/node/v2/node_v2.pb.go +++ b/api/grpc/node/v2/node_v2.pb.go @@ -21,6 +21,7 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +// Request that the Node store a batch of chunks. type StoreChunksRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -28,6 +29,32 @@ type StoreChunksRequest struct { // batch of blobs to store Batch *v2.Batch `protobuf:"bytes,1,opt,name=batch,proto3" json:"batch,omitempty"` + // Signature using the disperser's ECDSA key over keccak hash of the batch. The purpose of this signature + // is to prevent hooligans from tricking DA nodes into storing data that they shouldn't be storing. + // + // Algorithm for computing the hash is as follows. All integer values are serialized in big-endian order (unsigned). + // A reference implementation (golang) can be found at + // https://github.com/Layr-Labs/eigenda/blob/master/disperser/auth/request_signing.go + // + // 1. digest batch.BatchHeader.BatchRoot + // 2. digest batch.BatchHeader.ReferenceBlockNumber (8 bytes, unsigned big endian) + // 3. for each certificate in batch.BlobCertificates: + // a. digest certificate.BlobHeader.Version (4 bytes, unsigned big endian) + // b. for each quorum_number in certificate.BlobHeader.QuorumNumbers: + // i. digest quorum_number (4 bytes, unsigned big endian) + // c. digest certificate.BlobHeader.Commitment.Commitment + // d. digest certificate.BlobHeader.Commitment.LengthCommitment + // e. digest certificate.BlobHeader.Commitment.LengthProof + // f. digest certificate.BlobHeader.Commitment.Length (4 bytes, unsigned big endian) + // g. digest certificate.BlobHeader.PaymentHeader.AccountId + // h. digest certificate.BlobHeader.PaymentHeader.BinIndex (4 bytes, unsigned big endian) + // i. digest certificate.BlobHeader.PaymentHeader.CumulativePayment + // j. digest certificate.BlobHeader.Signature + // k. for each relay in certificate.Relays: + // i. digest relay (4 bytes, unsigned big endian) + // + // Note that this signature is not included in the hash for obvious reasons. + Signature []byte `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"` } func (x *StoreChunksRequest) Reset() { @@ -69,6 +96,13 @@ func (x *StoreChunksRequest) GetBatch() *v2.Batch { return nil } +func (x *StoreChunksRequest) GetSignature() []byte { + if x != nil { + return x.Signature + } + return nil +} + type StoreChunksReply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -347,53 +381,55 @@ var file_node_v2_node_v2_proto_rawDesc = []byte{ 0x0a, 0x15, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x76, 0x32, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x76, 0x32, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32, 0x1a, 0x16, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x76, 0x32, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, - 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x3c, 0x0a, 0x12, 0x53, 0x74, 0x6f, 0x72, + 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x5a, 0x0a, 0x12, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x05, 0x62, 0x61, 0x74, 0x63, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, - 0x05, 0x62, 0x61, 0x74, 0x63, 0x68, 0x22, 0x30, 0x0a, 0x10, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x43, - 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, - 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, - 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x4a, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x43, - 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, - 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, - 0x62, 0x6c, 0x6f, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x71, 0x75, 0x6f, 0x72, 0x75, - 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x71, 0x75, 0x6f, 0x72, - 0x75, 0x6d, 0x49, 0x64, 0x22, 0x28, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, - 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x06, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x22, 0x11, - 0x0a, 0x0f, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x22, 0x81, 0x01, 0x0a, 0x0d, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, - 0x70, 0x6c, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x6d, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x6d, 0x76, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x61, - 0x72, 0x63, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x72, 0x63, 0x68, 0x12, - 0x0e, 0x0a, 0x02, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x6f, 0x73, 0x12, - 0x17, 0x0a, 0x07, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x70, 0x75, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x43, 0x70, 0x75, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x65, 0x6d, 0x5f, - 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6d, 0x65, 0x6d, - 0x42, 0x79, 0x74, 0x65, 0x73, 0x32, 0x94, 0x01, 0x0a, 0x09, 0x44, 0x69, 0x73, 0x70, 0x65, 0x72, - 0x73, 0x61, 0x6c, 0x12, 0x47, 0x0a, 0x0b, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x75, 0x6e, - 0x6b, 0x73, 0x12, 0x1b, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x6f, - 0x72, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x19, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x43, - 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3e, 0x0a, 0x08, - 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, - 0x76, 0x32, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x6f, 0x64, - 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x32, 0x8e, 0x01, 0x0a, - 0x09, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x61, 0x6c, 0x12, 0x41, 0x0a, 0x09, 0x47, 0x65, - 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x12, 0x19, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, - 0x32, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, - 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3e, 0x0a, - 0x08, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x2e, 0x6e, 0x6f, 0x64, 0x65, - 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x6f, - 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x42, 0x2f, 0x5a, - 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4c, 0x61, 0x79, 0x72, - 0x2d, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x65, 0x69, 0x67, 0x65, 0x6e, 0x64, 0x61, 0x2f, 0x61, 0x70, - 0x69, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x76, 0x32, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x05, 0x62, 0x61, 0x74, 0x63, 0x68, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x22, 0x30, 0x0a, 0x10, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x75, + 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, + 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x4a, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x43, 0x68, 0x75, + 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, + 0x6f, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x62, 0x6c, + 0x6f, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x71, 0x75, 0x6f, 0x72, 0x75, 0x6d, 0x5f, + 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x71, 0x75, 0x6f, 0x72, 0x75, 0x6d, + 0x49, 0x64, 0x22, 0x28, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, + 0x65, 0x70, 0x6c, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0c, 0x52, 0x06, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x22, 0x11, 0x0a, 0x0f, + 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, + 0x81, 0x01, 0x0a, 0x0d, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x70, 0x6c, + 0x79, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x6d, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x73, 0x65, 0x6d, 0x76, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x72, 0x63, + 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x72, 0x63, 0x68, 0x12, 0x0e, 0x0a, + 0x02, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x6f, 0x73, 0x12, 0x17, 0x0a, + 0x07, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x70, 0x75, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, + 0x6e, 0x75, 0x6d, 0x43, 0x70, 0x75, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x65, 0x6d, 0x5f, 0x62, 0x79, + 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6d, 0x65, 0x6d, 0x42, 0x79, + 0x74, 0x65, 0x73, 0x32, 0x94, 0x01, 0x0a, 0x09, 0x44, 0x69, 0x73, 0x70, 0x65, 0x72, 0x73, 0x61, + 0x6c, 0x12, 0x47, 0x0a, 0x0b, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, + 0x12, 0x1b, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, + 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, + 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x75, + 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3e, 0x0a, 0x08, 0x4e, 0x6f, + 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32, + 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x16, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, + 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x32, 0x8e, 0x01, 0x0a, 0x09, 0x52, + 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x61, 0x6c, 0x12, 0x41, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x43, + 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x12, 0x19, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32, 0x2e, + 0x47, 0x65, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x17, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x68, + 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3e, 0x0a, 0x08, 0x4e, + 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, + 0x32, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x16, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x6f, 0x64, 0x65, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x42, 0x2f, 0x5a, 0x2d, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4c, 0x61, 0x79, 0x72, 0x2d, 0x4c, + 0x61, 0x62, 0x73, 0x2f, 0x65, 0x69, 0x67, 0x65, 0x6e, 0x64, 0x61, 0x2f, 0x61, 0x70, 0x69, 0x2f, + 0x67, 0x72, 0x70, 0x63, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x76, 0x32, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/api/proto/node/v2/node_v2.proto b/api/proto/node/v2/node_v2.proto index 7a13378be..9e2acef01 100644 --- a/api/proto/node/v2/node_v2.proto +++ b/api/proto/node/v2/node_v2.proto @@ -21,9 +21,37 @@ service Retrieval { // Requests and replies +// Request that the Node store a batch of chunks. message StoreChunksRequest { // batch of blobs to store common.v2.Batch batch = 1; + + // Signature using the disperser's ECDSA key over keccak hash of the batch. The purpose of this signature + // is to prevent hooligans from tricking DA nodes into storing data that they shouldn't be storing. + // + // Algorithm for computing the hash is as follows. All integer values are serialized in big-endian order (unsigned). + // A reference implementation (golang) can be found at + // https://github.com/Layr-Labs/eigenda/blob/master/disperser/auth/request_signing.go + // + // 1. digest batch.BatchHeader.BatchRoot + // 2. digest batch.BatchHeader.ReferenceBlockNumber (8 bytes, unsigned big endian) + // 3. for each certificate in batch.BlobCertificates: + // a. digest certificate.BlobHeader.Version (4 bytes, unsigned big endian) + // b. for each quorum_number in certificate.BlobHeader.QuorumNumbers: + // i. digest quorum_number (4 bytes, unsigned big endian) + // c. digest certificate.BlobHeader.Commitment.Commitment + // d. digest certificate.BlobHeader.Commitment.LengthCommitment + // e. digest certificate.BlobHeader.Commitment.LengthProof + // f. digest certificate.BlobHeader.Commitment.Length (4 bytes, unsigned big endian) + // g. digest certificate.BlobHeader.PaymentHeader.AccountId + // h. digest certificate.BlobHeader.PaymentHeader.BinIndex (4 bytes, unsigned big endian) + // i. digest certificate.BlobHeader.PaymentHeader.CumulativePayment + // j. digest certificate.BlobHeader.Signature + // k. for each relay in certificate.Relays: + // i. digest relay (4 bytes, unsigned big endian) + // + // Note that this signature is not included in the hash for obvious reasons. + bytes signature = 2; } message StoreChunksReply { diff --git a/disperser/auth/authenticator.go b/disperser/auth/key_utils.go similarity index 100% rename from disperser/auth/authenticator.go rename to disperser/auth/key_utils.go diff --git a/disperser/auth/authenticator_test.go b/disperser/auth/key_utils_test.go similarity index 100% rename from disperser/auth/authenticator_test.go rename to disperser/auth/key_utils_test.go diff --git a/disperser/auth/request_signing.go b/disperser/auth/request_signing.go new file mode 100644 index 000000000..b07003095 --- /dev/null +++ b/disperser/auth/request_signing.go @@ -0,0 +1,85 @@ +package auth + +import ( + "crypto/ecdsa" + "crypto/rand" + "encoding/binary" + "fmt" + commonv1 "github.com/Layr-Labs/eigenda/api/grpc/common" + common "github.com/Layr-Labs/eigenda/api/grpc/common/v2" + grpc "github.com/Layr-Labs/eigenda/api/grpc/node/v2" + "golang.org/x/crypto/sha3" + "hash" +) + +// HashStoreChunksRequest hashes the given StoreChunksRequest. TODO document how +func HashStoreChunksRequest(request *grpc.StoreChunksRequest) []byte { + hasher := sha3.NewLegacyKeccak256() + + hashBatchHeader(hasher, request.Batch.Header) + for _, blobCertificate := range request.Batch.BlobCertificates { + hashBlobCertificate(hasher, blobCertificate) + } + + return hasher.Sum(nil) +} + +func hashBlobCertificate(hasher hash.Hash, blobCertificate *common.BlobCertificate) { + hashBlobHeader(hasher, blobCertificate.BlobHeader) + for _, relayID := range blobCertificate.Relays { + hashUint32(hasher, relayID) + } +} + +func hashBlobHeader(hasher hash.Hash, header *common.BlobHeader) { + hashUint32(hasher, header.Version) + for _, quorum := range header.QuorumNumbers { + hashUint32(hasher, quorum) + } + hashBlobCommitment(hasher, header.Commitment) + hashPaymentHeader(hasher, header.PaymentHeader) + hasher.Write(header.Signature) +} + +func hashBatchHeader(hasher hash.Hash, header *common.BatchHeader) { + hasher.Write(header.BatchRoot) + hashUint64(hasher, header.ReferenceBlockNumber) +} + +func hashBlobCommitment(hasher hash.Hash, commitment *commonv1.BlobCommitment) { + hasher.Write(commitment.Commitment) + hasher.Write(commitment.LengthCommitment) + hasher.Write(commitment.LengthProof) + hashUint32(hasher, commitment.Length) +} + +func hashPaymentHeader(hasher hash.Hash, header *commonv1.PaymentHeader) { + hasher.Write([]byte(header.AccountId)) + hashUint32(hasher, header.BinIndex) + hasher.Write(header.CumulativePayment) +} + +func hashUint32(hasher hash.Hash, value uint32) { + bytes := make([]byte, 4) + binary.BigEndian.PutUint32(bytes, value) + hasher.Write(bytes) +} + +func hashUint64(hasher hash.Hash, value uint64) { + bytes := make([]byte, 8) + binary.BigEndian.PutUint64(bytes, value) + hasher.Write(bytes) +} + +// SignStoreChunksRequest signs the given StoreChunksRequest with the given private key. Does not +// write the signature into the request. +func SignStoreChunksRequest(key *ecdsa.PrivateKey, request *grpc.StoreChunksRequest) ([]byte, error) { + hash := HashStoreChunksRequest(request) + + signature, err := ecdsa.SignASN1(rand.Reader, key, hash) + if err != nil { + return nil, fmt.Errorf("failed to sign request: %w", err) + } + + return signature, nil +} From f3485506a6a0d2cd7c0478a041643267fccead41 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Tue, 10 Dec 2024 14:00:47 -0600 Subject: [PATCH 11/94] Update docs. Signed-off-by: Cody Littley --- api/docs/eigenda-protos.html | 33 ++++++++++++++++++++++++++++++++- api/docs/eigenda-protos.md | 9 ++++++++- api/docs/node_v2.html | 33 ++++++++++++++++++++++++++++++++- api/docs/node_v2.md | 9 ++++++++- 4 files changed, 80 insertions(+), 4 deletions(-) diff --git a/api/docs/eigenda-protos.html b/api/docs/eigenda-protos.html index 9f1c0c1c9..29bec7d59 100644 --- a/api/docs/eigenda-protos.html +++ b/api/docs/eigenda-protos.html @@ -3479,7 +3479,7 @@

StoreChunksReply

StoreChunksRequest

-

+

Request that the Node store a batch of chunks.

@@ -3495,6 +3495,37 @@

StoreChunksRequest

+ + + + + + +

batch of blobs to store

signaturebytes

Signature using the disperser's ECDSA key over keccak hash of the batch. The purpose of this signature +is to prevent hooligans from tricking DA nodes into storing data that they shouldn't be storing. + +Algorithm for computing the hash is as follows. All integer values are serialized in big-endian order (unsigned). +A reference implementation (golang) can be found at +https://github.com/Layr-Labs/eigenda/blob/master/disperser/auth/request_signing.go + +1. digest batch.BatchHeader.BatchRoot +2. digest batch.BatchHeader.ReferenceBlockNumber (8 bytes, unsigned big endian) +3. for each certificate in batch.BlobCertificates: + a. digest certificate.BlobHeader.Version (4 bytes, unsigned big endian) + b. for each quorum_number in certificate.BlobHeader.QuorumNumbers: + i. digest quorum_number (4 bytes, unsigned big endian) + c. digest certificate.BlobHeader.Commitment.Commitment + d. digest certificate.BlobHeader.Commitment.LengthCommitment + e. digest certificate.BlobHeader.Commitment.LengthProof + f. digest certificate.BlobHeader.Commitment.Length (4 bytes, unsigned big endian) + g. digest certificate.BlobHeader.PaymentHeader.AccountId + h. digest certificate.BlobHeader.PaymentHeader.BinIndex (4 bytes, unsigned big endian) + i. digest certificate.BlobHeader.PaymentHeader.CumulativePayment + j. digest certificate.BlobHeader.Signature + k. for each relay in certificate.Relays: + i. digest relay (4 bytes, unsigned big endian) + +Note that this signature is not included in the hash for obvious reasons.

diff --git a/api/docs/eigenda-protos.md b/api/docs/eigenda-protos.md index 8b4992a42..7c6777c81 100644 --- a/api/docs/eigenda-protos.md +++ b/api/docs/eigenda-protos.md @@ -1476,12 +1476,19 @@ Node info request ### StoreChunksRequest - +Request that the Node store a batch of chunks. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | batch | [common.v2.Batch](#common-v2-Batch) | | batch of blobs to store | +| signature | [bytes](#bytes) | | Signature using the disperser's ECDSA key over keccak hash of the batch. The purpose of this signature is to prevent hooligans from tricking DA nodes into storing data that they shouldn't be storing. + +Algorithm for computing the hash is as follows. All integer values are serialized in big-endian order (unsigned). A reference implementation (golang) can be found at https://github.com/Layr-Labs/eigenda/blob/master/disperser/auth/request_signing.go + +1. digest batch.BatchHeader.BatchRoot 2. digest batch.BatchHeader.ReferenceBlockNumber (8 bytes, unsigned big endian) 3. for each certificate in batch.BlobCertificates: a. digest certificate.BlobHeader.Version (4 bytes, unsigned big endian) b. for each quorum_number in certificate.BlobHeader.QuorumNumbers: i. digest quorum_number (4 bytes, unsigned big endian) c. digest certificate.BlobHeader.Commitment.Commitment d. digest certificate.BlobHeader.Commitment.LengthCommitment e. digest certificate.BlobHeader.Commitment.LengthProof f. digest certificate.BlobHeader.Commitment.Length (4 bytes, unsigned big endian) g. digest certificate.BlobHeader.PaymentHeader.AccountId h. digest certificate.BlobHeader.PaymentHeader.BinIndex (4 bytes, unsigned big endian) i. digest certificate.BlobHeader.PaymentHeader.CumulativePayment j. digest certificate.BlobHeader.Signature k. for each relay in certificate.Relays: i. digest relay (4 bytes, unsigned big endian) + +Note that this signature is not included in the hash for obvious reasons. | diff --git a/api/docs/node_v2.html b/api/docs/node_v2.html index 22d657aca..3cbebeaaa 100644 --- a/api/docs/node_v2.html +++ b/api/docs/node_v2.html @@ -369,7 +369,7 @@

StoreChunksReply

StoreChunksRequest

-

+

Request that the Node store a batch of chunks.

@@ -385,6 +385,37 @@

StoreChunksRequest

+ + + + + + +

batch of blobs to store

signaturebytes

Signature using the disperser's ECDSA key over keccak hash of the batch. The purpose of this signature +is to prevent hooligans from tricking DA nodes into storing data that they shouldn't be storing. + +Algorithm for computing the hash is as follows. All integer values are serialized in big-endian order (unsigned). +A reference implementation (golang) can be found at +https://github.com/Layr-Labs/eigenda/blob/master/disperser/auth/request_signing.go + +1. digest batch.BatchHeader.BatchRoot +2. digest batch.BatchHeader.ReferenceBlockNumber (8 bytes, unsigned big endian) +3. for each certificate in batch.BlobCertificates: + a. digest certificate.BlobHeader.Version (4 bytes, unsigned big endian) + b. for each quorum_number in certificate.BlobHeader.QuorumNumbers: + i. digest quorum_number (4 bytes, unsigned big endian) + c. digest certificate.BlobHeader.Commitment.Commitment + d. digest certificate.BlobHeader.Commitment.LengthCommitment + e. digest certificate.BlobHeader.Commitment.LengthProof + f. digest certificate.BlobHeader.Commitment.Length (4 bytes, unsigned big endian) + g. digest certificate.BlobHeader.PaymentHeader.AccountId + h. digest certificate.BlobHeader.PaymentHeader.BinIndex (4 bytes, unsigned big endian) + i. digest certificate.BlobHeader.PaymentHeader.CumulativePayment + j. digest certificate.BlobHeader.Signature + k. for each relay in certificate.Relays: + i. digest relay (4 bytes, unsigned big endian) + +Note that this signature is not included in the hash for obvious reasons.

diff --git a/api/docs/node_v2.md b/api/docs/node_v2.md index 27dbaace4..2e2d36eac 100644 --- a/api/docs/node_v2.md +++ b/api/docs/node_v2.md @@ -103,12 +103,19 @@ Node info request ### StoreChunksRequest - +Request that the Node store a batch of chunks. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | batch | [common.v2.Batch](#common-v2-Batch) | | batch of blobs to store | +| signature | [bytes](#bytes) | | Signature using the disperser's ECDSA key over keccak hash of the batch. The purpose of this signature is to prevent hooligans from tricking DA nodes into storing data that they shouldn't be storing. + +Algorithm for computing the hash is as follows. All integer values are serialized in big-endian order (unsigned). A reference implementation (golang) can be found at https://github.com/Layr-Labs/eigenda/blob/master/disperser/auth/request_signing.go + +1. digest batch.BatchHeader.BatchRoot 2. digest batch.BatchHeader.ReferenceBlockNumber (8 bytes, unsigned big endian) 3. for each certificate in batch.BlobCertificates: a. digest certificate.BlobHeader.Version (4 bytes, unsigned big endian) b. for each quorum_number in certificate.BlobHeader.QuorumNumbers: i. digest quorum_number (4 bytes, unsigned big endian) c. digest certificate.BlobHeader.Commitment.Commitment d. digest certificate.BlobHeader.Commitment.LengthCommitment e. digest certificate.BlobHeader.Commitment.LengthProof f. digest certificate.BlobHeader.Commitment.Length (4 bytes, unsigned big endian) g. digest certificate.BlobHeader.PaymentHeader.AccountId h. digest certificate.BlobHeader.PaymentHeader.BinIndex (4 bytes, unsigned big endian) i. digest certificate.BlobHeader.PaymentHeader.CumulativePayment j. digest certificate.BlobHeader.Signature k. for each relay in certificate.Relays: i. digest relay (4 bytes, unsigned big endian) + +Note that this signature is not included in the hash for obvious reasons. | From 01b7eced0edec852c7b83cfbfb50c87a3e2998fd Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Wed, 11 Dec 2024 08:00:22 -0600 Subject: [PATCH 12/94] Incremental progress. Signed-off-by: Cody Littley --- api/clients/node_client_v2.go | 32 ++++++++++++++++++++++++++++--- disperser/auth/request_signing.go | 6 +++--- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/api/clients/node_client_v2.go b/api/clients/node_client_v2.go index 58452aa94..e45dfc986 100644 --- a/api/clients/node_client_v2.go +++ b/api/clients/node_client_v2.go @@ -2,7 +2,9 @@ package clients import ( "context" + "crypto/ecdsa" "fmt" + "github.com/Layr-Labs/eigenda/disperser/auth" "sync" commonpb "github.com/Layr-Labs/eigenda/api/grpc/common/v2" @@ -16,6 +18,8 @@ type NodeClientV2Config struct { Hostname string Port string UseSecureGrpcFlag bool + // The .pem file containing the private key used to sign StoreChunks() requests. If "" then no signing is done. + PrivateKeyFile string } type NodeClientV2 interface { @@ -27,6 +31,7 @@ type nodeClientV2 struct { config *NodeClientV2Config initOnce sync.Once conn *grpc.ClientConn + key *ecdsa.PrivateKey dispersalClient nodegrpc.DispersalClient } @@ -37,8 +42,19 @@ func NewNodeClientV2(config *NodeClientV2Config) (*nodeClientV2, error) { if config == nil || config.Hostname == "" || config.Port == "" { return nil, fmt.Errorf("invalid config: %v", config) } + + var key *ecdsa.PrivateKey // TODO update flags + if config.PrivateKeyFile != "" { + var err error + key, err = auth.ReadPrivateECDSAKeyFile(config.PrivateKeyFile) + if err != nil { + return nil, fmt.Errorf("failed to read private key file: %v", err) + } + } + return &nodeClientV2{ config: config, + key: key, }, nil } @@ -60,8 +76,7 @@ func (c *nodeClientV2) StoreChunks(ctx context.Context, batch *corev2.Batch) (*c } } - // Call the gRPC method to store chunks - response, err := c.dispersalClient.StoreChunks(ctx, &nodegrpc.StoreChunksRequest{ + request := &nodegrpc.StoreChunksRequest{ Batch: &commonpb.Batch{ Header: &commonpb.BatchHeader{ BatchRoot: batch.BatchHeader.BatchRoot[:], @@ -69,7 +84,18 @@ func (c *nodeClientV2) StoreChunks(ctx context.Context, batch *corev2.Batch) (*c }, BlobCertificates: blobCerts, }, - }) + } + + if c.key != nil { + signature, err := auth.SignStoreChunksRequest(c.key, request) // TODO + if err != nil { + return nil, fmt.Errorf("failed to sign request: %v", err) + } + request.Signature = signature + } + + // Call the gRPC method to store chunks + response, err := c.dispersalClient.StoreChunks(ctx, request) if err != nil { return nil, err } diff --git a/disperser/auth/request_signing.go b/disperser/auth/request_signing.go index b07003095..ec0669662 100644 --- a/disperser/auth/request_signing.go +++ b/disperser/auth/request_signing.go @@ -12,7 +12,7 @@ import ( "hash" ) -// HashStoreChunksRequest hashes the given StoreChunksRequest. TODO document how +// HashStoreChunksRequest hashes the given StoreChunksRequest. func HashStoreChunksRequest(request *grpc.StoreChunksRequest) []byte { hasher := sha3.NewLegacyKeccak256() @@ -74,9 +74,9 @@ func hashUint64(hasher hash.Hash, value uint64) { // SignStoreChunksRequest signs the given StoreChunksRequest with the given private key. Does not // write the signature into the request. func SignStoreChunksRequest(key *ecdsa.PrivateKey, request *grpc.StoreChunksRequest) ([]byte, error) { - hash := HashStoreChunksRequest(request) + requestHash := HashStoreChunksRequest(request) - signature, err := ecdsa.SignASN1(rand.Reader, key, hash) + signature, err := ecdsa.SignASN1(rand.Reader, key, requestHash) if err != nil { return nil, fmt.Errorf("failed to sign request: %w", err) } From e09a77c4b7ddb9c49a598aeb7b9a35695d010d82 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Wed, 11 Dec 2024 10:22:20 -0600 Subject: [PATCH 13/94] Incremental progress. Signed-off-by: Cody Littley --- api/docs/eigenda-protos.html | 8 + api/docs/eigenda-protos.md | 3 +- api/docs/node_v2.html | 8 + api/docs/node_v2.md | 3 +- api/grpc/node/v2/node_v2.pb.go | 106 +++++----- api/proto/node/v2/node_v2.proto | 6 +- disperser/auth/request_signing.go | 36 ++-- .../auth/{key_utils.go => serialization.go} | 2 - ...ey_utils_test.go => serialization_test.go} | 0 node/auth/authenticator.go | 184 ++++++++++++++++++ 10 files changed, 291 insertions(+), 65 deletions(-) rename disperser/auth/{key_utils.go => serialization.go} (96%) rename disperser/auth/{key_utils_test.go => serialization_test.go} (100%) create mode 100644 node/auth/authenticator.go diff --git a/api/docs/eigenda-protos.html b/api/docs/eigenda-protos.html index 29bec7d59..94dc2c02e 100644 --- a/api/docs/eigenda-protos.html +++ b/api/docs/eigenda-protos.html @@ -3495,6 +3495,13 @@

StoreChunksRequest

batch of blobs to store

+ + disperserID + uint32 + +

ID of the disperser that is requesting the storage of the batch.

+ + signature bytes @@ -3522,6 +3529,7 @@

StoreChunksRequest

j. digest certificate.BlobHeader.Signature k. for each relay in certificate.Relays: i. digest relay (4 bytes, unsigned big endian) +4. digest disperserID (4 bytes, unsigned big endian) Note that this signature is not included in the hash for obvious reasons.

diff --git a/api/docs/eigenda-protos.md b/api/docs/eigenda-protos.md index 7c6777c81..fdd5bb20e 100644 --- a/api/docs/eigenda-protos.md +++ b/api/docs/eigenda-protos.md @@ -1482,11 +1482,12 @@ Request that the Node store a batch of chunks. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | batch | [common.v2.Batch](#common-v2-Batch) | | batch of blobs to store | +| disperserID | [uint32](#uint32) | | ID of the disperser that is requesting the storage of the batch. | | signature | [bytes](#bytes) | | Signature using the disperser's ECDSA key over keccak hash of the batch. The purpose of this signature is to prevent hooligans from tricking DA nodes into storing data that they shouldn't be storing. Algorithm for computing the hash is as follows. All integer values are serialized in big-endian order (unsigned). A reference implementation (golang) can be found at https://github.com/Layr-Labs/eigenda/blob/master/disperser/auth/request_signing.go -1. digest batch.BatchHeader.BatchRoot 2. digest batch.BatchHeader.ReferenceBlockNumber (8 bytes, unsigned big endian) 3. for each certificate in batch.BlobCertificates: a. digest certificate.BlobHeader.Version (4 bytes, unsigned big endian) b. for each quorum_number in certificate.BlobHeader.QuorumNumbers: i. digest quorum_number (4 bytes, unsigned big endian) c. digest certificate.BlobHeader.Commitment.Commitment d. digest certificate.BlobHeader.Commitment.LengthCommitment e. digest certificate.BlobHeader.Commitment.LengthProof f. digest certificate.BlobHeader.Commitment.Length (4 bytes, unsigned big endian) g. digest certificate.BlobHeader.PaymentHeader.AccountId h. digest certificate.BlobHeader.PaymentHeader.BinIndex (4 bytes, unsigned big endian) i. digest certificate.BlobHeader.PaymentHeader.CumulativePayment j. digest certificate.BlobHeader.Signature k. for each relay in certificate.Relays: i. digest relay (4 bytes, unsigned big endian) +1. digest batch.BatchHeader.BatchRoot 2. digest batch.BatchHeader.ReferenceBlockNumber (8 bytes, unsigned big endian) 3. for each certificate in batch.BlobCertificates: a. digest certificate.BlobHeader.Version (4 bytes, unsigned big endian) b. for each quorum_number in certificate.BlobHeader.QuorumNumbers: i. digest quorum_number (4 bytes, unsigned big endian) c. digest certificate.BlobHeader.Commitment.Commitment d. digest certificate.BlobHeader.Commitment.LengthCommitment e. digest certificate.BlobHeader.Commitment.LengthProof f. digest certificate.BlobHeader.Commitment.Length (4 bytes, unsigned big endian) g. digest certificate.BlobHeader.PaymentHeader.AccountId h. digest certificate.BlobHeader.PaymentHeader.BinIndex (4 bytes, unsigned big endian) i. digest certificate.BlobHeader.PaymentHeader.CumulativePayment j. digest certificate.BlobHeader.Signature k. for each relay in certificate.Relays: i. digest relay (4 bytes, unsigned big endian) 4. digest disperserID (4 bytes, unsigned big endian) Note that this signature is not included in the hash for obvious reasons. | diff --git a/api/docs/node_v2.html b/api/docs/node_v2.html index 3cbebeaaa..b092e74eb 100644 --- a/api/docs/node_v2.html +++ b/api/docs/node_v2.html @@ -385,6 +385,13 @@

StoreChunksRequest

batch of blobs to store

+ + disperserID + uint32 + +

ID of the disperser that is requesting the storage of the batch.

+ + signature bytes @@ -412,6 +419,7 @@

StoreChunksRequest

j. digest certificate.BlobHeader.Signature k. for each relay in certificate.Relays: i. digest relay (4 bytes, unsigned big endian) +4. digest disperserID (4 bytes, unsigned big endian) Note that this signature is not included in the hash for obvious reasons.

diff --git a/api/docs/node_v2.md b/api/docs/node_v2.md index 2e2d36eac..cc17bbd46 100644 --- a/api/docs/node_v2.md +++ b/api/docs/node_v2.md @@ -109,11 +109,12 @@ Request that the Node store a batch of chunks. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | batch | [common.v2.Batch](#common-v2-Batch) | | batch of blobs to store | +| disperserID | [uint32](#uint32) | | ID of the disperser that is requesting the storage of the batch. | | signature | [bytes](#bytes) | | Signature using the disperser's ECDSA key over keccak hash of the batch. The purpose of this signature is to prevent hooligans from tricking DA nodes into storing data that they shouldn't be storing. Algorithm for computing the hash is as follows. All integer values are serialized in big-endian order (unsigned). A reference implementation (golang) can be found at https://github.com/Layr-Labs/eigenda/blob/master/disperser/auth/request_signing.go -1. digest batch.BatchHeader.BatchRoot 2. digest batch.BatchHeader.ReferenceBlockNumber (8 bytes, unsigned big endian) 3. for each certificate in batch.BlobCertificates: a. digest certificate.BlobHeader.Version (4 bytes, unsigned big endian) b. for each quorum_number in certificate.BlobHeader.QuorumNumbers: i. digest quorum_number (4 bytes, unsigned big endian) c. digest certificate.BlobHeader.Commitment.Commitment d. digest certificate.BlobHeader.Commitment.LengthCommitment e. digest certificate.BlobHeader.Commitment.LengthProof f. digest certificate.BlobHeader.Commitment.Length (4 bytes, unsigned big endian) g. digest certificate.BlobHeader.PaymentHeader.AccountId h. digest certificate.BlobHeader.PaymentHeader.BinIndex (4 bytes, unsigned big endian) i. digest certificate.BlobHeader.PaymentHeader.CumulativePayment j. digest certificate.BlobHeader.Signature k. for each relay in certificate.Relays: i. digest relay (4 bytes, unsigned big endian) +1. digest batch.BatchHeader.BatchRoot 2. digest batch.BatchHeader.ReferenceBlockNumber (8 bytes, unsigned big endian) 3. for each certificate in batch.BlobCertificates: a. digest certificate.BlobHeader.Version (4 bytes, unsigned big endian) b. for each quorum_number in certificate.BlobHeader.QuorumNumbers: i. digest quorum_number (4 bytes, unsigned big endian) c. digest certificate.BlobHeader.Commitment.Commitment d. digest certificate.BlobHeader.Commitment.LengthCommitment e. digest certificate.BlobHeader.Commitment.LengthProof f. digest certificate.BlobHeader.Commitment.Length (4 bytes, unsigned big endian) g. digest certificate.BlobHeader.PaymentHeader.AccountId h. digest certificate.BlobHeader.PaymentHeader.BinIndex (4 bytes, unsigned big endian) i. digest certificate.BlobHeader.PaymentHeader.CumulativePayment j. digest certificate.BlobHeader.Signature k. for each relay in certificate.Relays: i. digest relay (4 bytes, unsigned big endian) 4. digest disperserID (4 bytes, unsigned big endian) Note that this signature is not included in the hash for obvious reasons. | diff --git a/api/grpc/node/v2/node_v2.pb.go b/api/grpc/node/v2/node_v2.pb.go index 7f75acfa9..f97be225a 100644 --- a/api/grpc/node/v2/node_v2.pb.go +++ b/api/grpc/node/v2/node_v2.pb.go @@ -29,6 +29,8 @@ type StoreChunksRequest struct { // batch of blobs to store Batch *v2.Batch `protobuf:"bytes,1,opt,name=batch,proto3" json:"batch,omitempty"` + // ID of the disperser that is requesting the storage of the batch. + DisperserID uint32 `protobuf:"varint,2,opt,name=disperserID,proto3" json:"disperserID,omitempty"` // Signature using the disperser's ECDSA key over keccak hash of the batch. The purpose of this signature // is to prevent hooligans from tricking DA nodes into storing data that they shouldn't be storing. // @@ -52,9 +54,10 @@ type StoreChunksRequest struct { // j. digest certificate.BlobHeader.Signature // k. for each relay in certificate.Relays: // i. digest relay (4 bytes, unsigned big endian) + // 4. digest disperserID (4 bytes, unsigned big endian) // // Note that this signature is not included in the hash for obvious reasons. - Signature []byte `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"` + Signature []byte `protobuf:"bytes,3,opt,name=signature,proto3" json:"signature,omitempty"` } func (x *StoreChunksRequest) Reset() { @@ -96,6 +99,13 @@ func (x *StoreChunksRequest) GetBatch() *v2.Batch { return nil } +func (x *StoreChunksRequest) GetDisperserID() uint32 { + if x != nil { + return x.DisperserID + } + return 0 +} + func (x *StoreChunksRequest) GetSignature() []byte { if x != nil { return x.Signature @@ -381,55 +391,57 @@ var file_node_v2_node_v2_proto_rawDesc = []byte{ 0x0a, 0x15, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x76, 0x32, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x76, 0x32, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32, 0x1a, 0x16, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x76, 0x32, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, - 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x5a, 0x0a, 0x12, 0x53, 0x74, 0x6f, 0x72, + 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x7c, 0x0a, 0x12, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x05, 0x62, 0x61, 0x74, 0x63, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, - 0x05, 0x62, 0x61, 0x74, 0x63, 0x68, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, - 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, - 0x74, 0x75, 0x72, 0x65, 0x22, 0x30, 0x0a, 0x10, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x75, - 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, - 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, - 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x4a, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x43, 0x68, 0x75, - 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, - 0x6f, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x62, 0x6c, - 0x6f, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x71, 0x75, 0x6f, 0x72, 0x75, 0x6d, 0x5f, - 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x71, 0x75, 0x6f, 0x72, 0x75, 0x6d, - 0x49, 0x64, 0x22, 0x28, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, - 0x65, 0x70, 0x6c, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0c, 0x52, 0x06, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x22, 0x11, 0x0a, 0x0f, - 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, - 0x81, 0x01, 0x0a, 0x0d, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x70, 0x6c, - 0x79, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x6d, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x73, 0x65, 0x6d, 0x76, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x72, 0x63, - 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x72, 0x63, 0x68, 0x12, 0x0e, 0x0a, - 0x02, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x6f, 0x73, 0x12, 0x17, 0x0a, - 0x07, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x70, 0x75, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, - 0x6e, 0x75, 0x6d, 0x43, 0x70, 0x75, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x65, 0x6d, 0x5f, 0x62, 0x79, - 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6d, 0x65, 0x6d, 0x42, 0x79, - 0x74, 0x65, 0x73, 0x32, 0x94, 0x01, 0x0a, 0x09, 0x44, 0x69, 0x73, 0x70, 0x65, 0x72, 0x73, 0x61, - 0x6c, 0x12, 0x47, 0x0a, 0x0b, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, - 0x12, 0x1b, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, - 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, - 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x75, - 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3e, 0x0a, 0x08, 0x4e, 0x6f, - 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32, - 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x16, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, - 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x32, 0x8e, 0x01, 0x0a, 0x09, 0x52, - 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x61, 0x6c, 0x12, 0x41, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x43, - 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x12, 0x19, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32, 0x2e, - 0x47, 0x65, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x17, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x68, - 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3e, 0x0a, 0x08, 0x4e, - 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, - 0x32, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x16, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x6f, 0x64, 0x65, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x42, 0x2f, 0x5a, 0x2d, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4c, 0x61, 0x79, 0x72, 0x2d, 0x4c, - 0x61, 0x62, 0x73, 0x2f, 0x65, 0x69, 0x67, 0x65, 0x6e, 0x64, 0x61, 0x2f, 0x61, 0x70, 0x69, 0x2f, - 0x67, 0x72, 0x70, 0x63, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x76, 0x32, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x05, 0x62, 0x61, 0x74, 0x63, 0x68, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x65, 0x72, + 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x64, 0x69, 0x73, + 0x70, 0x65, 0x72, 0x73, 0x65, 0x72, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, + 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x30, 0x0a, 0x10, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x43, + 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, + 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x4a, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x43, + 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, + 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, + 0x62, 0x6c, 0x6f, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x71, 0x75, 0x6f, 0x72, 0x75, + 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x71, 0x75, 0x6f, 0x72, + 0x75, 0x6d, 0x49, 0x64, 0x22, 0x28, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, + 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x06, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x22, 0x11, + 0x0a, 0x0f, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x22, 0x81, 0x01, 0x0a, 0x0d, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, + 0x70, 0x6c, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x6d, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x6d, 0x76, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x61, + 0x72, 0x63, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x72, 0x63, 0x68, 0x12, + 0x0e, 0x0a, 0x02, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x6f, 0x73, 0x12, + 0x17, 0x0a, 0x07, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x70, 0x75, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x43, 0x70, 0x75, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x65, 0x6d, 0x5f, + 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6d, 0x65, 0x6d, + 0x42, 0x79, 0x74, 0x65, 0x73, 0x32, 0x94, 0x01, 0x0a, 0x09, 0x44, 0x69, 0x73, 0x70, 0x65, 0x72, + 0x73, 0x61, 0x6c, 0x12, 0x47, 0x0a, 0x0b, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x75, 0x6e, + 0x6b, 0x73, 0x12, 0x1b, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x6f, + 0x72, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x19, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x43, + 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3e, 0x0a, 0x08, + 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, + 0x76, 0x32, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x6f, 0x64, + 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x32, 0x8e, 0x01, 0x0a, + 0x09, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x61, 0x6c, 0x12, 0x41, 0x0a, 0x09, 0x47, 0x65, + 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x12, 0x19, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, + 0x32, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, + 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3e, 0x0a, + 0x08, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x2e, 0x6e, 0x6f, 0x64, 0x65, + 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x6f, + 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x42, 0x2f, 0x5a, + 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4c, 0x61, 0x79, 0x72, + 0x2d, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x65, 0x69, 0x67, 0x65, 0x6e, 0x64, 0x61, 0x2f, 0x61, 0x70, + 0x69, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x76, 0x32, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/api/proto/node/v2/node_v2.proto b/api/proto/node/v2/node_v2.proto index 9e2acef01..652154f63 100644 --- a/api/proto/node/v2/node_v2.proto +++ b/api/proto/node/v2/node_v2.proto @@ -26,6 +26,9 @@ message StoreChunksRequest { // batch of blobs to store common.v2.Batch batch = 1; + // ID of the disperser that is requesting the storage of the batch. + uint32 disperserID = 2; + // Signature using the disperser's ECDSA key over keccak hash of the batch. The purpose of this signature // is to prevent hooligans from tricking DA nodes into storing data that they shouldn't be storing. // @@ -49,9 +52,10 @@ message StoreChunksRequest { // j. digest certificate.BlobHeader.Signature // k. for each relay in certificate.Relays: // i. digest relay (4 bytes, unsigned big endian) + // 4. digest disperserID (4 bytes, unsigned big endian) // // Note that this signature is not included in the hash for obvious reasons. - bytes signature = 2; + bytes signature = 3; } message StoreChunksReply { diff --git a/disperser/auth/request_signing.go b/disperser/auth/request_signing.go index ec0669662..3d7216c1b 100644 --- a/disperser/auth/request_signing.go +++ b/disperser/auth/request_signing.go @@ -12,6 +12,28 @@ import ( "hash" ) +// TODO test these methods + +// SignStoreChunksRequest signs the given StoreChunksRequest with the given private key. Does not +// write the signature into the request. +func SignStoreChunksRequest(key *ecdsa.PrivateKey, request *grpc.StoreChunksRequest) ([]byte, error) { + requestHash := HashStoreChunksRequest(request) + + signature, err := ecdsa.SignASN1(rand.Reader, key, requestHash) + if err != nil { + return nil, fmt.Errorf("failed to sign request: %w", err) + } + + return signature, nil +} + +// VerifyStoreChunksRequest verifies the given signature of the given StoreChunksRequest with the given +// public key. +func VerifyStoreChunksRequest(key *ecdsa.PublicKey, request *grpc.StoreChunksRequest, signature []byte) bool { + requestHash := HashStoreChunksRequest(request) + return ecdsa.VerifyASN1(key, requestHash, signature) +} + // HashStoreChunksRequest hashes the given StoreChunksRequest. func HashStoreChunksRequest(request *grpc.StoreChunksRequest) []byte { hasher := sha3.NewLegacyKeccak256() @@ -20,6 +42,7 @@ func HashStoreChunksRequest(request *grpc.StoreChunksRequest) []byte { for _, blobCertificate := range request.Batch.BlobCertificates { hashBlobCertificate(hasher, blobCertificate) } + hashUint32(hasher, request.DisperserID) return hasher.Sum(nil) } @@ -70,16 +93,3 @@ func hashUint64(hasher hash.Hash, value uint64) { binary.BigEndian.PutUint64(bytes, value) hasher.Write(bytes) } - -// SignStoreChunksRequest signs the given StoreChunksRequest with the given private key. Does not -// write the signature into the request. -func SignStoreChunksRequest(key *ecdsa.PrivateKey, request *grpc.StoreChunksRequest) ([]byte, error) { - requestHash := HashStoreChunksRequest(request) - - signature, err := ecdsa.SignASN1(rand.Reader, key, requestHash) - if err != nil { - return nil, fmt.Errorf("failed to sign request: %w", err) - } - - return signature, nil -} diff --git a/disperser/auth/key_utils.go b/disperser/auth/serialization.go similarity index 96% rename from disperser/auth/key_utils.go rename to disperser/auth/serialization.go index 4cd6c8d02..43ac84bf6 100644 --- a/disperser/auth/key_utils.go +++ b/disperser/auth/serialization.go @@ -43,8 +43,6 @@ func ReadPublicECDSAKeyFile(publicKeyFile string) (*ecdsa.PublicKey, error) { // ReadPrivateECDSAKeyFile reads a private ECDSA key from a .pem file. func ReadPrivateECDSAKeyFile(privateKeyFile string) (*ecdsa.PrivateKey, error) { - //publicKey, err := ReadPublicECDSAKeyFile(publicKeyFile) - file, err := os.Open(privateKeyFile) if err != nil { return nil, fmt.Errorf("error opening private key file: %w", err) diff --git a/disperser/auth/key_utils_test.go b/disperser/auth/serialization_test.go similarity index 100% rename from disperser/auth/key_utils_test.go rename to disperser/auth/serialization_test.go diff --git a/node/auth/authenticator.go b/node/auth/authenticator.go new file mode 100644 index 000000000..e05c487b2 --- /dev/null +++ b/node/auth/authenticator.go @@ -0,0 +1,184 @@ +package auth + +import ( + "context" + "crypto/ecdsa" + "errors" + "fmt" + grpc "github.com/Layr-Labs/eigenda/api/grpc/node/v2" + "github.com/Layr-Labs/eigenda/core" + dauth "github.com/Layr-Labs/eigenda/disperser/auth" + lru "github.com/hashicorp/golang-lru/v2" + "time" +) + +// RequestAuthenticator authenticates requests to the DA node. This object is thread safe. +type RequestAuthenticator interface { + // AuthenticateStoreChunksRequest authenticates a StoreChunksRequest, returning an error if the request is invalid. + // The origin is the address of the peer that sent the request. This may be used to cache auth results + // in order to save node resources. + AuthenticateStoreChunksRequest( + ctx context.Context, + origin string, + request *grpc.StoreChunksRequest, + now time.Time) error +} + +// keyWithTimeout is a key with that key's expiration time. After a key "expires", it should be reloaded +// from the chain state in case the key has been changed. +type keyWithTimeout struct { + key *ecdsa.PublicKey + expiration time.Time +} + +var _ RequestAuthenticator = &requestAuthenticator{} + +type requestAuthenticator struct { + ics core.IndexedChainState + + // keyCache is used to cache the public keys of dispersers. + keyCache *lru.Cache[uint32, *keyWithTimeout] + + // keyTimeoutDuration is the duration for which a key is cached. After this duration, the key should be + // reloaded from the chain state in case the key has been changed. + keyTimeoutDuration time.Duration + + // authenticatedDispersers is a set of disperser addresses that have been recently authenticated, mapped + // to the time when that cached authentication will expire. + authenticatedDispersers *lru.Cache[string, time.Time] + + // authenticationTimeoutDuration is the duration for which an auth is valid. + // If this is zero, then auth saving is disabled, and each request will be authenticated independently. + authenticationTimeoutDuration time.Duration +} + +// NewRequestAuthenticator creates a new RequestAuthenticator. +func NewRequestAuthenticator( + ctx context.Context, + ics core.IndexedChainState, + keyCacheSize int, + keyTimeoutDuration time.Duration, + authenticationTimeoutDuration time.Duration, + now time.Time) (RequestAuthenticator, error) { + + keyCache, err := lru.New[uint32, *keyWithTimeout](keyCacheSize) + if err != nil { + return nil, fmt.Errorf("failed to create key cache: %w", err) + } + + authenticatedDispersers, err := lru.New[string, time.Time](keyCacheSize) + if err != nil { + return nil, fmt.Errorf("failed to create authenticated dispersers cache: %w", err) + } + + authenticator := &requestAuthenticator{ + ics: ics, + keyCache: keyCache, + keyTimeoutDuration: keyTimeoutDuration, + authenticatedDispersers: authenticatedDispersers, + authenticationTimeoutDuration: authenticationTimeoutDuration, + } + + err = authenticator.preloadCache(ctx, now) + if err != nil { + return nil, fmt.Errorf("failed to preload cache: %w", err) + } + + return authenticator, nil +} + +func (a *requestAuthenticator) preloadCache(ctx context.Context, now time.Time) error { + // TODO (cody-littley): this will need to be updated for decentralized dispersers + key, err := a.getDisperserKey(ctx, now, 0) + if err != nil { + return fmt.Errorf("failed to get operator key: %w", err) + } + + if key == nil { + return errors.New("key is nil") + } + + return nil +} + +func (a *requestAuthenticator) AuthenticateStoreChunksRequest( + ctx context.Context, + origin string, + request *grpc.StoreChunksRequest, + now time.Time) error { + + if a.isAuthenticationStillValid(now, origin) { + // We've recently authenticated this client. Do not authenticate again for a while. + return nil + } + + key, err := a.getDisperserKey(ctx, now, request.DisperserID) + if err != nil { + return fmt.Errorf("failed to get operator key: %w", err) + } + + signature := request.Signature + isValid := dauth.VerifyStoreChunksRequest(key, request, signature) + + if !isValid { + return errors.New("signature verification failed") + } + + a.saveAuthenticationResult(now, origin) + return nil +} + +// getDisperserKey returns the public key of the operator with the given ID, caching the result. +func (a *requestAuthenticator) getDisperserKey( + ctx context.Context, + now time.Time, + disperserID uint32) (*ecdsa.PublicKey, error) { + key, ok := a.keyCache.Get(disperserID) + if ok { + expirationTime := key.expiration + if now.Before(expirationTime) { + return key.key, nil + } + } + + // TODO add logic for fetching key + + //blockNumber, err := a.ics.GetCurrentBlockNumber() + //if err != nil { + // return nil, fmt.Errorf("failed to get current block number: %w", err) + //} + //operators, err := a.ics.GetIndexedOperators(ctx, blockNumber) + //if err != nil { + // return nil, fmt.Errorf("failed to get operators: %w", err) + //} + // + //operator, ok := operators[operatorID] + //if !ok { + // return nil, errors.New("operator not found") + //} + //key = operator.PubkeyG2 + // + //a.keyCache.Add(operatorID, key) + return nil, nil +} + +// saveAuthenticationResult saves the result of an auth. +func (a *requestAuthenticator) saveAuthenticationResult(now time.Time, origin string) { + if a.authenticationTimeoutDuration == 0 { + // Authentication saving is disabled. + return + } + + a.authenticatedDispersers.Add(origin, now.Add(a.authenticationTimeoutDuration)) +} + +// isAuthenticationStillValid returns true if the client at the given address has been authenticated recently. +func (a *requestAuthenticator) isAuthenticationStillValid(now time.Time, address string) bool { + if a.authenticationTimeoutDuration == 0 { + // Authentication saving is disabled. + return false + } + + expiration, ok := a.authenticatedDispersers.Get(address) + return ok && now.Before(expiration) +} From 290c93ccb956251ed36dcbc77b759c0f63f5be19 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Wed, 11 Dec 2024 12:02:21 -0600 Subject: [PATCH 14/94] Incremental progress. Signed-off-by: Cody Littley --- core/chainio.go | 3 +++ core/eth/reader.go | 7 ++++++ core/mock/writer.go | 11 +++++++++ node/auth/authenticator.go | 49 +++++++++++++++++--------------------- 4 files changed, 43 insertions(+), 27 deletions(-) diff --git a/core/chainio.go b/core/chainio.go index b5b587c8d..eefac7cea 100644 --- a/core/chainio.go +++ b/core/chainio.go @@ -126,6 +126,9 @@ type Reader interface { // GetRelayURLs returns the relay URL addresses for all relays. GetRelayURLs(ctx context.Context) (map[uint32]string, error) + + // GetDisperserAddress returns the disperser address with the given ID. + GetDisperserAddress(ctx context.Context, disperserID uint32) (gethcommon.Address, error) } type Writer interface { diff --git a/core/eth/reader.go b/core/eth/reader.go index 8e231f867..1c0cfbb1a 100644 --- a/core/eth/reader.go +++ b/core/eth/reader.go @@ -748,3 +748,10 @@ func (t *Reader) GetRelayURLs(ctx context.Context) (map[uint32]string, error) { return res, nil } + +func (t *Reader) GetDisperserAddress(ctx context.Context, disperserID uint32) (gethcommon.Address, error) { + // TODO(cody-littley/arch): this is just a place holder until we register dispersers on chain + bytes := make([]byte, gethcommon.AddressLength) + address := gethcommon.BytesToAddress(bytes) + return address, nil +} diff --git a/core/mock/writer.go b/core/mock/writer.go index 7e56e64cc..36fb0906a 100644 --- a/core/mock/writer.go +++ b/core/mock/writer.go @@ -266,3 +266,14 @@ func (t *MockWriter) GetRelayURLs(ctx context.Context) (map[uint32]string, error return result.(map[uint32]string), args.Error(1) } + +func (t *MockWriter) GetDisperserAddress(ctx context.Context, disperserID uint32) (gethcommon.Address, error) { + args := t.Called() + result := args.Get(0) + if result == nil { + var zeroValue gethcommon.Address + return zeroValue, args.Error(1) + } + + return result.(gethcommon.Address), args.Error(1) +} diff --git a/node/auth/authenticator.go b/node/auth/authenticator.go index e05c487b2..a36bba74c 100644 --- a/node/auth/authenticator.go +++ b/node/auth/authenticator.go @@ -8,6 +8,7 @@ import ( grpc "github.com/Layr-Labs/eigenda/api/grpc/node/v2" "github.com/Layr-Labs/eigenda/core" dauth "github.com/Layr-Labs/eigenda/disperser/auth" + "github.com/ethereum/go-ethereum/crypto" lru "github.com/hashicorp/golang-lru/v2" "time" ) @@ -34,7 +35,8 @@ type keyWithTimeout struct { var _ RequestAuthenticator = &requestAuthenticator{} type requestAuthenticator struct { - ics core.IndexedChainState + // chainReader is used to read the chain state. + chainReader core.Reader // keyCache is used to cache the public keys of dispersers. keyCache *lru.Cache[uint32, *keyWithTimeout] @@ -55,7 +57,7 @@ type requestAuthenticator struct { // NewRequestAuthenticator creates a new RequestAuthenticator. func NewRequestAuthenticator( ctx context.Context, - ics core.IndexedChainState, + chainReader core.Reader, keyCacheSize int, keyTimeoutDuration time.Duration, authenticationTimeoutDuration time.Duration, @@ -72,7 +74,7 @@ func NewRequestAuthenticator( } authenticator := &requestAuthenticator{ - ics: ics, + chainReader: chainReader, keyCache: keyCache, keyTimeoutDuration: keyTimeoutDuration, authenticatedDispersers: authenticatedDispersers, @@ -89,15 +91,11 @@ func NewRequestAuthenticator( func (a *requestAuthenticator) preloadCache(ctx context.Context, now time.Time) error { // TODO (cody-littley): this will need to be updated for decentralized dispersers - key, err := a.getDisperserKey(ctx, now, 0) + _, err := a.getDisperserKey(ctx, now, 0) if err != nil { return fmt.Errorf("failed to get operator key: %w", err) } - if key == nil { - return errors.New("key is nil") - } - return nil } @@ -141,25 +139,22 @@ func (a *requestAuthenticator) getDisperserKey( } } - // TODO add logic for fetching key - - //blockNumber, err := a.ics.GetCurrentBlockNumber() - //if err != nil { - // return nil, fmt.Errorf("failed to get current block number: %w", err) - //} - //operators, err := a.ics.GetIndexedOperators(ctx, blockNumber) - //if err != nil { - // return nil, fmt.Errorf("failed to get operators: %w", err) - //} - // - //operator, ok := operators[operatorID] - //if !ok { - // return nil, errors.New("operator not found") - //} - //key = operator.PubkeyG2 - // - //a.keyCache.Add(operatorID, key) - return nil, nil + address, err := a.chainReader.GetDisperserAddress(ctx, disperserID) + if err != nil { + return nil, fmt.Errorf("failed to get disperser address: %w", err) + } + + ecdsaKey, err := crypto.UnmarshalPubkey(address.Bytes()) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal public key: %w", err) + } + + a.keyCache.Add(disperserID, &keyWithTimeout{ + key: ecdsaKey, + expiration: now.Add(a.keyTimeoutDuration), + }) + + return ecdsaKey, nil } // saveAuthenticationResult saves the result of an auth. From 5ba4b7b6bd3159b2aa43dfe4581e82be89340376 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Wed, 11 Dec 2024 13:47:44 -0600 Subject: [PATCH 15/94] Incremental progress. Signed-off-by: Cody Littley --- node/cmd/main.go | 16 +++++- node/config.go | 109 +++++++++++++++++++++--------------- node/flags/flags.go | 31 ++++++++++ node/grpc/server_test.go | 15 ++--- node/grpc/server_v2.go | 66 ++++++++++++++++++---- node/grpc/server_v2_test.go | 8 ++- node/node.go | 8 +-- test/integration_test.go | 34 ++++++----- 8 files changed, 200 insertions(+), 87 deletions(-) diff --git a/node/cmd/main.go b/node/cmd/main.go index 0225b33d7..f0740001d 100644 --- a/node/cmd/main.go +++ b/node/cmd/main.go @@ -3,6 +3,8 @@ package main import ( "context" "fmt" + "github.com/Layr-Labs/eigenda/common/geth" + rpccalls "github.com/Layr-Labs/eigensdk-go/metrics/collectors/rpc_calls" "log" "os" "time" @@ -72,8 +74,14 @@ func NodeMain(ctx *cli.Context) error { ratelimiter := ratelimit.NewRateLimiter(reg, globalParams, bucketStore, logger) + rpcCallsCollector := rpccalls.NewCollector(node.AppName, reg) + client, err := geth.NewInstrumentedEthClient(config.EthClientConfig, rpcCallsCollector, logger) + if err != nil { + return fmt.Errorf("cannot create chain.Client: %w", err) + } + // Create the node. - node, err := node.NewNode(reg, config, pubIPProvider, logger) + node, err := node.NewNode(reg, config, pubIPProvider, client, logger) if err != nil { return err } @@ -86,7 +94,11 @@ func NodeMain(ctx *cli.Context) error { // Creates the GRPC server. server := nodegrpc.NewServer(config, node, logger, ratelimiter) - serverV2 := nodegrpc.NewServerV2(config, node, logger, ratelimiter) + serverV2, err := nodegrpc.NewServerV2(context.Background(), config, node, logger, ratelimiter, client) + if err != nil { + return fmt.Errorf("failed to create grpc v2 server: %w", err) + } + err = nodegrpc.RunServers(server, serverV2, config, logger) return err diff --git a/node/config.go b/node/config.go index fd8d53085..64874db71 100644 --- a/node/config.go +++ b/node/config.go @@ -3,7 +3,6 @@ package node import ( "errors" "fmt" - "os" "strconv" "strings" @@ -95,6 +94,20 @@ type Config struct { PprofHttpPort string EnablePprof bool + + // TODO update flags + + // if true then the node will not authenticate StoreChunks requests from dispersers (v2 only) + DisableDispersalAuthentication bool + // the size of the cache for storing public keys of dispersers + DispersalAuthenticationKeyCacheSize int + // the timeout for disperser keys (after which the disperser key is reloaded from the chain) + DisperserKeyTimeout time.Duration + // the timeout for disperser authentication (set to 0 to disable), if enabled then a successful authentication + // of a StoreChunks request causes the node to skip validation for requests coming from the same IP address + // for this duration. Adds risk of disruptive behavior if an attacker is able to send requests from the same IP + // address as a legitimate disperser, but reduces performance overhead of StoreChunks validation. + DispersalAuthenticationTimeout time.Duration } // NewConfig parses the Config from the provided flags or environment variables and @@ -199,50 +212,54 @@ func NewConfig(ctx *cli.Context) (*Config, error) { } return &Config{ - Hostname: ctx.GlobalString(flags.HostnameFlag.Name), - DispersalPort: ctx.GlobalString(flags.DispersalPortFlag.Name), - RetrievalPort: ctx.GlobalString(flags.RetrievalPortFlag.Name), - InternalDispersalPort: internalDispersalFlag, - InternalRetrievalPort: internalRetrievalFlag, - EnableNodeApi: ctx.GlobalBool(flags.EnableNodeApiFlag.Name), - NodeApiPort: ctx.GlobalString(flags.NodeApiPortFlag.Name), - EnableMetrics: ctx.GlobalBool(flags.EnableMetricsFlag.Name), - MetricsPort: ctx.GlobalString(flags.MetricsPortFlag.Name), - OnchainMetricsInterval: ctx.GlobalInt64(flags.OnchainMetricsIntervalFlag.Name), - Timeout: timeout, - RegisterNodeAtStart: registerNodeAtStart, - ExpirationPollIntervalSec: expirationPollIntervalSec, - ReachabilityPollIntervalSec: reachabilityPollIntervalSec, - EnableTestMode: testMode, - OverrideBlockStaleMeasure: ctx.GlobalInt64(flags.OverrideBlockStaleMeasureFlag.Name), - OverrideStoreDurationBlocks: ctx.GlobalInt64(flags.OverrideStoreDurationBlocksFlag.Name), - QuorumIDList: ids, - DbPath: ctx.GlobalString(flags.DbPathFlag.Name), - PrivateBls: privateBls, - EthClientConfig: ethClientConfig, - EncoderConfig: kzg.ReadCLIConfig(ctx), - LoggerConfig: *loggerConfig, - BLSOperatorStateRetrieverAddr: ctx.GlobalString(flags.BlsOperatorStateRetrieverFlag.Name), - EigenDAServiceManagerAddr: ctx.GlobalString(flags.EigenDAServiceManagerFlag.Name), - PubIPProvider: ctx.GlobalString(flags.PubIPProviderFlag.Name), - PubIPCheckInterval: pubIPCheckInterval, - ChurnerUrl: ctx.GlobalString(flags.ChurnerUrlFlag.Name), - DataApiUrl: ctx.GlobalString(flags.DataApiUrlFlag.Name), - NumBatchValidators: ctx.GlobalInt(flags.NumBatchValidatorsFlag.Name), - NumBatchDeserializationWorkers: ctx.GlobalInt(flags.NumBatchDeserializationWorkersFlag.Name), - EnableGnarkBundleEncoding: ctx.Bool(flags.EnableGnarkBundleEncodingFlag.Name), - ClientIPHeader: ctx.GlobalString(flags.ClientIPHeaderFlag.Name), - UseSecureGrpc: ctx.GlobalBoolT(flags.ChurnerUseSecureGRPC.Name), - DisableNodeInfoResources: ctx.GlobalBool(flags.DisableNodeInfoResourcesFlag.Name), - BLSRemoteSignerUrl: ctx.GlobalString(flags.BLSRemoteSignerUrlFlag.Name), - BLSPublicKeyHex: ctx.GlobalString(flags.BLSPublicKeyHexFlag.Name), - BLSKeyPassword: ctx.GlobalString(flags.BlsKeyPasswordFlag.Name), - BLSSignerTLSCertFilePath: ctx.GlobalString(flags.BLSSignerCertFileFlag.Name), - BLSRemoteSignerEnabled: blsRemoteSignerEnabled, - EnableV2: ctx.GlobalBool(flags.EnableV2Flag.Name), - OnchainStateRefreshInterval: ctx.GlobalDuration(flags.OnchainStateRefreshIntervalFlag.Name), - ChunkDownloadTimeout: ctx.GlobalDuration(flags.ChunkDownloadTimeoutFlag.Name), - PprofHttpPort: ctx.GlobalString(flags.PprofHttpPort.Name), - EnablePprof: ctx.GlobalBool(flags.EnablePprof.Name), + Hostname: ctx.GlobalString(flags.HostnameFlag.Name), + DispersalPort: ctx.GlobalString(flags.DispersalPortFlag.Name), + RetrievalPort: ctx.GlobalString(flags.RetrievalPortFlag.Name), + InternalDispersalPort: internalDispersalFlag, + InternalRetrievalPort: internalRetrievalFlag, + EnableNodeApi: ctx.GlobalBool(flags.EnableNodeApiFlag.Name), + NodeApiPort: ctx.GlobalString(flags.NodeApiPortFlag.Name), + EnableMetrics: ctx.GlobalBool(flags.EnableMetricsFlag.Name), + MetricsPort: ctx.GlobalString(flags.MetricsPortFlag.Name), + OnchainMetricsInterval: ctx.GlobalInt64(flags.OnchainMetricsIntervalFlag.Name), + Timeout: timeout, + RegisterNodeAtStart: registerNodeAtStart, + ExpirationPollIntervalSec: expirationPollIntervalSec, + ReachabilityPollIntervalSec: reachabilityPollIntervalSec, + EnableTestMode: testMode, + OverrideBlockStaleMeasure: ctx.GlobalInt64(flags.OverrideBlockStaleMeasureFlag.Name), + OverrideStoreDurationBlocks: ctx.GlobalInt64(flags.OverrideStoreDurationBlocksFlag.Name), + QuorumIDList: ids, + DbPath: ctx.GlobalString(flags.DbPathFlag.Name), + PrivateBls: privateBls, + EthClientConfig: ethClientConfig, + EncoderConfig: kzg.ReadCLIConfig(ctx), + LoggerConfig: *loggerConfig, + BLSOperatorStateRetrieverAddr: ctx.GlobalString(flags.BlsOperatorStateRetrieverFlag.Name), + EigenDAServiceManagerAddr: ctx.GlobalString(flags.EigenDAServiceManagerFlag.Name), + PubIPProvider: ctx.GlobalString(flags.PubIPProviderFlag.Name), + PubIPCheckInterval: pubIPCheckInterval, + ChurnerUrl: ctx.GlobalString(flags.ChurnerUrlFlag.Name), + DataApiUrl: ctx.GlobalString(flags.DataApiUrlFlag.Name), + NumBatchValidators: ctx.GlobalInt(flags.NumBatchValidatorsFlag.Name), + NumBatchDeserializationWorkers: ctx.GlobalInt(flags.NumBatchDeserializationWorkersFlag.Name), + EnableGnarkBundleEncoding: ctx.Bool(flags.EnableGnarkBundleEncodingFlag.Name), + ClientIPHeader: ctx.GlobalString(flags.ClientIPHeaderFlag.Name), + UseSecureGrpc: ctx.GlobalBoolT(flags.ChurnerUseSecureGRPC.Name), + DisableNodeInfoResources: ctx.GlobalBool(flags.DisableNodeInfoResourcesFlag.Name), + BLSRemoteSignerUrl: ctx.GlobalString(flags.BLSRemoteSignerUrlFlag.Name), + BLSPublicKeyHex: ctx.GlobalString(flags.BLSPublicKeyHexFlag.Name), + BLSKeyPassword: ctx.GlobalString(flags.BlsKeyPasswordFlag.Name), + BLSSignerTLSCertFilePath: ctx.GlobalString(flags.BLSSignerCertFileFlag.Name), + BLSRemoteSignerEnabled: blsRemoteSignerEnabled, + EnableV2: ctx.GlobalBool(flags.EnableV2Flag.Name), + OnchainStateRefreshInterval: ctx.GlobalDuration(flags.OnchainStateRefreshIntervalFlag.Name), + ChunkDownloadTimeout: ctx.GlobalDuration(flags.ChunkDownloadTimeoutFlag.Name), + PprofHttpPort: ctx.GlobalString(flags.PprofHttpPort.Name), + EnablePprof: ctx.GlobalBool(flags.EnablePprof.Name), + DisableDispersalAuthentication: ctx.GlobalBool(flags.DisableDispersalAuthenticationFlag.Name), + DispersalAuthenticationKeyCacheSize: ctx.GlobalInt(flags.DispersalAuthenticationKeyCacheSizeFlag.Name), + DisperserKeyTimeout: ctx.GlobalDuration(flags.DisperserKeyTimeoutFlag.Name), + DispersalAuthenticationTimeout: ctx.GlobalDuration(flags.DispersalAuthenticationTimeoutFlag.Name), }, nil } diff --git a/node/flags/flags.go b/node/flags/flags.go index ac0bbbae8..a10ef2dc5 100644 --- a/node/flags/flags.go +++ b/node/flags/flags.go @@ -238,6 +238,33 @@ var ( EnvVar: common.PrefixEnvVar(EnvVarPrefix, "CHUNK_DOWNLOAD_TIMEOUT"), Value: 20 * time.Second, } + DisableDispersalAuthenticationFlag = cli.BoolFlag{ + Name: common.PrefixFlag(FlagPrefix, "disable-dispersal-authentication"), + Usage: "Disable authentication for StoreChunks() calls from the disperser", + Required: false, + EnvVar: common.PrefixEnvVar(EnvVarPrefix, "DISABLE_DISPERSAL_AUTHENTICATION"), + } + DispersalAuthenticationKeyCacheSizeFlag = cli.IntFlag{ + Name: common.PrefixFlag(FlagPrefix, "dispersal-authentication-key-cache-size"), + Usage: "The size of the dispersal authentication key cache", + Required: false, + EnvVar: common.PrefixEnvVar(EnvVarPrefix, "DISPERSAL_AUTHENTICATION_KEY_CACHE_SIZE"), + Value: 1024, + } + DisperserKeyTimeoutFlag = cli.DurationFlag{ + Name: common.PrefixFlag(FlagPrefix, "disperser-key-timeout"), + Usage: "The duration for which a disperser key is cached", + Required: false, + EnvVar: common.PrefixEnvVar(EnvVarPrefix, "DISPERSER_KEY_TIMEOUT"), + Value: 1 * time.Hour, + } + DispersalAuthenticationTimeoutFlag = cli.DurationFlag{ + Name: common.PrefixFlag(FlagPrefix, "dispersal-authentication-timeout"), + Usage: "The duration for which a disperser authentication is valid", + Required: false, + EnvVar: common.PrefixEnvVar(EnvVarPrefix, "DISPERSAL_AUTHENTICATION_TIMEOUT"), + Value: 5 * time.Minute, + } // Test only, DO NOT USE the following flags in production @@ -384,6 +411,10 @@ var optionalFlags = []cli.Flag{ ChunkDownloadTimeoutFlag, PprofHttpPort, EnablePprof, + DisableDispersalAuthenticationFlag, + DispersalAuthenticationKeyCacheSizeFlag, + DisperserKeyTimeoutFlag, + DispersalAuthenticationTimeoutFlag, } func init() { diff --git a/node/grpc/server_test.go b/node/grpc/server_test.go index 825876192..edbc45a7a 100644 --- a/node/grpc/server_test.go +++ b/node/grpc/server_test.go @@ -78,13 +78,14 @@ func makeTestComponents() (encoding.Prover, encoding.Verifier, error) { func makeConfig(t *testing.T) *node.Config { return &node.Config{ - Timeout: 10 * time.Second, - ExpirationPollIntervalSec: 1, - QuorumIDList: []core.QuorumID{0}, - DbPath: t.TempDir(), - ID: opID, - NumBatchValidators: runtime.GOMAXPROCS(0), - EnableV2: false, + Timeout: 10 * time.Second, + ExpirationPollIntervalSec: 1, + QuorumIDList: []core.QuorumID{0}, + DbPath: t.TempDir(), + ID: opID, + NumBatchValidators: runtime.GOMAXPROCS(0), + EnableV2: false, + DisableDispersalAuthentication: true, // TODO re-enable } } diff --git a/node/grpc/server_v2.go b/node/grpc/server_v2.go index 4f46a70d5..1979593b7 100644 --- a/node/grpc/server_v2.go +++ b/node/grpc/server_v2.go @@ -3,8 +3,13 @@ package grpc import ( "context" "encoding/hex" + "errors" "fmt" + coreeth "github.com/Layr-Labs/eigenda/core/eth" + "github.com/Layr-Labs/eigenda/node/auth" + "google.golang.org/grpc/peer" "runtime" + "time" "github.com/Layr-Labs/eigenda/api" pb "github.com/Layr-Labs/eigenda/api/grpc/node/v2" @@ -22,25 +27,53 @@ type ServerV2 struct { pb.UnimplementedDispersalServer pb.UnimplementedRetrievalServer - config *node.Config - node *node.Node - ratelimiter common.RateLimiter - logger logging.Logger + config *node.Config + node *node.Node + ratelimiter common.RateLimiter + authenticator auth.RequestAuthenticator + + logger logging.Logger } // NewServerV2 creates a new Server instance with the provided parameters. func NewServerV2( + ctx context.Context, config *node.Config, node *node.Node, logger logging.Logger, ratelimiter common.RateLimiter, -) *ServerV2 { - return &ServerV2{ - config: config, - node: node, - ratelimiter: ratelimiter, - logger: logger, + client common.EthClient, +) (*ServerV2, error) { + var authenticator auth.RequestAuthenticator + if !config.DisableDispersalAuthentication { + reader, err := coreeth.NewReader( + logger, + client, + config.BLSOperatorStateRetrieverAddr, + config.EigenDAServiceManagerAddr) + if err != nil { + return nil, fmt.Errorf("cannot create eth.Reader: %w", err) + } + + authenticator, err = auth.NewRequestAuthenticator( + ctx, + reader, + config.DispersalAuthenticationKeyCacheSize, + config.DisperserKeyTimeout, + config.DispersalAuthenticationTimeout, + time.Now()) + if err != nil { + return nil, fmt.Errorf("failed to create authenticator: %w", err) + } } + + return &ServerV2{ + config: config, + node: node, + ratelimiter: ratelimiter, + authenticator: authenticator, + logger: logger, + }, nil } func (s *ServerV2) NodeInfo(ctx context.Context, in *pb.NodeInfoRequest) (*pb.NodeInfoReply, error) { @@ -62,6 +95,19 @@ func (s *ServerV2) StoreChunks(ctx context.Context, in *pb.StoreChunksRequest) ( return nil, api.NewErrorInvalidArg("v2 API is disabled") } + if s.authenticator != nil { + disperserPeer, ok := peer.FromContext(ctx) + if !ok { + return nil, errors.New("could not get peer information") + } + disperserAddress := disperserPeer.Addr.String() + + err := s.authenticator.AuthenticateStoreChunksRequest(ctx, disperserAddress, in, time.Now()) + if err != nil { + return nil, fmt.Errorf("failed to authenticate request: %w", err) + } + } + if s.node.StoreV2 == nil { return nil, api.NewErrorInternal("v2 store not initialized") } diff --git a/node/grpc/server_v2_test.go b/node/grpc/server_v2_test.go index 11ef1fce3..8e16ddd8a 100644 --- a/node/grpc/server_v2_test.go +++ b/node/grpc/server_v2_test.go @@ -82,7 +82,13 @@ func newTestComponents(t *testing.T, config *node.Config) *testComponents { RelayClient: atomicRelayClient, } node.BlobVersionParams.Store(v2.NewBlobVersionParameterMap(blobParamsMap)) - server := grpc.NewServerV2(config, node, logger, ratelimiter) + + // The eth client is only utilized for StoreChunks validation, which is disabled in these tests + // TODO write test for StoreChunks validation + var client common.EthClient + + server, err := grpc.NewServerV2(context.Background(), config, node, logger, ratelimiter, client) + require.NoError(t, err) return &testComponents{ server: server, node: node, diff --git a/node/node.go b/node/node.go index 3e36d5f78..0c63d8050 100644 --- a/node/node.go +++ b/node/node.go @@ -42,7 +42,6 @@ import ( v2 "github.com/Layr-Labs/eigenda/core/v2" "github.com/Layr-Labs/eigensdk-go/logging" "github.com/Layr-Labs/eigensdk-go/metrics" - rpccalls "github.com/Layr-Labs/eigensdk-go/metrics/collectors/rpc_calls" "github.com/Layr-Labs/eigensdk-go/nodeapi" "github.com/gammazero/workerpool" @@ -94,6 +93,7 @@ func NewNode( reg *prometheus.Registry, config *Config, pubIPProvider pubip.Provider, + client *geth.InstrumentedEthClient, logger logging.Logger, ) (*Node, error) { // Setup metrics @@ -105,7 +105,6 @@ func NewNode( nodeLogger := logger.With("component", "Node") eigenMetrics := metrics.NewEigenMetrics(AppName, ":"+config.MetricsPort, reg, logger.With("component", "EigenMetrics")) - rpcCallsCollector := rpccalls.NewCollector(AppName, reg) // Make sure config folder exists. err := os.MkdirAll(config.DbPath, os.ModePerm) @@ -113,11 +112,6 @@ func NewNode( return nil, fmt.Errorf("could not create db directory at %s: %w", config.DbPath, err) } - client, err := geth.NewInstrumentedEthClient(config.EthClientConfig, rpcCallsCollector, logger) - if err != nil { - return nil, fmt.Errorf("cannot create chain.Client: %w", err) - } - chainID, err := client.ChainID(context.Background()) if err != nil { return nil, fmt.Errorf("failed to get chainID: %w", err) diff --git a/test/integration_test.go b/test/integration_test.go index b3efdc25f..eae4e1dd2 100644 --- a/test/integration_test.go +++ b/test/integration_test.go @@ -6,6 +6,7 @@ import ( "encoding/hex" "errors" "fmt" + "github.com/stretchr/testify/require" "log" "math" "math/big" @@ -349,19 +350,20 @@ func mustMakeOperators(t *testing.T, cst *coremock.ChainDataMock, logger logging } config := &node.Config{ - Hostname: op.Host, - DispersalPort: op.DispersalPort, - RetrievalPort: op.RetrievalPort, - InternalRetrievalPort: op.RetrievalPort, - InternalDispersalPort: op.DispersalPort, - EnableMetrics: false, - Timeout: 10, - ExpirationPollIntervalSec: 10, - DbPath: dbPath, - LogPath: logPath, - PrivateBls: string(op.KeyPair.GetPubKeyG1().Serialize()), - ID: id, - QuorumIDList: registeredQuorums, + Hostname: op.Host, + DispersalPort: op.DispersalPort, + RetrievalPort: op.RetrievalPort, + InternalRetrievalPort: op.RetrievalPort, + InternalDispersalPort: op.DispersalPort, + EnableMetrics: false, + Timeout: 10, + ExpirationPollIntervalSec: 10, + DbPath: dbPath, + LogPath: logPath, + PrivateBls: string(op.KeyPair.GetPubKeyG1().Serialize()), + ID: id, + QuorumIDList: registeredQuorums, + DisableDispersalAuthentication: true, // TODO re-enable } // creating a new instance of encoder instead of sharing enc because enc is not thread safe @@ -417,8 +419,12 @@ func mustMakeOperators(t *testing.T, cst *coremock.ChainDataMock, logger logging ratelimiter := &commonmock.NoopRatelimiter{} + // TODO enable request validation + var client common.EthClient + serverV1 := nodegrpc.NewServer(config, n, logger, ratelimiter) - serverV2 := nodegrpc.NewServerV2(config, n, logger, ratelimiter) + serverV2, err := nodegrpc.NewServerV2(context.Background(), config, n, logger, ratelimiter, client) + require.NoError(t, err) ops[id] = TestOperator{ Node: n, From e0943716c9608b3105c69eba51bd24dd1ba1fea2 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Wed, 11 Dec 2024 14:03:25 -0600 Subject: [PATCH 16/94] Added test placeholder. Signed-off-by: Cody Littley --- node/auth/authenticator_tests.go | 1 + 1 file changed, 1 insertion(+) create mode 100644 node/auth/authenticator_tests.go diff --git a/node/auth/authenticator_tests.go b/node/auth/authenticator_tests.go new file mode 100644 index 000000000..8832b06d1 --- /dev/null +++ b/node/auth/authenticator_tests.go @@ -0,0 +1 @@ +package auth From db0885a765a0e6b9b0c1c9de736112d961c24a25 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Thu, 12 Dec 2024 09:22:03 -0600 Subject: [PATCH 17/94] shuffle stuff around Signed-off-by: Cody Littley --- api/clients/node_client_v2.go | 3 +- disperser/auth/auth_test.go | 104 -------------------- node/auth/authenticator.go | 3 +- {disperser => node}/auth/request_signing.go | 2 - node/auth/request_signing_tests.go | 8 ++ 5 files changed, 11 insertions(+), 109 deletions(-) delete mode 100644 disperser/auth/auth_test.go rename {disperser => node}/auth/request_signing.go (99%) create mode 100644 node/auth/request_signing_tests.go diff --git a/api/clients/node_client_v2.go b/api/clients/node_client_v2.go index e45dfc986..25d101696 100644 --- a/api/clients/node_client_v2.go +++ b/api/clients/node_client_v2.go @@ -5,6 +5,7 @@ import ( "crypto/ecdsa" "fmt" "github.com/Layr-Labs/eigenda/disperser/auth" + auth2 "github.com/Layr-Labs/eigenda/node/auth" "sync" commonpb "github.com/Layr-Labs/eigenda/api/grpc/common/v2" @@ -87,7 +88,7 @@ func (c *nodeClientV2) StoreChunks(ctx context.Context, batch *corev2.Batch) (*c } if c.key != nil { - signature, err := auth.SignStoreChunksRequest(c.key, request) // TODO + signature, err := auth2.SignStoreChunksRequest(c.key, request) // TODO if err != nil { return nil, fmt.Errorf("failed to sign request: %v", err) } diff --git a/disperser/auth/auth_test.go b/disperser/auth/auth_test.go deleted file mode 100644 index c38b550e1..000000000 --- a/disperser/auth/auth_test.go +++ /dev/null @@ -1,104 +0,0 @@ -package auth - -import ( - "context" - "crypto/tls" - "crypto/x509" - "fmt" - "github.com/Layr-Labs/eigenda/api/grpc/node/v2" - "github.com/stretchr/testify/require" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" - "net" - "os" - "testing" -) - -// The purpose of these tests are to verify that TLS key generation works as expected. -// TODO recreate keys each time a test is run - -var _ v2.DispersalServer = (*mockDispersalServer)(nil) - -type mockDispersalServer struct { - v2.DispersalServer -} - -func (s *mockDispersalServer) StoreChunks(context.Context, *v2.StoreChunksRequest) (*v2.StoreChunksReply, error) { - fmt.Printf("called StoreChunks\n") - return nil, nil -} - -func (s *mockDispersalServer) NodeInfo(context.Context, *v2.NodeInfoRequest) (*v2.NodeInfoReply, error) { - fmt.Printf("called NodeInfo\n") - return nil, nil -} - -func buildClient(t *testing.T) v2.DispersalClient { - addr := "0.0.0.0:50051" - - cert, err := tls.LoadX509KeyPair("./test-disperser.crt", "./test-disperser.key") - require.NoError(t, err) - - // This is what we'd enable if we wanted to verify the server's certificate - //nodeCert, err := os.ReadFile("./test-node.crt") - //require.NoError(t, err) - //certPool := x509.NewCertPool() - //ok := certPool.AppendCertsFromPEM(nodeCert) - //require.True(t, ok) - - creds := credentials.NewTLS(&tls.Config{ - Certificates: []tls.Certificate{cert}, - //RootCAs: certPool, - InsecureSkipVerify: true, - }) - - conn, err := grpc.NewClient(addr, grpc.WithTransportCredentials(creds)) - require.NoError(t, err) - - return v2.NewDispersalClient(conn) -} - -func buildServer(t *testing.T) (v2.DispersalServer, *grpc.Server) { - dispersalServer := &mockDispersalServer{} - - cert, err := tls.LoadX509KeyPair("./test-node.crt", "./test-node.key") - require.NoError(t, err) - - disperserCert, err := os.ReadFile("./test-disperser.crt") - require.NoError(t, err) - certPool := x509.NewCertPool() - ok := certPool.AppendCertsFromPEM(disperserCert) - require.True(t, ok) - - creds := credentials.NewTLS(&tls.Config{ - Certificates: []tls.Certificate{cert}, - ClientCAs: certPool, - ClientAuth: tls.RequireAndVerifyClientCert, - }) - - server := grpc.NewServer(grpc.Creds(creds)) - v2.RegisterDispersalServer(server, dispersalServer) - - addr := "0.0.0.0:50051" - listener, err := net.Listen("tcp", addr) - require.NoError(t, err) - - go func() { - err = server.Serve(listener) - require.NoError(t, err) - }() - - return dispersalServer, server -} - -func TestServerWithTLS(t *testing.T) { - dispersalServer, server := buildServer(t) - defer server.Stop() - require.NotNil(t, dispersalServer) // TODO remove - - client := buildClient(t) - - response, err := client.NodeInfo(context.Background(), &v2.NodeInfoRequest{}) - require.NoError(t, err) - require.NotNil(t, response) -} diff --git a/node/auth/authenticator.go b/node/auth/authenticator.go index a36bba74c..eac071e79 100644 --- a/node/auth/authenticator.go +++ b/node/auth/authenticator.go @@ -7,7 +7,6 @@ import ( "fmt" grpc "github.com/Layr-Labs/eigenda/api/grpc/node/v2" "github.com/Layr-Labs/eigenda/core" - dauth "github.com/Layr-Labs/eigenda/disperser/auth" "github.com/ethereum/go-ethereum/crypto" lru "github.com/hashicorp/golang-lru/v2" "time" @@ -116,7 +115,7 @@ func (a *requestAuthenticator) AuthenticateStoreChunksRequest( } signature := request.Signature - isValid := dauth.VerifyStoreChunksRequest(key, request, signature) + isValid := VerifyStoreChunksRequest(key, request, signature) if !isValid { return errors.New("signature verification failed") diff --git a/disperser/auth/request_signing.go b/node/auth/request_signing.go similarity index 99% rename from disperser/auth/request_signing.go rename to node/auth/request_signing.go index 3d7216c1b..f4b9df3f0 100644 --- a/disperser/auth/request_signing.go +++ b/node/auth/request_signing.go @@ -12,8 +12,6 @@ import ( "hash" ) -// TODO test these methods - // SignStoreChunksRequest signs the given StoreChunksRequest with the given private key. Does not // write the signature into the request. func SignStoreChunksRequest(key *ecdsa.PrivateKey, request *grpc.StoreChunksRequest) ([]byte, error) { diff --git a/node/auth/request_signing_tests.go b/node/auth/request_signing_tests.go new file mode 100644 index 000000000..afd0bd499 --- /dev/null +++ b/node/auth/request_signing_tests.go @@ -0,0 +1,8 @@ +package auth + +import grpc "github.com/Layr-Labs/eigenda/api/grpc/node/v2" + +func randomStoreChunksRequest(random tu.TestRandom) *grpc.StoreChunksRequest { + + return nil +} From b04fcfa46e39af89278afcbdea882cd6752c5773 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Thu, 12 Dec 2024 11:13:18 -0600 Subject: [PATCH 18/94] Unit tests for request signing. Signed-off-by: Cody Littley --- api/proto/node/v2/node_v2.proto | 7 +- common/testutils/random/test_random.go | 42 +++- common/testutils/random/test_random_test.go | 8 +- node/auth/request_signing.go | 3 +- node/auth/request_signing_test.go | 233 ++++++++++++++++++++ node/auth/request_signing_tests.go | 8 - 6 files changed, 283 insertions(+), 18 deletions(-) create mode 100644 node/auth/request_signing_test.go delete mode 100644 node/auth/request_signing_tests.go diff --git a/api/proto/node/v2/node_v2.proto b/api/proto/node/v2/node_v2.proto index 652154f63..4005249f2 100644 --- a/api/proto/node/v2/node_v2.proto +++ b/api/proto/node/v2/node_v2.proto @@ -47,10 +47,11 @@ message StoreChunksRequest { // e. digest certificate.BlobHeader.Commitment.LengthProof // f. digest certificate.BlobHeader.Commitment.Length (4 bytes, unsigned big endian) // g. digest certificate.BlobHeader.PaymentHeader.AccountId - // h. digest certificate.BlobHeader.PaymentHeader.BinIndex (4 bytes, unsigned big endian) + // h. digest certificate.BlobHeader.PaymentHeader.ReservationPeriod (4 bytes, unsigned big endian) // i. digest certificate.BlobHeader.PaymentHeader.CumulativePayment - // j. digest certificate.BlobHeader.Signature - // k. for each relay in certificate.Relays: + // j. digest certificate.BlobHeader.PaymentHeader.Salt (4 bytes, unsigned big endian) + // k. digest certificate.BlobHeader.Signature + // l. for each relay in certificate.Relays: // i. digest relay (4 bytes, unsigned big endian) // 4. digest disperserID (4 bytes, unsigned big endian) // diff --git a/common/testutils/random/test_random.go b/common/testutils/random/test_random.go index ed4699a6d..f8a0ecff1 100644 --- a/common/testutils/random/test_random.go +++ b/common/testutils/random/test_random.go @@ -1,19 +1,31 @@ package random import ( + "crypto/ecdsa" + "crypto/elliptic" "fmt" + "github.com/stretchr/testify/require" + "io" "math/rand" + "testing" "time" ) // TestRandom provides all the functionality of math/rand.Rand, plus additional randomness functionality useful for testing type TestRandom struct { + // The source of randomness *rand.Rand + + // The testing object + t *testing.T + + // The seed used to initialize the random number generator + seed int64 } // NewTestRandom creates a new instance of TestRandom // This method may either be seeded, or not seeded. If no seed is provided, then current unix nano time is used. -func NewTestRandom(fixedSeed ...int64) *TestRandom { +func NewTestRandom(t *testing.T, fixedSeed ...int64) *TestRandom { var seed int64 if len(fixedSeed) == 0 { seed = time.Now().UnixNano() @@ -25,10 +37,18 @@ func NewTestRandom(fixedSeed ...int64) *TestRandom { fmt.Printf("Random seed: %d\n", seed) return &TestRandom{ - rand.New(rand.NewSource(seed)), + Rand: rand.New(rand.NewSource(seed)), + t: t, + seed: seed, } } +// Reset resets the random number generator to the state it was in when it was first created. +// This method is not thread safe with respect to other methods in this struct. +func (r *TestRandom) Reset() { + r.Seed(r.seed) +} + // RandomBytes generates a random byte slice of a given length. func (r *TestRandom) RandomBytes(length int) []byte { bytes := make([]byte, length) @@ -53,3 +73,21 @@ func (r *TestRandom) RandomString(length int) string { } return string(b) } + +var _ io.Reader = &randIOReader{} + +// randIOReader is an io.Reader that reads from a random number generator. +type randIOReader struct { + rand *TestRandom +} + +func (i *randIOReader) Read(p []byte) (n int, err error) { + return i.rand.Read(p) +} + +// RandomECDSA generates a random ECDSA key. FOR TESTING PURPOSES ONLY. DO NOT USE THESE KEYS FOR SECURITY PURPOSES. +func (r *TestRandom) RandomECDSA() (*ecdsa.PublicKey, *ecdsa.PrivateKey) { + key, err := ecdsa.GenerateKey(elliptic.P256(), &randIOReader{r}) + require.NoError(r.t, err) + return &key.PublicKey, key +} diff --git a/common/testutils/random/test_random_test.go b/common/testutils/random/test_random_test.go index 822afd855..228faf5dd 100644 --- a/common/testutils/random/test_random_test.go +++ b/common/testutils/random/test_random_test.go @@ -8,19 +8,19 @@ import ( // Tests that random seeding produces random results, and that consistent seeding produces consistent results func TestSetup(t *testing.T) { - testRandom1 := NewTestRandom() + testRandom1 := NewTestRandom(t) x := testRandom1.Int() - testRandom2 := NewTestRandom() + testRandom2 := NewTestRandom(t) y := testRandom2.Int() assert.NotEqual(t, x, y) seed := rand.Int63() - testRandom3 := NewTestRandom(seed) + testRandom3 := NewTestRandom(t, seed) a := testRandom3.Int() - testRandom4 := NewTestRandom(seed) + testRandom4 := NewTestRandom(t, seed) b := testRandom4.Int() assert.Equal(t, a, b) diff --git a/node/auth/request_signing.go b/node/auth/request_signing.go index f4b9df3f0..7150b212a 100644 --- a/node/auth/request_signing.go +++ b/node/auth/request_signing.go @@ -76,8 +76,9 @@ func hashBlobCommitment(hasher hash.Hash, commitment *commonv1.BlobCommitment) { func hashPaymentHeader(hasher hash.Hash, header *commonv1.PaymentHeader) { hasher.Write([]byte(header.AccountId)) - hashUint32(hasher, header.BinIndex) + hashUint32(hasher, header.ReservationPeriod) hasher.Write(header.CumulativePayment) + hashUint32(hasher, header.Salt) } func hashUint32(hasher hash.Hash, value uint32) { diff --git a/node/auth/request_signing_test.go b/node/auth/request_signing_test.go new file mode 100644 index 000000000..c33333ed1 --- /dev/null +++ b/node/auth/request_signing_test.go @@ -0,0 +1,233 @@ +package auth + +import ( + "github.com/Layr-Labs/eigenda/api/grpc/common" + v2 "github.com/Layr-Labs/eigenda/api/grpc/common/v2" + grpc "github.com/Layr-Labs/eigenda/api/grpc/node/v2" + "github.com/Layr-Labs/eigenda/common/testutils/random" + "github.com/stretchr/testify/require" + "testing" +) + +func randomStoreChunksRequest(rand *random.TestRandom) *grpc.StoreChunksRequest { + certificateCount := rand.Intn(10) + 1 + blobCertificates := make([]*v2.BlobCertificate, certificateCount) + for i := 0; i < certificateCount; i++ { + + relayCount := rand.Intn(10) + 1 + relays := make([]uint32, relayCount) + for j := 0; j < relayCount; j++ { + relays[j] = rand.Uint32() + } + + quorumCount := rand.Intn(10) + 1 + quorumNumbers := make([]uint32, quorumCount) + for j := 0; j < quorumCount; j++ { + quorumNumbers[j] = rand.Uint32() + } + + blobCertificates[i] = &v2.BlobCertificate{ + BlobHeader: &v2.BlobHeader{ + Version: rand.Uint32(), + QuorumNumbers: quorumNumbers, + Commitment: &common.BlobCommitment{ + Commitment: rand.RandomBytes(32), + LengthCommitment: rand.RandomBytes(32), + LengthProof: rand.RandomBytes(32), + Length: rand.Uint32(), + }, + PaymentHeader: &common.PaymentHeader{ + AccountId: rand.RandomString(32), + ReservationPeriod: rand.Uint32(), + CumulativePayment: rand.RandomBytes(32), + Salt: rand.Uint32(), + }, + Signature: rand.RandomBytes(32), + }, + Relays: relays, + } + } + + return &grpc.StoreChunksRequest{ + Batch: &v2.Batch{ + Header: &v2.BatchHeader{ + BatchRoot: rand.RandomBytes(32), + ReferenceBlockNumber: rand.Uint64(), + }, + BlobCertificates: blobCertificates, + }, + DisperserID: rand.Uint32(), + Signature: rand.RandomBytes(32), + } +} + +func TestHashing(t *testing.T) { + rand := random.NewTestRandom(t) + + request := randomStoreChunksRequest(rand) + originalRequestHash := HashStoreChunksRequest(request) + + // modifying the signature should not change the hash + request.Signature = rand.RandomBytes(32) + hash := HashStoreChunksRequest(request) + require.Equal(t, originalRequestHash, hash) + + // modify the disperser id + rand.Reset() + request = randomStoreChunksRequest(rand) + request.DisperserID = request.DisperserID + 1 + hash = HashStoreChunksRequest(request) + require.NotEqual(t, originalRequestHash, hash) + + // remove a blob cert + rand.Reset() + request = randomStoreChunksRequest(rand) + request.Batch.BlobCertificates = request.Batch.BlobCertificates[:len(request.Batch.BlobCertificates)-1] + hash = HashStoreChunksRequest(request) + require.NotEqual(t, originalRequestHash, hash) + + // within a blob cert, modify a relay + rand.Reset() + request = randomStoreChunksRequest(rand) + request.Batch.BlobCertificates[0].Relays[0] = request.Batch.BlobCertificates[0].Relays[0] + 1 + hash = HashStoreChunksRequest(request) + require.NotEqual(t, originalRequestHash, hash) + + // within a blob cert, remove a relay + rand.Reset() + request = randomStoreChunksRequest(rand) + request.Batch.BlobCertificates[0].Relays = + request.Batch.BlobCertificates[0].Relays[:len(request.Batch.BlobCertificates[0].Relays)-1] + hash = HashStoreChunksRequest(request) + require.NotEqual(t, originalRequestHash, hash) + + // within a blob cert, add a relay + rand.Reset() + request = randomStoreChunksRequest(rand) + request.Batch.BlobCertificates[0].Relays = append(request.Batch.BlobCertificates[0].Relays, rand.Uint32()) + hash = HashStoreChunksRequest(request) + require.NotEqual(t, originalRequestHash, hash) + + // within a blob cert, modify a quorum number + rand.Reset() + request = randomStoreChunksRequest(rand) + request.Batch.BlobCertificates[0].BlobHeader.QuorumNumbers[0] = + request.Batch.BlobCertificates[0].BlobHeader.QuorumNumbers[0] + 1 + hash = HashStoreChunksRequest(request) + require.NotEqual(t, originalRequestHash, hash) + + // within a blob cert, remove a quorum number + rand.Reset() + request = randomStoreChunksRequest(rand) + request.Batch.BlobCertificates[0].BlobHeader.QuorumNumbers = + request.Batch.BlobCertificates[0].BlobHeader.QuorumNumbers[:len( + request.Batch.BlobCertificates[0].BlobHeader.QuorumNumbers)-1] + hash = HashStoreChunksRequest(request) + require.NotEqual(t, originalRequestHash, hash) + + // within a blob cert, add a quorum number + rand.Reset() + request = randomStoreChunksRequest(rand) + request.Batch.BlobCertificates[0].BlobHeader.QuorumNumbers = append( + request.Batch.BlobCertificates[0].BlobHeader.QuorumNumbers, rand.Uint32()) + hash = HashStoreChunksRequest(request) + require.NotEqual(t, originalRequestHash, hash) + + // within a blob cert, modify the Commitment.Commitment + rand.Reset() + request = randomStoreChunksRequest(rand) + request.Batch.BlobCertificates[0].BlobHeader.Commitment.Commitment = rand.RandomBytes(32) + hash = HashStoreChunksRequest(request) + require.NotEqual(t, originalRequestHash, hash) + + // within a blob cert, modify the Commitment.LengthCommitment + rand.Reset() + request = randomStoreChunksRequest(rand) + request.Batch.BlobCertificates[0].BlobHeader.Commitment.LengthCommitment = rand.RandomBytes(32) + hash = HashStoreChunksRequest(request) + require.NotEqual(t, originalRequestHash, hash) + + // within a blob cert, modify the Commitment.LengthProof + rand.Reset() + request = randomStoreChunksRequest(rand) + request.Batch.BlobCertificates[0].BlobHeader.Commitment.LengthProof = rand.RandomBytes(32) + hash = HashStoreChunksRequest(request) + require.NotEqual(t, originalRequestHash, hash) + + // within a blob cert, modify the Commitment.Length + rand.Reset() + request = randomStoreChunksRequest(rand) + request.Batch.BlobCertificates[0].BlobHeader.Commitment.Length = rand.Uint32() + hash = HashStoreChunksRequest(request) + require.NotEqual(t, originalRequestHash, hash) + + // within a blob cert, modify the PaymentHeader.AccountId + rand.Reset() + request = randomStoreChunksRequest(rand) + request.Batch.BlobCertificates[0].BlobHeader.PaymentHeader.AccountId = rand.RandomString(32) + hash = HashStoreChunksRequest(request) + require.NotEqual(t, originalRequestHash, hash) + + // within a blob cert, modify the PaymentHeader.ReservationPeriod + rand.Reset() + request = randomStoreChunksRequest(rand) + request.Batch.BlobCertificates[0].BlobHeader.PaymentHeader.ReservationPeriod = rand.Uint32() + hash = HashStoreChunksRequest(request) + require.NotEqual(t, originalRequestHash, hash) + + // within a blob cert, modify the PaymentHeader.CumulativePayment + rand.Reset() + request = randomStoreChunksRequest(rand) + request.Batch.BlobCertificates[0].BlobHeader.PaymentHeader.CumulativePayment = rand.RandomBytes(32) + hash = HashStoreChunksRequest(request) + require.NotEqual(t, originalRequestHash, hash) + + // within a blob cert, modify the PaymentHeader.Salt + rand.Reset() + request = randomStoreChunksRequest(rand) + request.Batch.BlobCertificates[0].BlobHeader.PaymentHeader.Salt = rand.Uint32() + hash = HashStoreChunksRequest(request) + require.NotEqual(t, originalRequestHash, hash) + + // within a blob cert, modify the Signature + rand.Reset() + request = randomStoreChunksRequest(rand) + request.Batch.BlobCertificates[0].BlobHeader.Signature = rand.RandomBytes(32) + hash = HashStoreChunksRequest(request) + require.NotEqual(t, originalRequestHash, hash) +} + +func TestRequestSigning(t *testing.T) { + rand := random.NewTestRandom(t) + + public, private := rand.RandomECDSA() + request := randomStoreChunksRequest(rand) + + signature, err := SignStoreChunksRequest(private, request) + require.NoError(t, err) + + isValid := VerifyStoreChunksRequest(public, request, signature) + require.True(t, isValid) + + // Adding the signature to the request should not change the hash, so it should still be valid + request.Signature = signature + isValid = VerifyStoreChunksRequest(public, request, signature) + require.True(t, isValid) + + // Using a different public key should make the signature invalid + otherPublic, _ := rand.RandomECDSA() + isValid = VerifyStoreChunksRequest(otherPublic, request, signature) + require.False(t, isValid) + + // Changing a byte in the signature should make it invalid + alteredSignature := make([]byte, len(signature)) + copy(alteredSignature, signature) + alteredSignature[0] = alteredSignature[0] + 1 + isValid = VerifyStoreChunksRequest(public, request, alteredSignature) + require.False(t, isValid) + + // Changing a field in the request should make it invalid + request.DisperserID = request.DisperserID + 1 + isValid = VerifyStoreChunksRequest(public, request, signature) + require.False(t, isValid) +} diff --git a/node/auth/request_signing_tests.go b/node/auth/request_signing_tests.go deleted file mode 100644 index afd0bd499..000000000 --- a/node/auth/request_signing_tests.go +++ /dev/null @@ -1,8 +0,0 @@ -package auth - -import grpc "github.com/Layr-Labs/eigenda/api/grpc/node/v2" - -func randomStoreChunksRequest(random tu.TestRandom) *grpc.StoreChunksRequest { - - return nil -} From 29e7e7d16bb88ed9b993e946200fdb2bd5dd9880 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Thu, 12 Dec 2024 11:16:30 -0600 Subject: [PATCH 19/94] Delete stale code. Signed-off-by: Cody Littley --- disperser/auth/generate-ecdsa-keypair.sh | 6 -- disperser/auth/serialization.go | 72 ------------------------ disperser/auth/serialization_test.go | 44 --------------- disperser/auth/test-private.pem | 16 ------ disperser/auth/test-public.pem | 15 ----- 5 files changed, 153 deletions(-) delete mode 100755 disperser/auth/generate-ecdsa-keypair.sh delete mode 100644 disperser/auth/serialization.go delete mode 100644 disperser/auth/serialization_test.go delete mode 100644 disperser/auth/test-private.pem delete mode 100644 disperser/auth/test-public.pem diff --git a/disperser/auth/generate-ecdsa-keypair.sh b/disperser/auth/generate-ecdsa-keypair.sh deleted file mode 100755 index 1a017b655..000000000 --- a/disperser/auth/generate-ecdsa-keypair.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -# This script generates a new ECDSA keypair for the disperser service. - -openssl ecparam -name prime256v1 -genkey -noout -out eigenda-disperser-private.pem -openssl ec -in eigenda-disperser-private.pem -pubout -out eigenda-disperser-public.pem diff --git a/disperser/auth/serialization.go b/disperser/auth/serialization.go deleted file mode 100644 index 43ac84bf6..000000000 --- a/disperser/auth/serialization.go +++ /dev/null @@ -1,72 +0,0 @@ -package auth - -import ( - "crypto/ecdsa" - "crypto/x509" - "encoding/pem" - "fmt" - "io" - "os" -) - -// ReadPublicECDSAKeyFile reads a public ECDSA key from a .pem file. -func ReadPublicECDSAKeyFile(publicKeyFile string) (*ecdsa.PublicKey, error) { - file, err := os.Open(publicKeyFile) - if err != nil { - return nil, fmt.Errorf("error opening public key file: %w", err) - } - defer file.Close() - - bytes, err := io.ReadAll(file) - if err != nil { - return nil, fmt.Errorf("error reading public key file: %w", err) - } - - block, _ := pem.Decode(bytes) - if block == nil { - return nil, fmt.Errorf("no PEM data found in public key file") - } - - if block.Type != "PUBLIC KEY" { - return nil, fmt.Errorf("unexpected block type: %s", block.Type) - } - - genericPublicKey, err := x509.ParsePKIXPublicKey(block.Bytes) - if err != nil { - return nil, fmt.Errorf("error parsing public key: %w", err) - } - - publicKey := genericPublicKey.(*ecdsa.PublicKey) - - return publicKey, nil -} - -// ReadPrivateECDSAKeyFile reads a private ECDSA key from a .pem file. -func ReadPrivateECDSAKeyFile(privateKeyFile string) (*ecdsa.PrivateKey, error) { - file, err := os.Open(privateKeyFile) - if err != nil { - return nil, fmt.Errorf("error opening private key file: %w", err) - } - defer file.Close() - - bytes, err := io.ReadAll(file) - if err != nil { - return nil, fmt.Errorf("error reading private key file: %w", err) - } - - block, _ := pem.Decode(bytes) - if block == nil { - return nil, fmt.Errorf("no PEM data found in private key file") - } - - if block.Type != "EC PRIVATE KEY" { - return nil, fmt.Errorf("unexpected block type: %s", block.Type) - } - - privateKey, err := x509.ParseECPrivateKey(block.Bytes) - if err != nil { - return nil, fmt.Errorf("error parsing private key: %w", err) - } - - return privateKey, nil -} diff --git a/disperser/auth/serialization_test.go b/disperser/auth/serialization_test.go deleted file mode 100644 index 8919f266d..000000000 --- a/disperser/auth/serialization_test.go +++ /dev/null @@ -1,44 +0,0 @@ -package auth - -import ( - "crypto/ecdsa" - tu "github.com/Layr-Labs/eigenda/common/testutils" - "github.com/aws/smithy-go/rand" - "github.com/stretchr/testify/require" - "testing" -) - -func TestReadingKeysFromFile(t *testing.T) { - tu.InitializeRandom() - - publicKey, err := ReadPublicECDSAKeyFile("./test-public.pem") - require.NoError(t, err) - require.NotNil(t, publicKey) - - privateKey, err := ReadPrivateECDSAKeyFile("./test-private.pem") - require.NoError(t, err) - require.NotNil(t, privateKey) - - bytesToSign := tu.RandomBytes(32) - - signature, err := ecdsa.SignASN1(rand.Reader, privateKey, bytesToSign) - require.NoError(t, err) - - isValid := ecdsa.VerifyASN1(publicKey, bytesToSign, signature) - require.True(t, isValid) - - // Change some bytes in the signature, it should be invalid now - signature2 := make([]byte, len(signature)) - copy(signature2, signature) - signature2[0] = signature2[0] + 1 - isValid = ecdsa.VerifyASN1(publicKey, bytesToSign, signature2) - require.False(t, isValid) - - // Change some bytes in the message, it should be invalid now - bytesToSign2 := make([]byte, len(bytesToSign)) - copy(bytesToSign2, bytesToSign) - bytesToSign2[0] = bytesToSign2[0] + 1 - isValid = ecdsa.VerifyASN1(publicKey, bytesToSign2, signature) - require.False(t, isValid) - -} diff --git a/disperser/auth/test-private.pem b/disperser/auth/test-private.pem deleted file mode 100644 index cc5a69b24..000000000 --- a/disperser/auth/test-private.pem +++ /dev/null @@ -1,16 +0,0 @@ - ________________________________________ -/ This key is for testing purposes only, \ -| and is no way intended to be secret. | -| This private key corresponds to the | -\ public key test-public.pem. / - ---------------------------------------- - \ ^__^ - \ (oo)\_______ - (__)\ )\/\ - ||----w | - || || ------BEGIN EC PRIVATE KEY----- -MHcCAQEEIOP8un9zVKyLMwI7JHRfL10ggWEAIJwQZD7hhrkTcGU5oAoGCCqGSM49 -AwEHoUQDQgAERrZQAbrxLSoXbOqKx/3bC/f8YDQ1uDVyJNAys4DH8AQeq59lhqYv -XEuSUlR1qgkRjllsA1wKedW3P2fkH1kbGw== ------END EC PRIVATE KEY----- diff --git a/disperser/auth/test-public.pem b/disperser/auth/test-public.pem deleted file mode 100644 index c7c656729..000000000 --- a/disperser/auth/test-public.pem +++ /dev/null @@ -1,15 +0,0 @@ - ________________________________________ -/ This key is for testing purposes only, \ -| and is no way intended to be secret. | -| This public key corresponds to the | -\ private key test-private.pem. / - ---------------------------------------- - \ ^__^ - \ (oo)\_______ - (__)\ )\/\ - ||----w | - || || ------BEGIN PUBLIC KEY----- -MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERrZQAbrxLSoXbOqKx/3bC/f8YDQ1 -uDVyJNAys4DH8AQeq59lhqYvXEuSUlR1qgkRjllsA1wKedW3P2fkH1kbGw== ------END PUBLIC KEY----- From fb1ebfb2c3ffa4524c96e421ec934c926a12f406 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Thu, 12 Dec 2024 14:22:07 -0600 Subject: [PATCH 20/94] Get things kind of working Signed-off-by: Cody Littley --- api/clients/node_client_v2.go | 19 ++++---- api/docs/eigenda-protos.html | 7 +-- api/docs/eigenda-protos.md | 2 +- api/docs/node_v2.html | 7 +-- api/docs/node_v2.md | 2 +- api/grpc/node/v2/node_v2.pb.go | 7 +-- common/testutils/random/test_random.go | 20 ++++---- common/testutils/test_utils.go | 6 +-- core/mock/writer.go | 2 +- node/auth/authenticator.go | 31 +++++------- node/auth/authenticator_test.go | 66 ++++++++++++++++++++++++++ node/auth/authenticator_tests.go | 1 - node/auth/request_signing.go | 20 ++++++-- node/auth/request_signing_test.go | 58 +++++++++++----------- 14 files changed, 163 insertions(+), 85 deletions(-) create mode 100644 node/auth/authenticator_test.go delete mode 100644 node/auth/authenticator_tests.go diff --git a/api/clients/node_client_v2.go b/api/clients/node_client_v2.go index 25d101696..2bc358833 100644 --- a/api/clients/node_client_v2.go +++ b/api/clients/node_client_v2.go @@ -4,8 +4,7 @@ import ( "context" "crypto/ecdsa" "fmt" - "github.com/Layr-Labs/eigenda/disperser/auth" - auth2 "github.com/Layr-Labs/eigenda/node/auth" + "github.com/Layr-Labs/eigenda/node/auth" "sync" commonpb "github.com/Layr-Labs/eigenda/api/grpc/common/v2" @@ -45,13 +44,13 @@ func NewNodeClientV2(config *NodeClientV2Config) (*nodeClientV2, error) { } var key *ecdsa.PrivateKey // TODO update flags - if config.PrivateKeyFile != "" { - var err error - key, err = auth.ReadPrivateECDSAKeyFile(config.PrivateKeyFile) - if err != nil { - return nil, fmt.Errorf("failed to read private key file: %v", err) - } - } + //if config.PrivateKeyFile != "" { + // var err error + // key, err = auth.ReadPrivateECDSAKeyFile(config.PrivateKeyFile) + // if err != nil { + // return nil, fmt.Errorf("failed to read private key file: %v", err) + // } + //} return &nodeClientV2{ config: config, @@ -88,7 +87,7 @@ func (c *nodeClientV2) StoreChunks(ctx context.Context, batch *corev2.Batch) (*c } if c.key != nil { - signature, err := auth2.SignStoreChunksRequest(c.key, request) // TODO + signature, err := auth.SignStoreChunksRequest(c.key, request) // TODO if err != nil { return nil, fmt.Errorf("failed to sign request: %v", err) } diff --git a/api/docs/eigenda-protos.html b/api/docs/eigenda-protos.html index 161e5cb9d..51b2d4ee1 100644 --- a/api/docs/eigenda-protos.html +++ b/api/docs/eigenda-protos.html @@ -3524,10 +3524,11 @@

StoreChunksRequest

e. digest certificate.BlobHeader.Commitment.LengthProof f. digest certificate.BlobHeader.Commitment.Length (4 bytes, unsigned big endian) g. digest certificate.BlobHeader.PaymentHeader.AccountId - h. digest certificate.BlobHeader.PaymentHeader.BinIndex (4 bytes, unsigned big endian) + h. digest certificate.BlobHeader.PaymentHeader.ReservationPeriod (4 bytes, unsigned big endian) i. digest certificate.BlobHeader.PaymentHeader.CumulativePayment - j. digest certificate.BlobHeader.Signature - k. for each relay in certificate.Relays: + j. digest certificate.BlobHeader.PaymentHeader.Salt (4 bytes, unsigned big endian) + k. digest certificate.BlobHeader.Signature + l. for each relay in certificate.Relays: i. digest relay (4 bytes, unsigned big endian) 4. digest disperserID (4 bytes, unsigned big endian) diff --git a/api/docs/eigenda-protos.md b/api/docs/eigenda-protos.md index 3c78a0c34..f1231c974 100644 --- a/api/docs/eigenda-protos.md +++ b/api/docs/eigenda-protos.md @@ -1486,7 +1486,7 @@ Request that the Node store a batch of chunks. Algorithm for computing the hash is as follows. All integer values are serialized in big-endian order (unsigned). A reference implementation (golang) can be found at https://github.com/Layr-Labs/eigenda/blob/master/disperser/auth/request_signing.go -1. digest batch.BatchHeader.BatchRoot 2. digest batch.BatchHeader.ReferenceBlockNumber (8 bytes, unsigned big endian) 3. for each certificate in batch.BlobCertificates: a. digest certificate.BlobHeader.Version (4 bytes, unsigned big endian) b. for each quorum_number in certificate.BlobHeader.QuorumNumbers: i. digest quorum_number (4 bytes, unsigned big endian) c. digest certificate.BlobHeader.Commitment.Commitment d. digest certificate.BlobHeader.Commitment.LengthCommitment e. digest certificate.BlobHeader.Commitment.LengthProof f. digest certificate.BlobHeader.Commitment.Length (4 bytes, unsigned big endian) g. digest certificate.BlobHeader.PaymentHeader.AccountId h. digest certificate.BlobHeader.PaymentHeader.BinIndex (4 bytes, unsigned big endian) i. digest certificate.BlobHeader.PaymentHeader.CumulativePayment j. digest certificate.BlobHeader.Signature k. for each relay in certificate.Relays: i. digest relay (4 bytes, unsigned big endian) 4. digest disperserID (4 bytes, unsigned big endian) +1. digest batch.BatchHeader.BatchRoot 2. digest batch.BatchHeader.ReferenceBlockNumber (8 bytes, unsigned big endian) 3. for each certificate in batch.BlobCertificates: a. digest certificate.BlobHeader.Version (4 bytes, unsigned big endian) b. for each quorum_number in certificate.BlobHeader.QuorumNumbers: i. digest quorum_number (4 bytes, unsigned big endian) c. digest certificate.BlobHeader.Commitment.Commitment d. digest certificate.BlobHeader.Commitment.LengthCommitment e. digest certificate.BlobHeader.Commitment.LengthProof f. digest certificate.BlobHeader.Commitment.Length (4 bytes, unsigned big endian) g. digest certificate.BlobHeader.PaymentHeader.AccountId h. digest certificate.BlobHeader.PaymentHeader.ReservationPeriod (4 bytes, unsigned big endian) i. digest certificate.BlobHeader.PaymentHeader.CumulativePayment j. digest certificate.BlobHeader.PaymentHeader.Salt (4 bytes, unsigned big endian) k. digest certificate.BlobHeader.Signature l. for each relay in certificate.Relays: i. digest relay (4 bytes, unsigned big endian) 4. digest disperserID (4 bytes, unsigned big endian) Note that this signature is not included in the hash for obvious reasons. | diff --git a/api/docs/node_v2.html b/api/docs/node_v2.html index b092e74eb..ad54d473b 100644 --- a/api/docs/node_v2.html +++ b/api/docs/node_v2.html @@ -414,10 +414,11 @@

StoreChunksRequest

e. digest certificate.BlobHeader.Commitment.LengthProof f. digest certificate.BlobHeader.Commitment.Length (4 bytes, unsigned big endian) g. digest certificate.BlobHeader.PaymentHeader.AccountId - h. digest certificate.BlobHeader.PaymentHeader.BinIndex (4 bytes, unsigned big endian) + h. digest certificate.BlobHeader.PaymentHeader.ReservationPeriod (4 bytes, unsigned big endian) i. digest certificate.BlobHeader.PaymentHeader.CumulativePayment - j. digest certificate.BlobHeader.Signature - k. for each relay in certificate.Relays: + j. digest certificate.BlobHeader.PaymentHeader.Salt (4 bytes, unsigned big endian) + k. digest certificate.BlobHeader.Signature + l. for each relay in certificate.Relays: i. digest relay (4 bytes, unsigned big endian) 4. digest disperserID (4 bytes, unsigned big endian) diff --git a/api/docs/node_v2.md b/api/docs/node_v2.md index cc17bbd46..46d43c7d4 100644 --- a/api/docs/node_v2.md +++ b/api/docs/node_v2.md @@ -114,7 +114,7 @@ Request that the Node store a batch of chunks. Algorithm for computing the hash is as follows. All integer values are serialized in big-endian order (unsigned). A reference implementation (golang) can be found at https://github.com/Layr-Labs/eigenda/blob/master/disperser/auth/request_signing.go -1. digest batch.BatchHeader.BatchRoot 2. digest batch.BatchHeader.ReferenceBlockNumber (8 bytes, unsigned big endian) 3. for each certificate in batch.BlobCertificates: a. digest certificate.BlobHeader.Version (4 bytes, unsigned big endian) b. for each quorum_number in certificate.BlobHeader.QuorumNumbers: i. digest quorum_number (4 bytes, unsigned big endian) c. digest certificate.BlobHeader.Commitment.Commitment d. digest certificate.BlobHeader.Commitment.LengthCommitment e. digest certificate.BlobHeader.Commitment.LengthProof f. digest certificate.BlobHeader.Commitment.Length (4 bytes, unsigned big endian) g. digest certificate.BlobHeader.PaymentHeader.AccountId h. digest certificate.BlobHeader.PaymentHeader.BinIndex (4 bytes, unsigned big endian) i. digest certificate.BlobHeader.PaymentHeader.CumulativePayment j. digest certificate.BlobHeader.Signature k. for each relay in certificate.Relays: i. digest relay (4 bytes, unsigned big endian) 4. digest disperserID (4 bytes, unsigned big endian) +1. digest batch.BatchHeader.BatchRoot 2. digest batch.BatchHeader.ReferenceBlockNumber (8 bytes, unsigned big endian) 3. for each certificate in batch.BlobCertificates: a. digest certificate.BlobHeader.Version (4 bytes, unsigned big endian) b. for each quorum_number in certificate.BlobHeader.QuorumNumbers: i. digest quorum_number (4 bytes, unsigned big endian) c. digest certificate.BlobHeader.Commitment.Commitment d. digest certificate.BlobHeader.Commitment.LengthCommitment e. digest certificate.BlobHeader.Commitment.LengthProof f. digest certificate.BlobHeader.Commitment.Length (4 bytes, unsigned big endian) g. digest certificate.BlobHeader.PaymentHeader.AccountId h. digest certificate.BlobHeader.PaymentHeader.ReservationPeriod (4 bytes, unsigned big endian) i. digest certificate.BlobHeader.PaymentHeader.CumulativePayment j. digest certificate.BlobHeader.PaymentHeader.Salt (4 bytes, unsigned big endian) k. digest certificate.BlobHeader.Signature l. for each relay in certificate.Relays: i. digest relay (4 bytes, unsigned big endian) 4. digest disperserID (4 bytes, unsigned big endian) Note that this signature is not included in the hash for obvious reasons. | diff --git a/api/grpc/node/v2/node_v2.pb.go b/api/grpc/node/v2/node_v2.pb.go index f97be225a..60f69facd 100644 --- a/api/grpc/node/v2/node_v2.pb.go +++ b/api/grpc/node/v2/node_v2.pb.go @@ -49,10 +49,11 @@ type StoreChunksRequest struct { // e. digest certificate.BlobHeader.Commitment.LengthProof // f. digest certificate.BlobHeader.Commitment.Length (4 bytes, unsigned big endian) // g. digest certificate.BlobHeader.PaymentHeader.AccountId - // h. digest certificate.BlobHeader.PaymentHeader.BinIndex (4 bytes, unsigned big endian) + // h. digest certificate.BlobHeader.PaymentHeader.ReservationPeriod (4 bytes, unsigned big endian) // i. digest certificate.BlobHeader.PaymentHeader.CumulativePayment - // j. digest certificate.BlobHeader.Signature - // k. for each relay in certificate.Relays: + // j. digest certificate.BlobHeader.PaymentHeader.Salt (4 bytes, unsigned big endian) + // k. digest certificate.BlobHeader.Signature + // l. for each relay in certificate.Relays: // i. digest relay (4 bytes, unsigned big endian) // 4. digest disperserID (4 bytes, unsigned big endian) // diff --git a/common/testutils/random/test_random.go b/common/testutils/random/test_random.go index f8a0ecff1..b9a9933da 100644 --- a/common/testutils/random/test_random.go +++ b/common/testutils/random/test_random.go @@ -2,8 +2,8 @@ package random import ( "crypto/ecdsa" - "crypto/elliptic" "fmt" + "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/require" "io" "math/rand" @@ -49,8 +49,8 @@ func (r *TestRandom) Reset() { r.Seed(r.seed) } -// RandomBytes generates a random byte slice of a given length. -func (r *TestRandom) RandomBytes(length int) []byte { +// Bytes generates a random byte slice of a given length. +func (r *TestRandom) Bytes(length int) []byte { bytes := make([]byte, length) _, err := r.Read(bytes) if err != nil { @@ -59,13 +59,13 @@ func (r *TestRandom) RandomBytes(length int) []byte { return bytes } -// RandomTime generates a random time. -func (r *TestRandom) RandomTime() time.Time { +// Time generates a random time. +func (r *TestRandom) Time() time.Time { return time.Unix(r.Int63(), r.Int63()) } -// RandomString generates a random string out of printable ASCII characters. -func (r *TestRandom) RandomString(length int) string { +// String generates a random string out of printable ASCII characters. +func (r *TestRandom) String(length int) string { const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" b := make([]byte, length) for i := range b { @@ -85,9 +85,9 @@ func (i *randIOReader) Read(p []byte) (n int, err error) { return i.rand.Read(p) } -// RandomECDSA generates a random ECDSA key. FOR TESTING PURPOSES ONLY. DO NOT USE THESE KEYS FOR SECURITY PURPOSES. -func (r *TestRandom) RandomECDSA() (*ecdsa.PublicKey, *ecdsa.PrivateKey) { - key, err := ecdsa.GenerateKey(elliptic.P256(), &randIOReader{r}) +// ECDSA generates a random ECDSA key. FOR TESTING PURPOSES ONLY. DO NOT USE THESE KEYS FOR SECURITY PURPOSES. +func (r *TestRandom) ECDSA() (*ecdsa.PublicKey, *ecdsa.PrivateKey) { + key, err := ecdsa.GenerateKey(crypto.S256(), &randIOReader{r}) require.NoError(r.t, err) return &key.PublicKey, key } diff --git a/common/testutils/test_utils.go b/common/testutils/test_utils.go index ad06ecabb..38daa774b 100644 --- a/common/testutils/test_utils.go +++ b/common/testutils/test_utils.go @@ -86,7 +86,7 @@ func ExecuteWithTimeout(f func(), duration time.Duration, debugInfo ...any) { } // RandomBytes generates a random byte slice of a given length. -// Deprecated: use TestRandom.RandomBytes instead +// Deprecated: use TestRandom.Bytes instead func RandomBytes(length int) []byte { bytes := make([]byte, length) _, err := rand.Read(bytes) @@ -97,13 +97,13 @@ func RandomBytes(length int) []byte { } // RandomTime generates a random time. -// Deprecated: use TestRandom.RandomTime instead +// Deprecated: use TestRandom.Time instead func RandomTime() time.Time { return time.Unix(int64(rand.Int31()), 0) } // RandomString generates a random string out of printable ASCII characters. -// Deprecated: use TestRandom.RandomString instead +// Deprecated: use TestRandom.String instead func RandomString(length int) string { const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" b := make([]byte, length) diff --git a/core/mock/writer.go b/core/mock/writer.go index f367ae2eb..5c8340ef7 100644 --- a/core/mock/writer.go +++ b/core/mock/writer.go @@ -286,7 +286,7 @@ func (t *MockWriter) GetRelayURLs(ctx context.Context) (map[uint32]string, error } func (t *MockWriter) GetDisperserAddress(ctx context.Context, disperserID uint32) (gethcommon.Address, error) { - args := t.Called() + args := t.Called(disperserID) result := args.Get(0) if result == nil { var zeroValue gethcommon.Address diff --git a/node/auth/authenticator.go b/node/auth/authenticator.go index eac071e79..ce7181be2 100644 --- a/node/auth/authenticator.go +++ b/node/auth/authenticator.go @@ -2,17 +2,18 @@ package auth import ( "context" - "crypto/ecdsa" - "errors" "fmt" grpc "github.com/Layr-Labs/eigenda/api/grpc/node/v2" "github.com/Layr-Labs/eigenda/core" - "github.com/ethereum/go-ethereum/crypto" + gethcommon "github.com/ethereum/go-ethereum/common" lru "github.com/hashicorp/golang-lru/v2" "time" ) // RequestAuthenticator authenticates requests to the DA node. This object is thread safe. +// +// This class has largely been future-proofed for decentralized dispersers, with the exception of the +// preloadCache method, which will need to be updated to handle decentralized dispersers. type RequestAuthenticator interface { // AuthenticateStoreChunksRequest authenticates a StoreChunksRequest, returning an error if the request is invalid. // The origin is the address of the peer that sent the request. This may be used to cache auth results @@ -27,7 +28,7 @@ type RequestAuthenticator interface { // keyWithTimeout is a key with that key's expiration time. After a key "expires", it should be reloaded // from the chain state in case the key has been changed. type keyWithTimeout struct { - key *ecdsa.PublicKey + key gethcommon.Address expiration time.Time } @@ -89,7 +90,7 @@ func NewRequestAuthenticator( } func (a *requestAuthenticator) preloadCache(ctx context.Context, now time.Time) error { - // TODO (cody-littley): this will need to be updated for decentralized dispersers + // this will need to be updated for decentralized dispersers _, err := a.getDisperserKey(ctx, now, 0) if err != nil { return fmt.Errorf("failed to get operator key: %w", err) @@ -115,10 +116,9 @@ func (a *requestAuthenticator) AuthenticateStoreChunksRequest( } signature := request.Signature - isValid := VerifyStoreChunksRequest(key, request, signature) - - if !isValid { - return errors.New("signature verification failed") + err = VerifyStoreChunksRequest(*key, request, signature) + if err != nil { + return fmt.Errorf("failed to verify request: %w", err) } a.saveAuthenticationResult(now, origin) @@ -129,12 +129,12 @@ func (a *requestAuthenticator) AuthenticateStoreChunksRequest( func (a *requestAuthenticator) getDisperserKey( ctx context.Context, now time.Time, - disperserID uint32) (*ecdsa.PublicKey, error) { + disperserID uint32) (*gethcommon.Address, error) { key, ok := a.keyCache.Get(disperserID) if ok { expirationTime := key.expiration if now.Before(expirationTime) { - return key.key, nil + return &key.key, nil } } @@ -143,17 +143,12 @@ func (a *requestAuthenticator) getDisperserKey( return nil, fmt.Errorf("failed to get disperser address: %w", err) } - ecdsaKey, err := crypto.UnmarshalPubkey(address.Bytes()) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal public key: %w", err) - } - a.keyCache.Add(disperserID, &keyWithTimeout{ - key: ecdsaKey, + key: address, expiration: now.Add(a.keyTimeoutDuration), }) - return ecdsaKey, nil + return &address, nil } // saveAuthenticationResult saves the result of an auth. diff --git a/node/auth/authenticator_test.go b/node/auth/authenticator_test.go new file mode 100644 index 000000000..b36a671c5 --- /dev/null +++ b/node/auth/authenticator_test.go @@ -0,0 +1,66 @@ +package auth + +import ( + "context" + "github.com/Layr-Labs/eigenda/common/testutils/random" + wmock "github.com/Layr-Labs/eigenda/core/mock" + "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/require" + "testing" + "time" +) + +// TODO: +// - test good request +// - test bad request +// - test request from disperser that doesn't exist +// - test auth caching +// - test key caching +// - test cache sizes + +//// Verify that public key can be converted to an eth address and back +//func TestPubKeyRoundTrip(t *testing.T) { +// rand := random.NewTestRandom(t) +// +// publicKey, _ := rand.ECDSA() +// +// ethAddress := crypto.PubkeyToAddress(*publicKey) +// +// publicKey2, err := crypto.UnmarshalPubkey(ethAddress.Bytes()) +// require.NoError(t, err) +// +// require.Equal(t, publicKey, publicKey2) +//} + +func TestValidRequest(t *testing.T) { + rand := random.NewTestRandom(t) + + start := rand.Time() + + publicKey, privateKey := rand.ECDSA() + disperserAddress := crypto.PubkeyToAddress(*publicKey) + + //disperserAddressBytes := crypto.FromECDSAPub(publicKey) + //disperserAddress := gethcommon.BytesToAddress(disperserAddressBytes) + + chainReader := wmock.MockWriter{} + chainReader.Mock.On("GetDisperserAddress", uint32(0)).Return(disperserAddress, nil) + + authenticator, err := NewRequestAuthenticator( + context.Background(), + &chainReader, + 10, + time.Minute, + time.Minute, + start) + require.NoError(t, err) + + request := randomStoreChunksRequest(rand) + request.DisperserID = 0 + signature, err := SignStoreChunksRequest(privateKey, request) + require.NoError(t, err) + request.Signature = signature + + err = authenticator.AuthenticateStoreChunksRequest(context.Background(), "localhost", request, start) + require.NoError(t, err) +} diff --git a/node/auth/authenticator_tests.go b/node/auth/authenticator_tests.go deleted file mode 100644 index 8832b06d1..000000000 --- a/node/auth/authenticator_tests.go +++ /dev/null @@ -1 +0,0 @@ -package auth diff --git a/node/auth/request_signing.go b/node/auth/request_signing.go index 7150b212a..dd7e24cd3 100644 --- a/node/auth/request_signing.go +++ b/node/auth/request_signing.go @@ -2,12 +2,13 @@ package auth import ( "crypto/ecdsa" - "crypto/rand" "encoding/binary" "fmt" commonv1 "github.com/Layr-Labs/eigenda/api/grpc/common" common "github.com/Layr-Labs/eigenda/api/grpc/common/v2" grpc "github.com/Layr-Labs/eigenda/api/grpc/node/v2" + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" "golang.org/x/crypto/sha3" "hash" ) @@ -17,7 +18,7 @@ import ( func SignStoreChunksRequest(key *ecdsa.PrivateKey, request *grpc.StoreChunksRequest) ([]byte, error) { requestHash := HashStoreChunksRequest(request) - signature, err := ecdsa.SignASN1(rand.Reader, key, requestHash) + signature, err := crypto.Sign(requestHash, key) if err != nil { return nil, fmt.Errorf("failed to sign request: %w", err) } @@ -27,9 +28,20 @@ func SignStoreChunksRequest(key *ecdsa.PrivateKey, request *grpc.StoreChunksRequ // VerifyStoreChunksRequest verifies the given signature of the given StoreChunksRequest with the given // public key. -func VerifyStoreChunksRequest(key *ecdsa.PublicKey, request *grpc.StoreChunksRequest, signature []byte) bool { +func VerifyStoreChunksRequest(key gethcommon.Address, request *grpc.StoreChunksRequest, signature []byte) error { requestHash := HashStoreChunksRequest(request) - return ecdsa.VerifyASN1(key, requestHash, signature) + + signingPublicKey, err := crypto.SigToPub(requestHash, signature) + if err != nil { + return fmt.Errorf("failed to recover public key from signature: %w", err) + } + + signingAddress := crypto.PubkeyToAddress(*signingPublicKey) + + if key.Cmp(signingAddress) != 0 { + return fmt.Errorf("signature doesn't match with provided public key") + } + return nil } // HashStoreChunksRequest hashes the given StoreChunksRequest. diff --git a/node/auth/request_signing_test.go b/node/auth/request_signing_test.go index c33333ed1..1d6a6e622 100644 --- a/node/auth/request_signing_test.go +++ b/node/auth/request_signing_test.go @@ -5,6 +5,7 @@ import ( v2 "github.com/Layr-Labs/eigenda/api/grpc/common/v2" grpc "github.com/Layr-Labs/eigenda/api/grpc/node/v2" "github.com/Layr-Labs/eigenda/common/testutils/random" + "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/require" "testing" ) @@ -31,18 +32,18 @@ func randomStoreChunksRequest(rand *random.TestRandom) *grpc.StoreChunksRequest Version: rand.Uint32(), QuorumNumbers: quorumNumbers, Commitment: &common.BlobCommitment{ - Commitment: rand.RandomBytes(32), - LengthCommitment: rand.RandomBytes(32), - LengthProof: rand.RandomBytes(32), + Commitment: rand.Bytes(32), + LengthCommitment: rand.Bytes(32), + LengthProof: rand.Bytes(32), Length: rand.Uint32(), }, PaymentHeader: &common.PaymentHeader{ - AccountId: rand.RandomString(32), + AccountId: rand.String(32), ReservationPeriod: rand.Uint32(), - CumulativePayment: rand.RandomBytes(32), + CumulativePayment: rand.Bytes(32), Salt: rand.Uint32(), }, - Signature: rand.RandomBytes(32), + Signature: rand.Bytes(32), }, Relays: relays, } @@ -51,13 +52,13 @@ func randomStoreChunksRequest(rand *random.TestRandom) *grpc.StoreChunksRequest return &grpc.StoreChunksRequest{ Batch: &v2.Batch{ Header: &v2.BatchHeader{ - BatchRoot: rand.RandomBytes(32), + BatchRoot: rand.Bytes(32), ReferenceBlockNumber: rand.Uint64(), }, BlobCertificates: blobCertificates, }, DisperserID: rand.Uint32(), - Signature: rand.RandomBytes(32), + Signature: rand.Bytes(32), } } @@ -68,7 +69,7 @@ func TestHashing(t *testing.T) { originalRequestHash := HashStoreChunksRequest(request) // modifying the signature should not change the hash - request.Signature = rand.RandomBytes(32) + request.Signature = rand.Bytes(32) hash := HashStoreChunksRequest(request) require.Equal(t, originalRequestHash, hash) @@ -136,21 +137,21 @@ func TestHashing(t *testing.T) { // within a blob cert, modify the Commitment.Commitment rand.Reset() request = randomStoreChunksRequest(rand) - request.Batch.BlobCertificates[0].BlobHeader.Commitment.Commitment = rand.RandomBytes(32) + request.Batch.BlobCertificates[0].BlobHeader.Commitment.Commitment = rand.Bytes(32) hash = HashStoreChunksRequest(request) require.NotEqual(t, originalRequestHash, hash) // within a blob cert, modify the Commitment.LengthCommitment rand.Reset() request = randomStoreChunksRequest(rand) - request.Batch.BlobCertificates[0].BlobHeader.Commitment.LengthCommitment = rand.RandomBytes(32) + request.Batch.BlobCertificates[0].BlobHeader.Commitment.LengthCommitment = rand.Bytes(32) hash = HashStoreChunksRequest(request) require.NotEqual(t, originalRequestHash, hash) // within a blob cert, modify the Commitment.LengthProof rand.Reset() request = randomStoreChunksRequest(rand) - request.Batch.BlobCertificates[0].BlobHeader.Commitment.LengthProof = rand.RandomBytes(32) + request.Batch.BlobCertificates[0].BlobHeader.Commitment.LengthProof = rand.Bytes(32) hash = HashStoreChunksRequest(request) require.NotEqual(t, originalRequestHash, hash) @@ -164,7 +165,7 @@ func TestHashing(t *testing.T) { // within a blob cert, modify the PaymentHeader.AccountId rand.Reset() request = randomStoreChunksRequest(rand) - request.Batch.BlobCertificates[0].BlobHeader.PaymentHeader.AccountId = rand.RandomString(32) + request.Batch.BlobCertificates[0].BlobHeader.PaymentHeader.AccountId = rand.String(32) hash = HashStoreChunksRequest(request) require.NotEqual(t, originalRequestHash, hash) @@ -178,7 +179,7 @@ func TestHashing(t *testing.T) { // within a blob cert, modify the PaymentHeader.CumulativePayment rand.Reset() request = randomStoreChunksRequest(rand) - request.Batch.BlobCertificates[0].BlobHeader.PaymentHeader.CumulativePayment = rand.RandomBytes(32) + request.Batch.BlobCertificates[0].BlobHeader.PaymentHeader.CumulativePayment = rand.Bytes(32) hash = HashStoreChunksRequest(request) require.NotEqual(t, originalRequestHash, hash) @@ -192,7 +193,7 @@ func TestHashing(t *testing.T) { // within a blob cert, modify the Signature rand.Reset() request = randomStoreChunksRequest(rand) - request.Batch.BlobCertificates[0].BlobHeader.Signature = rand.RandomBytes(32) + request.Batch.BlobCertificates[0].BlobHeader.Signature = rand.Bytes(32) hash = HashStoreChunksRequest(request) require.NotEqual(t, originalRequestHash, hash) } @@ -200,34 +201,37 @@ func TestHashing(t *testing.T) { func TestRequestSigning(t *testing.T) { rand := random.NewTestRandom(t) - public, private := rand.RandomECDSA() + public, private := rand.ECDSA() + publicAddress := crypto.PubkeyToAddress(*public) + request := randomStoreChunksRequest(rand) signature, err := SignStoreChunksRequest(private, request) require.NoError(t, err) - isValid := VerifyStoreChunksRequest(public, request, signature) - require.True(t, isValid) + err = VerifyStoreChunksRequest(publicAddress, request, signature) + require.NoError(t, err) // Adding the signature to the request should not change the hash, so it should still be valid request.Signature = signature - isValid = VerifyStoreChunksRequest(public, request, signature) - require.True(t, isValid) + err = VerifyStoreChunksRequest(publicAddress, request, signature) + require.NoError(t, err) // Using a different public key should make the signature invalid - otherPublic, _ := rand.RandomECDSA() - isValid = VerifyStoreChunksRequest(otherPublic, request, signature) - require.False(t, isValid) + otherPublic, _ := rand.ECDSA() + otherPublicAddress := crypto.PubkeyToAddress(*otherPublic) + err = VerifyStoreChunksRequest(otherPublicAddress, request, signature) + require.Error(t, err) // Changing a byte in the signature should make it invalid alteredSignature := make([]byte, len(signature)) copy(alteredSignature, signature) alteredSignature[0] = alteredSignature[0] + 1 - isValid = VerifyStoreChunksRequest(public, request, alteredSignature) - require.False(t, isValid) + err = VerifyStoreChunksRequest(publicAddress, request, alteredSignature) + require.Error(t, err) // Changing a field in the request should make it invalid request.DisperserID = request.DisperserID + 1 - isValid = VerifyStoreChunksRequest(public, request, signature) - require.False(t, isValid) + err = VerifyStoreChunksRequest(publicAddress, request, signature) + require.Error(t, err) } From 6ca7c9b95ec9dccad0d25498a11664d07eab5bb3 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Fri, 13 Dec 2024 11:11:20 -0600 Subject: [PATCH 21/94] Finished unit tests. Signed-off-by: Cody Littley --- node/auth/authenticator_test.go | 455 ++++++++++++++++++++++++++++++-- 1 file changed, 435 insertions(+), 20 deletions(-) diff --git a/node/auth/authenticator_test.go b/node/auth/authenticator_test.go index b36a671c5..1ea701f4e 100644 --- a/node/auth/authenticator_test.go +++ b/node/auth/authenticator_test.go @@ -2,6 +2,9 @@ package auth import ( "context" + "crypto/ecdsa" + "errors" + "fmt" "github.com/Layr-Labs/eigenda/common/testutils/random" wmock "github.com/Layr-Labs/eigenda/core/mock" "github.com/ethereum/go-ethereum/crypto" @@ -11,27 +14,9 @@ import ( ) // TODO: -// - test good request -// - test bad request -// - test request from disperser that doesn't exist -// - test auth caching // - test key caching // - test cache sizes -//// Verify that public key can be converted to an eth address and back -//func TestPubKeyRoundTrip(t *testing.T) { -// rand := random.NewTestRandom(t) -// -// publicKey, _ := rand.ECDSA() -// -// ethAddress := crypto.PubkeyToAddress(*publicKey) -// -// publicKey2, err := crypto.UnmarshalPubkey(ethAddress.Bytes()) -// require.NoError(t, err) -// -// require.Equal(t, publicKey, publicKey2) -//} - func TestValidRequest(t *testing.T) { rand := random.NewTestRandom(t) @@ -40,8 +25,242 @@ func TestValidRequest(t *testing.T) { publicKey, privateKey := rand.ECDSA() disperserAddress := crypto.PubkeyToAddress(*publicKey) - //disperserAddressBytes := crypto.FromECDSAPub(publicKey) - //disperserAddress := gethcommon.BytesToAddress(disperserAddressBytes) + chainReader := wmock.MockWriter{} + chainReader.Mock.On("GetDisperserAddress", uint32(0)).Return(disperserAddress, nil) + + authenticator, err := NewRequestAuthenticator( + context.Background(), + &chainReader, + 10, + time.Minute, + time.Minute, + start) + require.NoError(t, err) + + request := randomStoreChunksRequest(rand) + request.DisperserID = 0 + signature, err := SignStoreChunksRequest(privateKey, request) + require.NoError(t, err) + request.Signature = signature + + err = authenticator.AuthenticateStoreChunksRequest(context.Background(), "localhost", request, start) + require.NoError(t, err) +} + +func TestInvalidRequestWrongHash(t *testing.T) { + rand := random.NewTestRandom(t) + + start := rand.Time() + + publicKey, privateKey := rand.ECDSA() + disperserAddress := crypto.PubkeyToAddress(*publicKey) + + chainReader := wmock.MockWriter{} + chainReader.Mock.On("GetDisperserAddress", uint32(0)).Return(disperserAddress, nil) + + authenticator, err := NewRequestAuthenticator( + context.Background(), + &chainReader, + 10, + time.Minute, + time.Minute, + start) + require.NoError(t, err) + + request := randomStoreChunksRequest(rand) + request.DisperserID = 0 + signature, err := SignStoreChunksRequest(privateKey, request) + require.NoError(t, err) + request.Signature = signature + + // Modify the request so that the hash is different + request.Batch.BlobCertificates[0].BlobHeader.Commitment.LengthProof = rand.Bytes(32) + + err = authenticator.AuthenticateStoreChunksRequest(context.Background(), "localhost", request, start) + require.Error(t, err) +} + +func TestInvalidRequestWrongKey(t *testing.T) { + rand := random.NewTestRandom(t) + + start := rand.Time() + + publicKey, _ := rand.ECDSA() + disperserAddress := crypto.PubkeyToAddress(*publicKey) + + chainReader := wmock.MockWriter{} + chainReader.Mock.On("GetDisperserAddress", uint32(0)).Return(disperserAddress, nil) + + authenticator, err := NewRequestAuthenticator( + context.Background(), + &chainReader, + 10, + time.Minute, + time.Minute, + start) + require.NoError(t, err) + + request := randomStoreChunksRequest(rand) + request.DisperserID = 0 + + _, differentPrivateKey := rand.ECDSA() + signature, err := SignStoreChunksRequest(differentPrivateKey, request) + require.NoError(t, err) + request.Signature = signature + + err = authenticator.AuthenticateStoreChunksRequest(context.Background(), "localhost", request, start) + require.Error(t, err) +} + +func TestInvalidRequestInvalidDisperserID(t *testing.T) { + rand := random.NewTestRandom(t) + + start := rand.Time() + + publicKey, _ := rand.ECDSA() + disperserAddress := crypto.PubkeyToAddress(*publicKey) + + chainReader := wmock.MockWriter{} + chainReader.Mock.On("GetDisperserAddress", uint32(0)).Return(disperserAddress, nil) + chainReader.Mock.On("GetDisperserAddress", uint32(1234)).Return( + nil, errors.New("disperser not found")) + + authenticator, err := NewRequestAuthenticator( + context.Background(), + &chainReader, + 10, + time.Minute, + time.Minute, + start) + require.NoError(t, err) + + request := randomStoreChunksRequest(rand) + request.DisperserID = 1234 + + _, differentPrivateKey := rand.ECDSA() + signature, err := SignStoreChunksRequest(differentPrivateKey, request) + require.NoError(t, err) + request.Signature = signature + + err = authenticator.AuthenticateStoreChunksRequest(context.Background(), "localhost", request, start) + require.Error(t, err) +} + +func TestAuthCaching(t *testing.T) { + rand := random.NewTestRandom(t) + + start := rand.Time() + + publicKey, privateKey := rand.ECDSA() + disperserAddress := crypto.PubkeyToAddress(*publicKey) + + chainReader := wmock.MockWriter{} + chainReader.Mock.On("GetDisperserAddress", uint32(0)).Return(disperserAddress, nil) + + authenticator, err := NewRequestAuthenticator( + context.Background(), + &chainReader, + 10, + time.Minute, + time.Minute, + start) + require.NoError(t, err) + + // The first request will actually be validated. + request := randomStoreChunksRequest(rand) + request.DisperserID = 0 + signature, err := SignStoreChunksRequest(privateKey, request) + require.NoError(t, err) + request.Signature = signature + + err = authenticator.AuthenticateStoreChunksRequest(context.Background(), "localhost", request, start) + require.NoError(t, err) + + // Make some more requests. Intentionally fiddle with the hash to make them invalid if checked. + // With auth caching, those checks won't happen until the auth timeout has passed (configured to 1 minute). + now := start + for i := 0; i < 60; i++ { + request = randomStoreChunksRequest(rand) + request.DisperserID = 0 + signature, err = SignStoreChunksRequest(privateKey, request) + require.NoError(t, err) + request.Signature = signature + + request.Batch.BlobCertificates[0].BlobHeader.Commitment.LengthProof = rand.Bytes(32) + + err = authenticator.AuthenticateStoreChunksRequest(context.Background(), "localhost", request, now) + now = now.Add(time.Second) + require.NoError(t, err) + + // making the same request from a different origin should cause validation to happen and for it to fail + err = authenticator.AuthenticateStoreChunksRequest(context.Background(), "otherhost", request, now) + require.Error(t, err) + } + + // The next request will be made after the auth timeout has passed, so it will be validated. + // Since it is actually invalid, the authenticator should reject it. + request = randomStoreChunksRequest(rand) + request.DisperserID = 0 + signature, err = SignStoreChunksRequest(privateKey, request) + require.NoError(t, err) + request.Signature = signature + + request.Batch.BlobCertificates[0].BlobHeader.Commitment.LengthProof = rand.Bytes(32) + + err = authenticator.AuthenticateStoreChunksRequest(context.Background(), "localhost", request, now) + require.Error(t, err) +} + +func TestAuthCachingDisabled(t *testing.T) { + rand := random.NewTestRandom(t) + + start := rand.Time() + + publicKey, privateKey := rand.ECDSA() + disperserAddress := crypto.PubkeyToAddress(*publicKey) + + chainReader := wmock.MockWriter{} + chainReader.Mock.On("GetDisperserAddress", uint32(0)).Return(disperserAddress, nil) + + authenticator, err := NewRequestAuthenticator( + context.Background(), + &chainReader, + 10, + time.Minute, + 0, // This disables auth caching + start) + require.NoError(t, err) + + // The first request will always be validated. + request := randomStoreChunksRequest(rand) + request.DisperserID = 0 + signature, err := SignStoreChunksRequest(privateKey, request) + require.NoError(t, err) + request.Signature = signature + + err = authenticator.AuthenticateStoreChunksRequest(context.Background(), "localhost", request, start) + require.NoError(t, err) + + // Make another request without moving time forward. It should be validated. + request = randomStoreChunksRequest(rand) + request.DisperserID = 0 + signature, err = SignStoreChunksRequest(privateKey, request) + require.NoError(t, err) + request.Signature = signature + + request.Batch.BlobCertificates[0].BlobHeader.Commitment.LengthProof = rand.Bytes(32) + + err = authenticator.AuthenticateStoreChunksRequest(context.Background(), "localhost", request, start) + require.Error(t, err) +} + +func TestKeyExpiry(t *testing.T) { + rand := random.NewTestRandom(t) + + start := rand.Time() + + publicKey, privateKey := rand.ECDSA() + disperserAddress := crypto.PubkeyToAddress(*publicKey) chainReader := wmock.MockWriter{} chainReader.Mock.On("GetDisperserAddress", uint32(0)).Return(disperserAddress, nil) @@ -55,6 +274,9 @@ func TestValidRequest(t *testing.T) { start) require.NoError(t, err) + // Preloading the cache should have grabbed Disperser 0's key + chainReader.Mock.AssertNumberOfCalls(t, "GetDisperserAddress", 1) + request := randomStoreChunksRequest(rand) request.DisperserID = 0 signature, err := SignStoreChunksRequest(privateKey, request) @@ -63,4 +285,197 @@ func TestValidRequest(t *testing.T) { err = authenticator.AuthenticateStoreChunksRequest(context.Background(), "localhost", request, start) require.NoError(t, err) + + // Since time hasn't advanced, the authenticator shouldn't have fetched the key again + chainReader.Mock.AssertNumberOfCalls(t, "GetDisperserAddress", 1) + + // Move time forward to just before the key expires. + now := start.Add(59 * time.Second) + err = authenticator.AuthenticateStoreChunksRequest(context.Background(), "localhost", request, now) + require.NoError(t, err) + + // The key should not yet have been fetched again. + chainReader.Mock.AssertNumberOfCalls(t, "GetDisperserAddress", 1) + + // Move time forward to just after the key expires. + now = now.Add(2 * time.Second) + err = authenticator.AuthenticateStoreChunksRequest(context.Background(), "localhost", request, now) + require.NoError(t, err) + + // The key should have been fetched again. + chainReader.Mock.AssertNumberOfCalls(t, "GetDisperserAddress", 2) +} + +func TestAuthCacheSize(t *testing.T) { + rand := random.NewTestRandom(t) + + start := rand.Time() + + publicKey, privateKey := rand.ECDSA() + disperserAddress := crypto.PubkeyToAddress(*publicKey) + + chainReader := wmock.MockWriter{} + chainReader.Mock.On("GetDisperserAddress", uint32(0)).Return(disperserAddress, nil) + + cacheSize := rand.Intn(10) + 2 + + authenticator, err := NewRequestAuthenticator( + context.Background(), + &chainReader, + cacheSize, + time.Minute, + time.Minute, + start) + require.NoError(t, err) + + // Make requests from cacheSize different origins. + for i := 0; i < cacheSize; i++ { + request := randomStoreChunksRequest(rand) + request.DisperserID = 0 + signature, err := SignStoreChunksRequest(privateKey, request) + require.NoError(t, err) + request.Signature = signature + + origin := fmt.Sprintf("%d", i) + + err = authenticator.AuthenticateStoreChunksRequest(context.Background(), origin, request, start) + require.NoError(t, err) + } + + // All origins should be authenticated in the auth cache. If we send invalid requests from the same origins, + // they should still be authenticated (since the authenticator won't re-check). + for i := 0; i < cacheSize; i++ { + request := randomStoreChunksRequest(rand) + request.DisperserID = 0 + signature, err := SignStoreChunksRequest(privateKey, request) + require.NoError(t, err) + request.Signature = signature + + request.Batch.BlobCertificates[0].BlobHeader.Commitment.LengthProof = rand.Bytes(32) + + origin := fmt.Sprintf("%d", i) + + err = authenticator.AuthenticateStoreChunksRequest(context.Background(), origin, request, start) + require.NoError(t, err) + } + + // Make a request from a new origin. This should boot origin 0 from the cache. + request := randomStoreChunksRequest(rand) + request.DisperserID = 0 + signature, err := SignStoreChunksRequest(privateKey, request) + require.NoError(t, err) + request.Signature = signature + + err = authenticator.AuthenticateStoreChunksRequest(context.Background(), "neworigin", request, start) + require.NoError(t, err) + + for i := 0; i < cacheSize; i++ { + request = randomStoreChunksRequest(rand) + request.DisperserID = 0 + signature, err = SignStoreChunksRequest(privateKey, request) + require.NoError(t, err) + request.Signature = signature + + request.Batch.BlobCertificates[0].BlobHeader.Commitment.LengthProof = rand.Bytes(32) + + origin := fmt.Sprintf("%d", i) + + err = authenticator.AuthenticateStoreChunksRequest(context.Background(), origin, request, start) + + if i == 0 { + // Origin 0 should have been booted from the cache, so this request should be re-validated. + require.Error(t, err) + } else { + // All other origins should still be in the cache. + require.NoError(t, err) + } + } +} + +func TestKeyCacheSize(t *testing.T) { + rand := random.NewTestRandom(t) + + start := rand.Time() + + cacheSize := rand.Intn(10) + 2 + + chainReader := wmock.MockWriter{} + keyMap := make(map[uint32]*ecdsa.PrivateKey, cacheSize+1) + for i := 0; i < cacheSize+1; i++ { + publicKey, privateKey := rand.ECDSA() + disperserAddress := crypto.PubkeyToAddress(*publicKey) + keyMap[uint32(i)] = privateKey + + chainReader.Mock.On("GetDisperserAddress", uint32(i)).Return(disperserAddress, nil) + } + + authenticator, err := NewRequestAuthenticator( + context.Background(), + &chainReader, + cacheSize, + time.Minute, + 0, // disable auth caching + start) + require.NoError(t, err) + + // The authenticator will preload key 0 into the cache. + chainReader.Mock.AssertNumberOfCalls(t, "GetDisperserAddress", 1) + + // Make a request for each key (except for the last one, which won't fit in the cache). + for i := 0; i < cacheSize; i++ { + request := randomStoreChunksRequest(rand) + request.DisperserID = uint32(i) + signature, err := SignStoreChunksRequest(keyMap[uint32(i)], request) + require.NoError(t, err) + request.Signature = signature + + origin := fmt.Sprintf("%d", i) + + err = authenticator.AuthenticateStoreChunksRequest(context.Background(), origin, request, start) + require.NoError(t, err) + } + + // All keys should have required exactly one read except from 0, which was preloaded. + chainReader.Mock.AssertNumberOfCalls(t, "GetDisperserAddress", cacheSize) + + // Make another request for each key. None should require a read from the chain. + for i := 0; i < cacheSize; i++ { + request := randomStoreChunksRequest(rand) + request.DisperserID = uint32(i) + signature, err := SignStoreChunksRequest(keyMap[uint32(i)], request) + require.NoError(t, err) + request.Signature = signature + + origin := fmt.Sprintf("%d", i) + + err = authenticator.AuthenticateStoreChunksRequest(context.Background(), origin, request, start) + require.NoError(t, err) + } + + chainReader.Mock.AssertNumberOfCalls(t, "GetDisperserAddress", cacheSize) + + // Make a request for the last key. This should require a read from the chain and will boot key 0 from the cache. + request := randomStoreChunksRequest(rand) + request.DisperserID = uint32(cacheSize) + signature, err := SignStoreChunksRequest(keyMap[uint32(cacheSize)], request) + require.NoError(t, err) + request.Signature = signature + + err = authenticator.AuthenticateStoreChunksRequest(context.Background(), + fmt.Sprintf("%d", cacheSize), request, start) + require.NoError(t, err) + + chainReader.Mock.AssertNumberOfCalls(t, "GetDisperserAddress", cacheSize+1) + + // Make another request for key 0. This should require a read from the chain. + request = randomStoreChunksRequest(rand) + request.DisperserID = 0 + signature, err = SignStoreChunksRequest(keyMap[0], request) + require.NoError(t, err) + request.Signature = signature + + err = authenticator.AuthenticateStoreChunksRequest(context.Background(), "0", request, start) + require.NoError(t, err) + + chainReader.Mock.AssertNumberOfCalls(t, "GetDisperserAddress", cacheSize+2) } From 3348883c1b22674358452e78faf1ff6f4e250326 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Fri, 13 Dec 2024 11:40:07 -0600 Subject: [PATCH 22/94] Cleanup. Signed-off-by: Cody Littley --- .gitignore | 3 --- node/auth/authenticator_test.go | 4 ---- node/config.go | 2 -- 3 files changed, 9 deletions(-) diff --git a/.gitignore b/.gitignore index c49c17e81..40fe5f34c 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,3 @@ lightnode/docker/args.sh .vscode icicle/* - -# Just to make sure somebody doesn't accidentally commit the disperser's private key. -eigenda-disperser-private.pem diff --git a/node/auth/authenticator_test.go b/node/auth/authenticator_test.go index 1ea701f4e..d33117469 100644 --- a/node/auth/authenticator_test.go +++ b/node/auth/authenticator_test.go @@ -13,10 +13,6 @@ import ( "time" ) -// TODO: -// - test key caching -// - test cache sizes - func TestValidRequest(t *testing.T) { rand := random.NewTestRandom(t) diff --git a/node/config.go b/node/config.go index 64874db71..7cfb219cd 100644 --- a/node/config.go +++ b/node/config.go @@ -95,8 +95,6 @@ type Config struct { PprofHttpPort string EnablePprof bool - // TODO update flags - // if true then the node will not authenticate StoreChunks requests from dispersers (v2 only) DisableDispersalAuthentication bool // the size of the cache for storing public keys of dispersers From e5966f0f2b6d17bb1641c898fa523a402336fd1c Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Fri, 13 Dec 2024 13:03:37 -0600 Subject: [PATCH 23/94] Cleanup Signed-off-by: Cody Littley --- api/clients/node_client_v2.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/clients/node_client_v2.go b/api/clients/node_client_v2.go index 2bc358833..73433aa6e 100644 --- a/api/clients/node_client_v2.go +++ b/api/clients/node_client_v2.go @@ -84,10 +84,11 @@ func (c *nodeClientV2) StoreChunks(ctx context.Context, batch *corev2.Batch) (*c }, BlobCertificates: blobCerts, }, + DisperserID: uint32(0), // currently hard coded, update for decentralized dispersers } if c.key != nil { - signature, err := auth.SignStoreChunksRequest(c.key, request) // TODO + signature, err := auth.SignStoreChunksRequest(c.key, request) if err != nil { return nil, fmt.Errorf("failed to sign request: %v", err) } From f0c6f4efc8398da3957e418c2503047b8658421f Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Mon, 16 Dec 2024 08:43:18 -0600 Subject: [PATCH 24/94] Cleanup. Signed-off-by: Cody Littley --- node/grpc/server_test.go | 2 +- test/integration_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/node/grpc/server_test.go b/node/grpc/server_test.go index edbc45a7a..de996504c 100644 --- a/node/grpc/server_test.go +++ b/node/grpc/server_test.go @@ -85,7 +85,7 @@ func makeConfig(t *testing.T) *node.Config { ID: opID, NumBatchValidators: runtime.GOMAXPROCS(0), EnableV2: false, - DisableDispersalAuthentication: true, // TODO re-enable + DisableDispersalAuthentication: true, // TODO enable } } diff --git a/test/integration_test.go b/test/integration_test.go index 47684456c..c31a286e8 100644 --- a/test/integration_test.go +++ b/test/integration_test.go @@ -363,7 +363,7 @@ func mustMakeOperators(t *testing.T, cst *coremock.ChainDataMock, logger logging PrivateBls: string(op.KeyPair.GetPubKeyG1().Serialize()), ID: id, QuorumIDList: registeredQuorums, - DisableDispersalAuthentication: true, // TODO re-enable + DisableDispersalAuthentication: true, // TODO enable } // creating a new instance of encoder instead of sharing enc because enc is not thread safe @@ -419,7 +419,7 @@ func mustMakeOperators(t *testing.T, cst *coremock.ChainDataMock, logger logging ratelimiter := &commonmock.NoopRatelimiter{} - // TODO enable request validation + // TODO this needs to be non-null once we enable dispersal authentication var client common.EthClient serverV1 := nodegrpc.NewServer(config, n, logger, ratelimiter) From 6a002566b05670b6a3296be9ad7d4d0dc1bf177e Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Mon, 16 Dec 2024 10:23:41 -0600 Subject: [PATCH 25/94] Added request signer. Signed-off-by: Cody Littley --- api/clients/node_client_v2.go | 33 ++----------------- api/clients/request_signer.go | 61 +++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 30 deletions(-) create mode 100644 api/clients/request_signer.go diff --git a/api/clients/node_client_v2.go b/api/clients/node_client_v2.go index 73433aa6e..58452aa94 100644 --- a/api/clients/node_client_v2.go +++ b/api/clients/node_client_v2.go @@ -2,9 +2,7 @@ package clients import ( "context" - "crypto/ecdsa" "fmt" - "github.com/Layr-Labs/eigenda/node/auth" "sync" commonpb "github.com/Layr-Labs/eigenda/api/grpc/common/v2" @@ -18,8 +16,6 @@ type NodeClientV2Config struct { Hostname string Port string UseSecureGrpcFlag bool - // The .pem file containing the private key used to sign StoreChunks() requests. If "" then no signing is done. - PrivateKeyFile string } type NodeClientV2 interface { @@ -31,7 +27,6 @@ type nodeClientV2 struct { config *NodeClientV2Config initOnce sync.Once conn *grpc.ClientConn - key *ecdsa.PrivateKey dispersalClient nodegrpc.DispersalClient } @@ -42,19 +37,8 @@ func NewNodeClientV2(config *NodeClientV2Config) (*nodeClientV2, error) { if config == nil || config.Hostname == "" || config.Port == "" { return nil, fmt.Errorf("invalid config: %v", config) } - - var key *ecdsa.PrivateKey // TODO update flags - //if config.PrivateKeyFile != "" { - // var err error - // key, err = auth.ReadPrivateECDSAKeyFile(config.PrivateKeyFile) - // if err != nil { - // return nil, fmt.Errorf("failed to read private key file: %v", err) - // } - //} - return &nodeClientV2{ config: config, - key: key, }, nil } @@ -76,7 +60,8 @@ func (c *nodeClientV2) StoreChunks(ctx context.Context, batch *corev2.Batch) (*c } } - request := &nodegrpc.StoreChunksRequest{ + // Call the gRPC method to store chunks + response, err := c.dispersalClient.StoreChunks(ctx, &nodegrpc.StoreChunksRequest{ Batch: &commonpb.Batch{ Header: &commonpb.BatchHeader{ BatchRoot: batch.BatchHeader.BatchRoot[:], @@ -84,19 +69,7 @@ func (c *nodeClientV2) StoreChunks(ctx context.Context, batch *corev2.Batch) (*c }, BlobCertificates: blobCerts, }, - DisperserID: uint32(0), // currently hard coded, update for decentralized dispersers - } - - if c.key != nil { - signature, err := auth.SignStoreChunksRequest(c.key, request) - if err != nil { - return nil, fmt.Errorf("failed to sign request: %v", err) - } - request.Signature = signature - } - - // Call the gRPC method to store chunks - response, err := c.dispersalClient.StoreChunks(ctx, request) + }) if err != nil { return nil, err } diff --git a/api/clients/request_signer.go b/api/clients/request_signer.go new file mode 100644 index 000000000..eecc2ce5b --- /dev/null +++ b/api/clients/request_signer.go @@ -0,0 +1,61 @@ +package clients + +import ( + "context" + "fmt" + grpc "github.com/Layr-Labs/eigenda/api/grpc/node/v2" + "github.com/Layr-Labs/eigenda/node/auth" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/kms" + "github.com/aws/aws-sdk-go-v2/service/kms/types" +) + +// RequestSigner encapsulates the logic for signing GetChunks requests. +type RequestSigner interface { + // SignStoreChunksRequest signs a StoreChunksRequest. Does not modify the request + // (i.e. it does not insert the signature). + SignStoreChunksRequest(ctx context.Context, request *grpc.StoreChunksRequest) ([]byte, error) +} + +var _ RequestSigner = &requestSigner{} + +type requestSigner struct { + keyID string + keyManager *kms.Client +} + +// NewRequestSigner creates a new RequestSigner. +func NewRequestSigner( + region string, + endpoint string, + keyID string) RequestSigner { + + keyManager := kms.New(kms.Options{ + Region: region, + BaseEndpoint: aws.String(endpoint), + }) + + return &requestSigner{ + keyID: keyID, + keyManager: keyManager, + } +} + +func (s *requestSigner) SignStoreChunksRequest(ctx context.Context, request *grpc.StoreChunksRequest) ([]byte, error) { + hash := auth.HashStoreChunksRequest(request) + + signOutput, err := s.keyManager.Sign( + ctx, + &kms.SignInput{ + KeyId: aws.String(s.keyID), + Message: hash, + SigningAlgorithm: types.SigningAlgorithmSpecEcdsaSha256, + MessageType: types.MessageTypeDigest, + }) + + if err != nil { + return nil, fmt.Errorf("failed to sign request: %w", err) + } + + return signOutput.Signature, nil +} From 27d5b8aed97b3fa18cc42ffcb3985ca949708265 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Mon, 16 Dec 2024 11:07:36 -0600 Subject: [PATCH 26/94] Incremental progress. Signed-off-by: Cody Littley --- api/clients/mock/static_request_signer.go | 30 +++++++++++++++++++ api/clients/node_client_v2.go | 28 ++++++++++++----- disperser/cmd/controller/main.go | 6 +++- disperser/controller/node_client_manager.go | 26 ++++++++++------ .../controller/node_client_manager_test.go | 11 ++++++- go.mod | 2 +- 6 files changed, 83 insertions(+), 20 deletions(-) create mode 100644 api/clients/mock/static_request_signer.go diff --git a/api/clients/mock/static_request_signer.go b/api/clients/mock/static_request_signer.go new file mode 100644 index 000000000..3e7ad8b0d --- /dev/null +++ b/api/clients/mock/static_request_signer.go @@ -0,0 +1,30 @@ +package mock + +import ( + "context" + "crypto/ecdsa" + "github.com/Layr-Labs/eigenda/api/clients" + v2 "github.com/Layr-Labs/eigenda/api/grpc/node/v2" + "github.com/Layr-Labs/eigenda/node/auth" +) + +var _ clients.RequestSigner = &staticRequestSigner{} + +// StaticRequestSigner is a RequestSigner that signs requests with a static key (i.e. it doesn't use AWS KMS). +// Useful for testing. +type staticRequestSigner struct { + key *ecdsa.PrivateKey +} + +func NewStaticRequestSigner(key *ecdsa.PrivateKey) clients.RequestSigner { + return &staticRequestSigner{ + key: key, + } +} + +func (s *staticRequestSigner) SignStoreChunksRequest( + ctx context.Context, + request *v2.StoreChunksRequest) ([]byte, error) { + + return auth.SignStoreChunksRequest(s.key, request) +} diff --git a/api/clients/node_client_v2.go b/api/clients/node_client_v2.go index 58452aa94..2e12f5278 100644 --- a/api/clients/node_client_v2.go +++ b/api/clients/node_client_v2.go @@ -24,21 +24,23 @@ type NodeClientV2 interface { } type nodeClientV2 struct { - config *NodeClientV2Config - initOnce sync.Once - conn *grpc.ClientConn + config *NodeClientV2Config + initOnce sync.Once + conn *grpc.ClientConn + requestSigner RequestSigner dispersalClient nodegrpc.DispersalClient } var _ NodeClientV2 = (*nodeClientV2)(nil) -func NewNodeClientV2(config *NodeClientV2Config) (*nodeClientV2, error) { +func NewNodeClientV2(config *NodeClientV2Config, requestSigner RequestSigner) (NodeClientV2, error) { if config == nil || config.Hostname == "" || config.Port == "" { return nil, fmt.Errorf("invalid config: %v", config) } return &nodeClientV2{ - config: config, + config: config, + requestSigner: requestSigner, }, nil } @@ -60,8 +62,7 @@ func (c *nodeClientV2) StoreChunks(ctx context.Context, batch *corev2.Batch) (*c } } - // Call the gRPC method to store chunks - response, err := c.dispersalClient.StoreChunks(ctx, &nodegrpc.StoreChunksRequest{ + request := &nodegrpc.StoreChunksRequest{ Batch: &commonpb.Batch{ Header: &commonpb.BatchHeader{ BatchRoot: batch.BatchHeader.BatchRoot[:], @@ -69,7 +70,18 @@ func (c *nodeClientV2) StoreChunks(ctx context.Context, batch *corev2.Batch) (*c }, BlobCertificates: blobCerts, }, - }) + DisperserID: 0, // this will need to be updated dispersers are decentralized + } + + // Sign the request to store chunks + signature, err := c.requestSigner.SignStoreChunksRequest(ctx, request) + if err != nil { + return nil, fmt.Errorf("failed to sign store chunks request: %v", err) + } + request.Signature = signature + + // Call the gRPC method to store chunks + response, err := c.dispersalClient.StoreChunks(ctx, request) if err != nil { return nil, err } diff --git a/disperser/cmd/controller/main.go b/disperser/cmd/controller/main.go index 2725c3fd4..9ca924fdb 100644 --- a/disperser/cmd/controller/main.go +++ b/disperser/cmd/controller/main.go @@ -3,6 +3,7 @@ package main import ( "context" "fmt" + "github.com/Layr-Labs/eigenda/api/clients" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/collectors" "github.com/prometheus/client_golang/prometheus/promhttp" @@ -148,7 +149,10 @@ func RunController(ctx *cli.Context) error { return err } } - nodeClientManager, err := controller.NewNodeClientManager(config.NodeClientCacheSize, logger) + + requestSigner := clients.NewRequestSigner("", "", "") // TODO: fill in the parameters + + nodeClientManager, err := controller.NewNodeClientManager(config.NodeClientCacheSize, requestSigner, logger) if err != nil { return fmt.Errorf("failed to create node client manager: %v", err) } diff --git a/disperser/controller/node_client_manager.go b/disperser/controller/node_client_manager.go index 0957428c6..50a29c1e4 100644 --- a/disperser/controller/node_client_manager.go +++ b/disperser/controller/node_client_manager.go @@ -14,13 +14,18 @@ type NodeClientManager interface { type nodeClientManager struct { // nodeClients is a cache of node clients keyed by socket address - nodeClients *lru.Cache[string, clients.NodeClientV2] - logger logging.Logger + nodeClients *lru.Cache[string, clients.NodeClientV2] + requestSigner clients.RequestSigner + logger logging.Logger } var _ NodeClientManager = (*nodeClientManager)(nil) -func NewNodeClientManager(cacheSize int, logger logging.Logger) (*nodeClientManager, error) { +func NewNodeClientManager( + cacheSize int, + requestSigner clients.RequestSigner, + logger logging.Logger) (*nodeClientManager, error) { + closeClient := func(socket string, value clients.NodeClientV2) { if err := value.Close(); err != nil { logger.Error("failed to close node client", "err", err) @@ -32,8 +37,9 @@ func NewNodeClientManager(cacheSize int, logger logging.Logger) (*nodeClientMana } return &nodeClientManager{ - nodeClients: nodeClients, - logger: logger, + nodeClients: nodeClients, + requestSigner: requestSigner, + logger: logger, }, nil } @@ -42,10 +48,12 @@ func (m *nodeClientManager) GetClient(host, port string) (clients.NodeClientV2, client, ok := m.nodeClients.Get(socket) if !ok { var err error - client, err = clients.NewNodeClientV2(&clients.NodeClientV2Config{ - Hostname: host, - Port: port, - }) + client, err = clients.NewNodeClientV2( + &clients.NodeClientV2Config{ + Hostname: host, + Port: port, + }, + m.requestSigner) if err != nil { return nil, fmt.Errorf("failed to create node client at %s: %w", socket, err) } diff --git a/disperser/controller/node_client_manager_test.go b/disperser/controller/node_client_manager_test.go index ffe0dc5a6..c394340fe 100644 --- a/disperser/controller/node_client_manager_test.go +++ b/disperser/controller/node_client_manager_test.go @@ -1,6 +1,8 @@ package controller_test import ( + "github.com/Layr-Labs/eigenda/api/clients/mock" + "github.com/Layr-Labs/eigenda/common/testutils/random" "testing" "github.com/Layr-Labs/eigenda/disperser/controller" @@ -8,7 +10,12 @@ import ( ) func TestNodeClientManager(t *testing.T) { - m, err := controller.NewNodeClientManager(2, nil) + rand := random.NewTestRandom(t) + + _, private := rand.ECDSA() + requestSigner := mock.NewStaticRequestSigner(private) + + m, err := controller.NewNodeClientManager(2, requestSigner, nil) require.NoError(t, err) client0, err := m.GetClient("localhost", "0000") @@ -38,3 +45,5 @@ func TestNodeClientManager(t *testing.T) { require.NotSame(t, client0, client4) } + +// TODO add test for request signing diff --git a/go.mod b/go.mod index f14a3556e..b5de6d4e7 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/aws/aws-sdk-go-v2 v1.26.1 github.com/aws/aws-sdk-go-v2/credentials v1.17.11 github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.13.12 + github.com/aws/aws-sdk-go-v2/service/kms v1.31.0 github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.28.6 github.com/consensys/gnark-crypto v0.12.1 github.com/emirpasic/gods v1.18.1 @@ -63,7 +64,6 @@ require ( github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.9.5 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.4 // indirect - github.com/aws/aws-sdk-go-v2/service/kms v1.31.0 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.20.5 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.28.6 // indirect From bbd82ecf3f5679bea9a926ec32651a4d718c7fcf Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Mon, 16 Dec 2024 11:23:59 -0600 Subject: [PATCH 27/94] Update flags. Signed-off-by: Cody Littley --- disperser/cmd/controller/config.go | 22 ++++++++++++---------- disperser/cmd/controller/flags/flags.go | 7 +++++++ disperser/cmd/controller/main.go | 5 ++++- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/disperser/cmd/controller/config.go b/disperser/cmd/controller/config.go index 62000b765..f833701a9 100644 --- a/disperser/cmd/controller/config.go +++ b/disperser/cmd/controller/config.go @@ -25,12 +25,13 @@ type Config struct { DynamoDBTableName string - EthClientConfig geth.EthClientConfig - AwsClientConfig aws.ClientConfig - LoggerConfig common.LoggerConfig - IndexerConfig indexer.Config - ChainStateConfig thegraph.Config - UseGraph bool + EthClientConfig geth.EthClientConfig + AwsClientConfig aws.ClientConfig + DisperserSigningKeyName string + LoggerConfig common.LoggerConfig + IndexerConfig indexer.Config + ChainStateConfig thegraph.Config + UseGraph bool BLSOperatorStateRetrieverAddr string EigenDAServiceManagerAddr string @@ -60,10 +61,11 @@ func NewConfig(ctx *cli.Context) (Config, error) { relays[i] = corev2.RelayKey(relay) } config := Config{ - DynamoDBTableName: ctx.GlobalString(flags.DynamoDBTableNameFlag.Name), - EthClientConfig: ethClientConfig, - AwsClientConfig: aws.ReadClientConfig(ctx, flags.FlagPrefix), - LoggerConfig: *loggerConfig, + DynamoDBTableName: ctx.GlobalString(flags.DynamoDBTableNameFlag.Name), + EthClientConfig: ethClientConfig, + AwsClientConfig: aws.ReadClientConfig(ctx, flags.FlagPrefix), + DisperserSigningKeyName: ctx.GlobalString(flags.DisperserSigningKeyNameFlag.Name), + LoggerConfig: *loggerConfig, EncodingManagerConfig: controller.EncodingManagerConfig{ PullInterval: ctx.GlobalDuration(flags.EncodingPullIntervalFlag.Name), EncodingRequestTimeout: ctx.GlobalDuration(flags.EncodingRequestTimeoutFlag.Name), diff --git a/disperser/cmd/controller/flags/flags.go b/disperser/cmd/controller/flags/flags.go index 7b3055234..ac6d5db41 100644 --- a/disperser/cmd/controller/flags/flags.go +++ b/disperser/cmd/controller/flags/flags.go @@ -178,6 +178,12 @@ var ( EnvVar: common.PrefixEnvVar(envVarPrefix, "METRICS_PORT"), Value: 9101, } + DisperserSigningKeyNameFlag = cli.StringFlag{ + Name: common.PrefixFlag(FlagPrefix, "disperser-signing-key-name"), + Usage: "Name of the key used to sign disperser requests (key must be stored in AWS KMS under this name)", + Required: true, + EnvVar: common.PrefixEnvVar(envVarPrefix, "DISPERSER_SIGNING_KEY_NAME"), + } ) var requiredFlags = []cli.Flag{ @@ -192,6 +198,7 @@ var requiredFlags = []cli.Flag{ DispatcherPullIntervalFlag, NodeRequestTimeoutFlag, NumConnectionsToNodesFlag, + DisperserSigningKeyNameFlag, } var optionalFlags = []cli.Flag{ diff --git a/disperser/cmd/controller/main.go b/disperser/cmd/controller/main.go index 9ca924fdb..8e8b18782 100644 --- a/disperser/cmd/controller/main.go +++ b/disperser/cmd/controller/main.go @@ -150,7 +150,10 @@ func RunController(ctx *cli.Context) error { } } - requestSigner := clients.NewRequestSigner("", "", "") // TODO: fill in the parameters + requestSigner := clients.NewRequestSigner( + config.AwsClientConfig.Region, + config.AwsClientConfig.EndpointURL, + config.DisperserSigningKeyName) nodeClientManager, err := controller.NewNodeClientManager(config.NodeClientCacheSize, requestSigner, logger) if err != nil { From 157c2ea09578d6334c88eabaae79a49d7c8050bd Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Mon, 16 Dec 2024 13:08:59 -0600 Subject: [PATCH 28/94] Started work on unit test, not yet working. Signed-off-by: Cody Littley --- api/clients/request_signer_test.go | 110 ++++++++++++++++++++ disperser/controller/node_client_manager.go | 2 +- node/auth/authenticator.go | 3 +- node/auth/authenticator_test.go | 36 +++---- node/auth/request_signing.go | 4 +- node/auth/request_signing_test.go | 109 +++++-------------- node/auth/request_signing_test_utils.go | 60 +++++++++++ 7 files changed, 218 insertions(+), 106 deletions(-) create mode 100644 api/clients/request_signer_test.go create mode 100644 node/auth/request_signing_test_utils.go diff --git a/api/clients/request_signer_test.go b/api/clients/request_signer_test.go new file mode 100644 index 000000000..c787c6ecb --- /dev/null +++ b/api/clients/request_signer_test.go @@ -0,0 +1,110 @@ +package clients + +import ( + "context" + "crypto/ecdsa" + "crypto/x509" + "github.com/Layr-Labs/eigenda/common/testutils/random" + "github.com/Layr-Labs/eigenda/inabox/deploy" + "github.com/Layr-Labs/eigenda/node/auth" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/kms" + "github.com/aws/aws-sdk-go-v2/service/kms/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ory/dockertest/v3" + "github.com/stretchr/testify/require" + "log" + "os" + "path/filepath" + "runtime" + "testing" +) + +var ( + dockertestPool *dockertest.Pool + dockertestResource *dockertest.Resource +) + +const ( + localstackPort = "4570" + localstackHost = "http://0.0.0.0:4570" + region = "us-east-1" +) + +func setup(t *testing.T) { + deployLocalStack := !(os.Getenv("DEPLOY_LOCALSTACK") == "false") + + _, b, _, _ := runtime.Caller(0) + rootPath := filepath.Join(filepath.Dir(b), "../..") + changeDirectory(filepath.Join(rootPath, "inabox")) + + if deployLocalStack { + var err error + dockertestPool, dockertestResource, err = deploy.StartDockertestWithLocalstackContainer(localstackPort) + require.NoError(t, err) + } +} + +func changeDirectory(path string) { + err := os.Chdir(path) + if err != nil { + log.Panicf("Failed to change directories. Error: %s", err) + } + + newDir, err := os.Getwd() + if err != nil { + log.Panicf("Failed to get working directory. Error: %s", err) + } + log.Printf("Current Working Directory: %s\n", newDir) +} + +func teardown() { + deployLocalStack := !(os.Getenv("DEPLOY_LOCALSTACK") == "false") + + if deployLocalStack { + deploy.PurgeDockertestResources(dockertestPool, dockertestResource) + } +} + +func TestRequestSigning(t *testing.T) { + rand := random.NewTestRandom(t) + setup(t) + defer teardown() + + keyManager := kms.New(kms.Options{ + Region: region, + BaseEndpoint: aws.String(localstackHost), + }) + createKeyOutput, err := keyManager.CreateKey(context.Background(), &kms.CreateKeyInput{ + KeySpec: types.KeySpecEccNistP256, + KeyUsage: types.KeyUsageTypeSignVerify, + }) + require.NoError(t, err) + + keyID := *createKeyOutput.KeyMetadata.KeyId + + getPublicKeyOutput, err := keyManager.GetPublicKey(context.Background(), &kms.GetPublicKeyInput{ + KeyId: aws.String(keyID), + }) + require.NoError(t, err) + + k, err := x509.ParsePKIXPublicKey(getPublicKeyOutput.PublicKey) + require.NoError(t, err) + + publicKey := k.(*ecdsa.PublicKey) + publicAddress := crypto.PubkeyToAddress(*publicKey) + + request := auth.RandomStoreChunksRequest(rand) + request.Signature = nil + + signer := NewRequestSigner(region, localstackHost, keyID) + + signature, err := signer.SignStoreChunksRequest(context.Background(), request) + require.NoError(t, err) + + require.Nil(t, request.Signature) + request.Signature = signature + + err = auth.VerifyStoreChunksRequest(publicAddress, request) + require.NoError(t, err) +} diff --git a/disperser/controller/node_client_manager.go b/disperser/controller/node_client_manager.go index 50a29c1e4..160dba6a9 100644 --- a/disperser/controller/node_client_manager.go +++ b/disperser/controller/node_client_manager.go @@ -24,7 +24,7 @@ var _ NodeClientManager = (*nodeClientManager)(nil) func NewNodeClientManager( cacheSize int, requestSigner clients.RequestSigner, - logger logging.Logger) (*nodeClientManager, error) { + logger logging.Logger) (NodeClientManager, error) { closeClient := func(socket string, value clients.NodeClientV2) { if err := value.Close(); err != nil { diff --git a/node/auth/authenticator.go b/node/auth/authenticator.go index ce7181be2..87621ec17 100644 --- a/node/auth/authenticator.go +++ b/node/auth/authenticator.go @@ -115,8 +115,7 @@ func (a *requestAuthenticator) AuthenticateStoreChunksRequest( return fmt.Errorf("failed to get operator key: %w", err) } - signature := request.Signature - err = VerifyStoreChunksRequest(*key, request, signature) + err = VerifyStoreChunksRequest(*key, request) if err != nil { return fmt.Errorf("failed to verify request: %w", err) } diff --git a/node/auth/authenticator_test.go b/node/auth/authenticator_test.go index d33117469..cac6bf891 100644 --- a/node/auth/authenticator_test.go +++ b/node/auth/authenticator_test.go @@ -33,7 +33,7 @@ func TestValidRequest(t *testing.T) { start) require.NoError(t, err) - request := randomStoreChunksRequest(rand) + request := RandomStoreChunksRequest(rand) request.DisperserID = 0 signature, err := SignStoreChunksRequest(privateKey, request) require.NoError(t, err) @@ -63,7 +63,7 @@ func TestInvalidRequestWrongHash(t *testing.T) { start) require.NoError(t, err) - request := randomStoreChunksRequest(rand) + request := RandomStoreChunksRequest(rand) request.DisperserID = 0 signature, err := SignStoreChunksRequest(privateKey, request) require.NoError(t, err) @@ -96,7 +96,7 @@ func TestInvalidRequestWrongKey(t *testing.T) { start) require.NoError(t, err) - request := randomStoreChunksRequest(rand) + request := RandomStoreChunksRequest(rand) request.DisperserID = 0 _, differentPrivateKey := rand.ECDSA() @@ -130,7 +130,7 @@ func TestInvalidRequestInvalidDisperserID(t *testing.T) { start) require.NoError(t, err) - request := randomStoreChunksRequest(rand) + request := RandomStoreChunksRequest(rand) request.DisperserID = 1234 _, differentPrivateKey := rand.ECDSA() @@ -163,7 +163,7 @@ func TestAuthCaching(t *testing.T) { require.NoError(t, err) // The first request will actually be validated. - request := randomStoreChunksRequest(rand) + request := RandomStoreChunksRequest(rand) request.DisperserID = 0 signature, err := SignStoreChunksRequest(privateKey, request) require.NoError(t, err) @@ -176,7 +176,7 @@ func TestAuthCaching(t *testing.T) { // With auth caching, those checks won't happen until the auth timeout has passed (configured to 1 minute). now := start for i := 0; i < 60; i++ { - request = randomStoreChunksRequest(rand) + request = RandomStoreChunksRequest(rand) request.DisperserID = 0 signature, err = SignStoreChunksRequest(privateKey, request) require.NoError(t, err) @@ -195,7 +195,7 @@ func TestAuthCaching(t *testing.T) { // The next request will be made after the auth timeout has passed, so it will be validated. // Since it is actually invalid, the authenticator should reject it. - request = randomStoreChunksRequest(rand) + request = RandomStoreChunksRequest(rand) request.DisperserID = 0 signature, err = SignStoreChunksRequest(privateKey, request) require.NoError(t, err) @@ -228,7 +228,7 @@ func TestAuthCachingDisabled(t *testing.T) { require.NoError(t, err) // The first request will always be validated. - request := randomStoreChunksRequest(rand) + request := RandomStoreChunksRequest(rand) request.DisperserID = 0 signature, err := SignStoreChunksRequest(privateKey, request) require.NoError(t, err) @@ -238,7 +238,7 @@ func TestAuthCachingDisabled(t *testing.T) { require.NoError(t, err) // Make another request without moving time forward. It should be validated. - request = randomStoreChunksRequest(rand) + request = RandomStoreChunksRequest(rand) request.DisperserID = 0 signature, err = SignStoreChunksRequest(privateKey, request) require.NoError(t, err) @@ -273,7 +273,7 @@ func TestKeyExpiry(t *testing.T) { // Preloading the cache should have grabbed Disperser 0's key chainReader.Mock.AssertNumberOfCalls(t, "GetDisperserAddress", 1) - request := randomStoreChunksRequest(rand) + request := RandomStoreChunksRequest(rand) request.DisperserID = 0 signature, err := SignStoreChunksRequest(privateKey, request) require.NoError(t, err) @@ -326,7 +326,7 @@ func TestAuthCacheSize(t *testing.T) { // Make requests from cacheSize different origins. for i := 0; i < cacheSize; i++ { - request := randomStoreChunksRequest(rand) + request := RandomStoreChunksRequest(rand) request.DisperserID = 0 signature, err := SignStoreChunksRequest(privateKey, request) require.NoError(t, err) @@ -341,7 +341,7 @@ func TestAuthCacheSize(t *testing.T) { // All origins should be authenticated in the auth cache. If we send invalid requests from the same origins, // they should still be authenticated (since the authenticator won't re-check). for i := 0; i < cacheSize; i++ { - request := randomStoreChunksRequest(rand) + request := RandomStoreChunksRequest(rand) request.DisperserID = 0 signature, err := SignStoreChunksRequest(privateKey, request) require.NoError(t, err) @@ -356,7 +356,7 @@ func TestAuthCacheSize(t *testing.T) { } // Make a request from a new origin. This should boot origin 0 from the cache. - request := randomStoreChunksRequest(rand) + request := RandomStoreChunksRequest(rand) request.DisperserID = 0 signature, err := SignStoreChunksRequest(privateKey, request) require.NoError(t, err) @@ -366,7 +366,7 @@ func TestAuthCacheSize(t *testing.T) { require.NoError(t, err) for i := 0; i < cacheSize; i++ { - request = randomStoreChunksRequest(rand) + request = RandomStoreChunksRequest(rand) request.DisperserID = 0 signature, err = SignStoreChunksRequest(privateKey, request) require.NoError(t, err) @@ -419,7 +419,7 @@ func TestKeyCacheSize(t *testing.T) { // Make a request for each key (except for the last one, which won't fit in the cache). for i := 0; i < cacheSize; i++ { - request := randomStoreChunksRequest(rand) + request := RandomStoreChunksRequest(rand) request.DisperserID = uint32(i) signature, err := SignStoreChunksRequest(keyMap[uint32(i)], request) require.NoError(t, err) @@ -436,7 +436,7 @@ func TestKeyCacheSize(t *testing.T) { // Make another request for each key. None should require a read from the chain. for i := 0; i < cacheSize; i++ { - request := randomStoreChunksRequest(rand) + request := RandomStoreChunksRequest(rand) request.DisperserID = uint32(i) signature, err := SignStoreChunksRequest(keyMap[uint32(i)], request) require.NoError(t, err) @@ -451,7 +451,7 @@ func TestKeyCacheSize(t *testing.T) { chainReader.Mock.AssertNumberOfCalls(t, "GetDisperserAddress", cacheSize) // Make a request for the last key. This should require a read from the chain and will boot key 0 from the cache. - request := randomStoreChunksRequest(rand) + request := RandomStoreChunksRequest(rand) request.DisperserID = uint32(cacheSize) signature, err := SignStoreChunksRequest(keyMap[uint32(cacheSize)], request) require.NoError(t, err) @@ -464,7 +464,7 @@ func TestKeyCacheSize(t *testing.T) { chainReader.Mock.AssertNumberOfCalls(t, "GetDisperserAddress", cacheSize+1) // Make another request for key 0. This should require a read from the chain. - request = randomStoreChunksRequest(rand) + request = RandomStoreChunksRequest(rand) request.DisperserID = 0 signature, err = SignStoreChunksRequest(keyMap[0], request) require.NoError(t, err) diff --git a/node/auth/request_signing.go b/node/auth/request_signing.go index dd7e24cd3..24ca80b8b 100644 --- a/node/auth/request_signing.go +++ b/node/auth/request_signing.go @@ -28,10 +28,10 @@ func SignStoreChunksRequest(key *ecdsa.PrivateKey, request *grpc.StoreChunksRequ // VerifyStoreChunksRequest verifies the given signature of the given StoreChunksRequest with the given // public key. -func VerifyStoreChunksRequest(key gethcommon.Address, request *grpc.StoreChunksRequest, signature []byte) error { +func VerifyStoreChunksRequest(key gethcommon.Address, request *grpc.StoreChunksRequest) error { requestHash := HashStoreChunksRequest(request) - signingPublicKey, err := crypto.SigToPub(requestHash, signature) + signingPublicKey, err := crypto.SigToPub(requestHash, request.Signature) if err != nil { return fmt.Errorf("failed to recover public key from signature: %w", err) } diff --git a/node/auth/request_signing_test.go b/node/auth/request_signing_test.go index 1d6a6e622..f43ac75ca 100644 --- a/node/auth/request_signing_test.go +++ b/node/auth/request_signing_test.go @@ -1,71 +1,16 @@ package auth import ( - "github.com/Layr-Labs/eigenda/api/grpc/common" - v2 "github.com/Layr-Labs/eigenda/api/grpc/common/v2" - grpc "github.com/Layr-Labs/eigenda/api/grpc/node/v2" "github.com/Layr-Labs/eigenda/common/testutils/random" "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/require" "testing" ) -func randomStoreChunksRequest(rand *random.TestRandom) *grpc.StoreChunksRequest { - certificateCount := rand.Intn(10) + 1 - blobCertificates := make([]*v2.BlobCertificate, certificateCount) - for i := 0; i < certificateCount; i++ { - - relayCount := rand.Intn(10) + 1 - relays := make([]uint32, relayCount) - for j := 0; j < relayCount; j++ { - relays[j] = rand.Uint32() - } - - quorumCount := rand.Intn(10) + 1 - quorumNumbers := make([]uint32, quorumCount) - for j := 0; j < quorumCount; j++ { - quorumNumbers[j] = rand.Uint32() - } - - blobCertificates[i] = &v2.BlobCertificate{ - BlobHeader: &v2.BlobHeader{ - Version: rand.Uint32(), - QuorumNumbers: quorumNumbers, - Commitment: &common.BlobCommitment{ - Commitment: rand.Bytes(32), - LengthCommitment: rand.Bytes(32), - LengthProof: rand.Bytes(32), - Length: rand.Uint32(), - }, - PaymentHeader: &common.PaymentHeader{ - AccountId: rand.String(32), - ReservationPeriod: rand.Uint32(), - CumulativePayment: rand.Bytes(32), - Salt: rand.Uint32(), - }, - Signature: rand.Bytes(32), - }, - Relays: relays, - } - } - - return &grpc.StoreChunksRequest{ - Batch: &v2.Batch{ - Header: &v2.BatchHeader{ - BatchRoot: rand.Bytes(32), - ReferenceBlockNumber: rand.Uint64(), - }, - BlobCertificates: blobCertificates, - }, - DisperserID: rand.Uint32(), - Signature: rand.Bytes(32), - } -} - func TestHashing(t *testing.T) { rand := random.NewTestRandom(t) - request := randomStoreChunksRequest(rand) + request := RandomStoreChunksRequest(rand) originalRequestHash := HashStoreChunksRequest(request) // modifying the signature should not change the hash @@ -75,28 +20,28 @@ func TestHashing(t *testing.T) { // modify the disperser id rand.Reset() - request = randomStoreChunksRequest(rand) + request = RandomStoreChunksRequest(rand) request.DisperserID = request.DisperserID + 1 hash = HashStoreChunksRequest(request) require.NotEqual(t, originalRequestHash, hash) // remove a blob cert rand.Reset() - request = randomStoreChunksRequest(rand) + request = RandomStoreChunksRequest(rand) request.Batch.BlobCertificates = request.Batch.BlobCertificates[:len(request.Batch.BlobCertificates)-1] hash = HashStoreChunksRequest(request) require.NotEqual(t, originalRequestHash, hash) // within a blob cert, modify a relay rand.Reset() - request = randomStoreChunksRequest(rand) + request = RandomStoreChunksRequest(rand) request.Batch.BlobCertificates[0].Relays[0] = request.Batch.BlobCertificates[0].Relays[0] + 1 hash = HashStoreChunksRequest(request) require.NotEqual(t, originalRequestHash, hash) // within a blob cert, remove a relay rand.Reset() - request = randomStoreChunksRequest(rand) + request = RandomStoreChunksRequest(rand) request.Batch.BlobCertificates[0].Relays = request.Batch.BlobCertificates[0].Relays[:len(request.Batch.BlobCertificates[0].Relays)-1] hash = HashStoreChunksRequest(request) @@ -104,14 +49,14 @@ func TestHashing(t *testing.T) { // within a blob cert, add a relay rand.Reset() - request = randomStoreChunksRequest(rand) + request = RandomStoreChunksRequest(rand) request.Batch.BlobCertificates[0].Relays = append(request.Batch.BlobCertificates[0].Relays, rand.Uint32()) hash = HashStoreChunksRequest(request) require.NotEqual(t, originalRequestHash, hash) // within a blob cert, modify a quorum number rand.Reset() - request = randomStoreChunksRequest(rand) + request = RandomStoreChunksRequest(rand) request.Batch.BlobCertificates[0].BlobHeader.QuorumNumbers[0] = request.Batch.BlobCertificates[0].BlobHeader.QuorumNumbers[0] + 1 hash = HashStoreChunksRequest(request) @@ -119,7 +64,7 @@ func TestHashing(t *testing.T) { // within a blob cert, remove a quorum number rand.Reset() - request = randomStoreChunksRequest(rand) + request = RandomStoreChunksRequest(rand) request.Batch.BlobCertificates[0].BlobHeader.QuorumNumbers = request.Batch.BlobCertificates[0].BlobHeader.QuorumNumbers[:len( request.Batch.BlobCertificates[0].BlobHeader.QuorumNumbers)-1] @@ -128,7 +73,7 @@ func TestHashing(t *testing.T) { // within a blob cert, add a quorum number rand.Reset() - request = randomStoreChunksRequest(rand) + request = RandomStoreChunksRequest(rand) request.Batch.BlobCertificates[0].BlobHeader.QuorumNumbers = append( request.Batch.BlobCertificates[0].BlobHeader.QuorumNumbers, rand.Uint32()) hash = HashStoreChunksRequest(request) @@ -136,63 +81,63 @@ func TestHashing(t *testing.T) { // within a blob cert, modify the Commitment.Commitment rand.Reset() - request = randomStoreChunksRequest(rand) + request = RandomStoreChunksRequest(rand) request.Batch.BlobCertificates[0].BlobHeader.Commitment.Commitment = rand.Bytes(32) hash = HashStoreChunksRequest(request) require.NotEqual(t, originalRequestHash, hash) // within a blob cert, modify the Commitment.LengthCommitment rand.Reset() - request = randomStoreChunksRequest(rand) + request = RandomStoreChunksRequest(rand) request.Batch.BlobCertificates[0].BlobHeader.Commitment.LengthCommitment = rand.Bytes(32) hash = HashStoreChunksRequest(request) require.NotEqual(t, originalRequestHash, hash) // within a blob cert, modify the Commitment.LengthProof rand.Reset() - request = randomStoreChunksRequest(rand) + request = RandomStoreChunksRequest(rand) request.Batch.BlobCertificates[0].BlobHeader.Commitment.LengthProof = rand.Bytes(32) hash = HashStoreChunksRequest(request) require.NotEqual(t, originalRequestHash, hash) // within a blob cert, modify the Commitment.Length rand.Reset() - request = randomStoreChunksRequest(rand) + request = RandomStoreChunksRequest(rand) request.Batch.BlobCertificates[0].BlobHeader.Commitment.Length = rand.Uint32() hash = HashStoreChunksRequest(request) require.NotEqual(t, originalRequestHash, hash) // within a blob cert, modify the PaymentHeader.AccountId rand.Reset() - request = randomStoreChunksRequest(rand) + request = RandomStoreChunksRequest(rand) request.Batch.BlobCertificates[0].BlobHeader.PaymentHeader.AccountId = rand.String(32) hash = HashStoreChunksRequest(request) require.NotEqual(t, originalRequestHash, hash) // within a blob cert, modify the PaymentHeader.ReservationPeriod rand.Reset() - request = randomStoreChunksRequest(rand) + request = RandomStoreChunksRequest(rand) request.Batch.BlobCertificates[0].BlobHeader.PaymentHeader.ReservationPeriod = rand.Uint32() hash = HashStoreChunksRequest(request) require.NotEqual(t, originalRequestHash, hash) // within a blob cert, modify the PaymentHeader.CumulativePayment rand.Reset() - request = randomStoreChunksRequest(rand) + request = RandomStoreChunksRequest(rand) request.Batch.BlobCertificates[0].BlobHeader.PaymentHeader.CumulativePayment = rand.Bytes(32) hash = HashStoreChunksRequest(request) require.NotEqual(t, originalRequestHash, hash) // within a blob cert, modify the PaymentHeader.Salt rand.Reset() - request = randomStoreChunksRequest(rand) + request = RandomStoreChunksRequest(rand) request.Batch.BlobCertificates[0].BlobHeader.PaymentHeader.Salt = rand.Uint32() hash = HashStoreChunksRequest(request) require.NotEqual(t, originalRequestHash, hash) // within a blob cert, modify the Signature rand.Reset() - request = randomStoreChunksRequest(rand) + request = RandomStoreChunksRequest(rand) request.Batch.BlobCertificates[0].BlobHeader.Signature = rand.Bytes(32) hash = HashStoreChunksRequest(request) require.NotEqual(t, originalRequestHash, hash) @@ -204,34 +149,32 @@ func TestRequestSigning(t *testing.T) { public, private := rand.ECDSA() publicAddress := crypto.PubkeyToAddress(*public) - request := randomStoreChunksRequest(rand) + request := RandomStoreChunksRequest(rand) signature, err := SignStoreChunksRequest(private, request) require.NoError(t, err) - - err = VerifyStoreChunksRequest(publicAddress, request, signature) - require.NoError(t, err) - - // Adding the signature to the request should not change the hash, so it should still be valid request.Signature = signature - err = VerifyStoreChunksRequest(publicAddress, request, signature) + + err = VerifyStoreChunksRequest(publicAddress, request) require.NoError(t, err) // Using a different public key should make the signature invalid otherPublic, _ := rand.ECDSA() otherPublicAddress := crypto.PubkeyToAddress(*otherPublic) - err = VerifyStoreChunksRequest(otherPublicAddress, request, signature) + err = VerifyStoreChunksRequest(otherPublicAddress, request) require.Error(t, err) // Changing a byte in the signature should make it invalid alteredSignature := make([]byte, len(signature)) copy(alteredSignature, signature) alteredSignature[0] = alteredSignature[0] + 1 - err = VerifyStoreChunksRequest(publicAddress, request, alteredSignature) + request.Signature = alteredSignature + err = VerifyStoreChunksRequest(publicAddress, request) require.Error(t, err) // Changing a field in the request should make it invalid request.DisperserID = request.DisperserID + 1 - err = VerifyStoreChunksRequest(publicAddress, request, signature) + request.Signature = signature + err = VerifyStoreChunksRequest(publicAddress, request) require.Error(t, err) } diff --git a/node/auth/request_signing_test_utils.go b/node/auth/request_signing_test_utils.go new file mode 100644 index 000000000..cdc82956a --- /dev/null +++ b/node/auth/request_signing_test_utils.go @@ -0,0 +1,60 @@ +package auth + +import ( + "github.com/Layr-Labs/eigenda/api/grpc/common" + v2 "github.com/Layr-Labs/eigenda/api/grpc/common/v2" + grpc "github.com/Layr-Labs/eigenda/api/grpc/node/v2" + "github.com/Layr-Labs/eigenda/common/testutils/random" +) + +func RandomStoreChunksRequest(rand *random.TestRandom) *grpc.StoreChunksRequest { + certificateCount := rand.Intn(10) + 1 + blobCertificates := make([]*v2.BlobCertificate, certificateCount) + for i := 0; i < certificateCount; i++ { + + relayCount := rand.Intn(10) + 1 + relays := make([]uint32, relayCount) + for j := 0; j < relayCount; j++ { + relays[j] = rand.Uint32() + } + + quorumCount := rand.Intn(10) + 1 + quorumNumbers := make([]uint32, quorumCount) + for j := 0; j < quorumCount; j++ { + quorumNumbers[j] = rand.Uint32() + } + + blobCertificates[i] = &v2.BlobCertificate{ + BlobHeader: &v2.BlobHeader{ + Version: rand.Uint32(), + QuorumNumbers: quorumNumbers, + Commitment: &common.BlobCommitment{ + Commitment: rand.Bytes(32), + LengthCommitment: rand.Bytes(32), + LengthProof: rand.Bytes(32), + Length: rand.Uint32(), + }, + PaymentHeader: &common.PaymentHeader{ + AccountId: rand.String(32), + ReservationPeriod: rand.Uint32(), + CumulativePayment: rand.Bytes(32), + Salt: rand.Uint32(), + }, + Signature: rand.Bytes(32), + }, + Relays: relays, + } + } + + return &grpc.StoreChunksRequest{ + Batch: &v2.Batch{ + Header: &v2.BatchHeader{ + BatchRoot: rand.Bytes(32), + ReferenceBlockNumber: rand.Uint64(), + }, + BlobCertificates: blobCertificates, + }, + DisperserID: rand.Uint32(), + Signature: rand.Bytes(32), + } +} From d793501405fd5bcf72a05fff0c6deb26e1dad2ed Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Mon, 16 Dec 2024 16:06:20 -0600 Subject: [PATCH 29/94] Incremental progress in kludging something together Signed-off-by: Cody Littley --- api/clients/request_signer_test.go | 172 +++++++++++++++++++++++++---- 1 file changed, 148 insertions(+), 24 deletions(-) diff --git a/api/clients/request_signer_test.go b/api/clients/request_signer_test.go index c787c6ecb..f443c7eb9 100644 --- a/api/clients/request_signer_test.go +++ b/api/clients/request_signer_test.go @@ -3,7 +3,11 @@ package clients import ( "context" "crypto/ecdsa" - "crypto/x509" + "crypto/elliptic" + "crypto/x509/pkix" + "encoding/asn1" + "errors" + "fmt" "github.com/Layr-Labs/eigenda/common/testutils/random" "github.com/Layr-Labs/eigenda/inabox/deploy" "github.com/Layr-Labs/eigenda/node/auth" @@ -11,9 +15,12 @@ import ( "github.com/aws/aws-sdk-go-v2/service/kms" "github.com/aws/aws-sdk-go-v2/service/kms/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto/secp256k1" "github.com/ory/dockertest/v3" "github.com/stretchr/testify/require" + "golang.org/x/crypto/cryptobyte" "log" + "math/big" "os" "path/filepath" "runtime" @@ -66,6 +73,98 @@ func teardown() { } } +type publicKeyInfo struct { + Raw asn1.RawContent + Algorithm pkix.AlgorithmIdentifier + PublicKey asn1.BitString +} + +func ParsePublicKey(keyBytes []byte) (*ecdsa.PublicKey, error) { + pki := publicKeyInfo{} + rest, err := asn1.Unmarshal(keyBytes, &pki) + + if err != nil { + return nil, err + } + if len(rest) > 0 { + return nil, fmt.Errorf("trailing data after public key (%d bytes)", len(rest)) + } + + rightAlignedKey := cryptobyte.String(pki.PublicKey.RightAlign()) + + x, y := elliptic.Unmarshal(crypto.S256(), rightAlignedKey) // TODO deprecated method + if x == nil { + return nil, errors.New("x509: failed to unmarshal elliptic curve point") + } + + return &ecdsa.PublicKey{ + Curve: crypto.S256(), + X: x, + Y: y, + }, nil +} + +type signatureInfo struct { + R *big.Int + S *big.Int +} + +// TODO this was generated by AI, verify correctness and clean up +func ComputeRecoveryID(hash []byte, r, s *big.Int, pubKey *ecdsa.PublicKey) (int, error) { + rBytes := r.Bytes() + sBytes := s.Bytes() + + // Ensure R and S are 32 bytes each + rPadded := make([]byte, 32) + sPadded := make([]byte, 32) + copy(rPadded[32-len(rBytes):], rBytes) + copy(sPadded[32-len(sBytes):], sBytes) + + for v := 0; v < 2; v++ { + sig := append(rPadded, append(sPadded, byte(v))...) + recoveredPubKey, err := secp256k1.RecoverPubkey(hash, sig) + if err != nil { + return -1, fmt.Errorf("failed to recover public key: %w", err) + } + + x, y := elliptic.Unmarshal(secp256k1.S256(), recoveredPubKey) + if x.Cmp(pubKey.X) == 0 && y.Cmp(pubKey.Y) == 0 { + return v, nil + } + } + + return -1, fmt.Errorf("no valid recovery ID found") +} + +// ParseSignature parses a signature from AWS into eth format +func ParseSignature( + publicKey *ecdsa.PublicKey, + hash []byte, + signatureBytes []byte) ([]byte, error) { + + si := signatureInfo{} + rest, err := asn1.Unmarshal(signatureBytes, &si) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal signature: %w", err) + } + if len(rest) > 0 { + return nil, fmt.Errorf("trailing data after signature (%d bytes)", len(rest)) + } + + result := make([]byte, 65) + copy(result[0:32], si.R.Bytes()) + copy(result[32:64], si.S.Bytes()) + + recoveryID, err := ComputeRecoveryID(hash, si.R, si.S, publicKey) + if err != nil { + return nil, fmt.Errorf("failed to compute recovery ID: %w", err) + } + + result[64] = byte(recoveryID) + + return result, nil +} + func TestRequestSigning(t *testing.T) { rand := random.NewTestRandom(t) setup(t) @@ -75,36 +174,61 @@ func TestRequestSigning(t *testing.T) { Region: region, BaseEndpoint: aws.String(localstackHost), }) - createKeyOutput, err := keyManager.CreateKey(context.Background(), &kms.CreateKeyInput{ - KeySpec: types.KeySpecEccNistP256, - KeyUsage: types.KeyUsageTypeSignVerify, - }) - require.NoError(t, err) - keyID := *createKeyOutput.KeyMetadata.KeyId + for i := 0; i < 1000; i++ { + fmt.Printf("iteration %d\n", i) // TODO remove - getPublicKeyOutput, err := keyManager.GetPublicKey(context.Background(), &kms.GetPublicKeyInput{ - KeyId: aws.String(keyID), - }) - require.NoError(t, err) + createKeyOutput, err := keyManager.CreateKey(context.Background(), &kms.CreateKeyInput{ + KeySpec: types.KeySpecEccSecgP256k1, + KeyUsage: types.KeyUsageTypeSignVerify, + }) + require.NoError(t, err) - k, err := x509.ParsePKIXPublicKey(getPublicKeyOutput.PublicKey) - require.NoError(t, err) + keyID := *createKeyOutput.KeyMetadata.KeyId - publicKey := k.(*ecdsa.PublicKey) - publicAddress := crypto.PubkeyToAddress(*publicKey) + getPublicKeyOutput, err := keyManager.GetPublicKey(context.Background(), &kms.GetPublicKeyInput{ + KeyId: aws.String(keyID), + }) + require.NoError(t, err) + + key, err := ParsePublicKey(getPublicKeyOutput.PublicKey) + require.NoError(t, err) - request := auth.RandomStoreChunksRequest(rand) - request.Signature = nil + publicAddress := crypto.PubkeyToAddress(*key) - signer := NewRequestSigner(region, localstackHost, keyID) + request := auth.RandomStoreChunksRequest(rand) + request.Signature = nil + + signer := NewRequestSigner(region, localstackHost, keyID) + + signature, err := signer.SignStoreChunksRequest(context.Background(), request) + require.NoError(t, err) - signature, err := signer.SignStoreChunksRequest(context.Background(), request) - require.NoError(t, err) + // TODO this doesn't belong here + signature, err = ParseSignature( + key, + auth.HashStoreChunksRequest(request), + signature) - require.Nil(t, request.Signature) - request.Signature = signature + require.Nil(t, request.Signature) + request.Signature = signature - err = auth.VerifyStoreChunksRequest(publicAddress, request) - require.NoError(t, err) + err = auth.VerifyStoreChunksRequest(publicAddress, request) + require.NoError(t, err) + + // Changing a byte in the middle of the signature should make the verification fail + badSignature := make([]byte, len(signature)) + copy(badSignature, signature) + badSignature[10] = badSignature[10] + 1 + request.Signature = badSignature + err = auth.VerifyStoreChunksRequest(publicAddress, request) + require.Error(t, err) + + // Changing a byte in the middle of the request should make the verification fail + request.DisperserID = request.DisperserID + 1 + request.Signature = signature + err = auth.VerifyStoreChunksRequest(publicAddress, request) + require.Error(t, err) + + } } From b81e430016171eee322c5e536eb528df0a8a0eb4 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Mon, 16 Dec 2024 16:11:35 -0600 Subject: [PATCH 30/94] Incremental test iteration. Signed-off-by: Cody Littley --- api/clients/request_signer_test.go | 59 ++++++++++++++++-------------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/api/clients/request_signer_test.go b/api/clients/request_signer_test.go index f443c7eb9..14d8635e3 100644 --- a/api/clients/request_signer_test.go +++ b/api/clients/request_signer_test.go @@ -175,8 +175,8 @@ func TestRequestSigning(t *testing.T) { BaseEndpoint: aws.String(localstackHost), }) - for i := 0; i < 1000; i++ { - fmt.Printf("iteration %d\n", i) // TODO remove + for i := 0; i < 100; i++ { + fmt.Printf("key iteration %d\n", i) // TODO remove createKeyOutput, err := keyManager.CreateKey(context.Background(), &kms.CreateKeyInput{ KeySpec: types.KeySpecEccSecgP256k1, @@ -196,39 +196,42 @@ func TestRequestSigning(t *testing.T) { publicAddress := crypto.PubkeyToAddress(*key) - request := auth.RandomStoreChunksRequest(rand) - request.Signature = nil + for j := 0; j < 100; j++ { + fmt.Printf(" request iteration %d\n", j) // TODO remove - signer := NewRequestSigner(region, localstackHost, keyID) + request := auth.RandomStoreChunksRequest(rand) + request.Signature = nil - signature, err := signer.SignStoreChunksRequest(context.Background(), request) - require.NoError(t, err) + signer := NewRequestSigner(region, localstackHost, keyID) - // TODO this doesn't belong here - signature, err = ParseSignature( - key, - auth.HashStoreChunksRequest(request), - signature) + signature, err := signer.SignStoreChunksRequest(context.Background(), request) + require.NoError(t, err) - require.Nil(t, request.Signature) - request.Signature = signature + // TODO this doesn't belong here + signature, err = ParseSignature( + key, + auth.HashStoreChunksRequest(request), + signature) - err = auth.VerifyStoreChunksRequest(publicAddress, request) - require.NoError(t, err) + require.Nil(t, request.Signature) + request.Signature = signature - // Changing a byte in the middle of the signature should make the verification fail - badSignature := make([]byte, len(signature)) - copy(badSignature, signature) - badSignature[10] = badSignature[10] + 1 - request.Signature = badSignature - err = auth.VerifyStoreChunksRequest(publicAddress, request) - require.Error(t, err) + err = auth.VerifyStoreChunksRequest(publicAddress, request) + require.NoError(t, err) - // Changing a byte in the middle of the request should make the verification fail - request.DisperserID = request.DisperserID + 1 - request.Signature = signature - err = auth.VerifyStoreChunksRequest(publicAddress, request) - require.Error(t, err) + // Changing a byte in the middle of the signature should make the verification fail + badSignature := make([]byte, len(signature)) + copy(badSignature, signature) + badSignature[10] = badSignature[10] + 1 + request.Signature = badSignature + err = auth.VerifyStoreChunksRequest(publicAddress, request) + require.Error(t, err) + // Changing a byte in the middle of the request should make the verification fail + request.DisperserID = request.DisperserID + 1 + request.Signature = signature + err = auth.VerifyStoreChunksRequest(publicAddress, request) + require.Error(t, err) + } } } From 7327db1c4fde418bc9474b51322a343f8faee916 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Tue, 17 Dec 2024 08:53:57 -0600 Subject: [PATCH 31/94] Added debug printing. Signed-off-by: Cody Littley --- api/clients/request_signer_test.go | 17 +++++++++++++---- node/auth/request_signing.go | 2 ++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/api/clients/request_signer_test.go b/api/clients/request_signer_test.go index 14d8635e3..8dc1545ac 100644 --- a/api/clients/request_signer_test.go +++ b/api/clients/request_signer_test.go @@ -92,7 +92,7 @@ func ParsePublicKey(keyBytes []byte) (*ecdsa.PublicKey, error) { rightAlignedKey := cryptobyte.String(pki.PublicKey.RightAlign()) - x, y := elliptic.Unmarshal(crypto.S256(), rightAlignedKey) // TODO deprecated method + x, y := elliptic.Unmarshal(crypto.S256(), rightAlignedKey) if x == nil { return nil, errors.New("x509: failed to unmarshal elliptic curve point") } @@ -115,13 +115,20 @@ func ComputeRecoveryID(hash []byte, r, s *big.Int, pubKey *ecdsa.PublicKey) (int sBytes := s.Bytes() // Ensure R and S are 32 bytes each + fmt.Printf(" len of rBytes: %d, %x\n", len(rBytes), rBytes) // TODO remove + fmt.Printf(" len of sBytes: %d, %x\n", len(sBytes), sBytes) // TODO remove + + // TODO necessary? rPadded := make([]byte, 32) sPadded := make([]byte, 32) copy(rPadded[32-len(rBytes):], rBytes) copy(sPadded[32-len(sBytes):], sBytes) - for v := 0; v < 2; v++ { - sig := append(rPadded, append(sPadded, byte(v))...) + for v := 0; v < 4; v++ { + sig := make([]byte, 65) + copy(sig[0:32], rPadded) + copy(sig[32:64], sPadded) + sig[64] = byte(v) recoveredPubKey, err := secp256k1.RecoverPubkey(hash, sig) if err != nil { return -1, fmt.Errorf("failed to recover public key: %w", err) @@ -204,6 +211,9 @@ func TestRequestSigning(t *testing.T) { signer := NewRequestSigner(region, localstackHost, keyID) + fmt.Printf(" >public key: %x\n", key) // TODO remove + + // Test a valid signature. signature, err := signer.SignStoreChunksRequest(context.Background(), request) require.NoError(t, err) @@ -215,7 +225,6 @@ func TestRequestSigning(t *testing.T) { require.Nil(t, request.Signature) request.Signature = signature - err = auth.VerifyStoreChunksRequest(publicAddress, request) require.NoError(t, err) diff --git a/node/auth/request_signing.go b/node/auth/request_signing.go index 24ca80b8b..da09c9933 100644 --- a/node/auth/request_signing.go +++ b/node/auth/request_signing.go @@ -36,6 +36,8 @@ func VerifyStoreChunksRequest(key gethcommon.Address, request *grpc.StoreChunksR return fmt.Errorf("failed to recover public key from signature: %w", err) } + fmt.Printf(" public key: %v\n", signingPublicKey) + signingAddress := crypto.PubkeyToAddress(*signingPublicKey) if key.Cmp(signingAddress) != 0 { From 8cfa9716d457bbd76e420672ac5461562dc6ca2e Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Tue, 17 Dec 2024 08:59:17 -0600 Subject: [PATCH 32/94] IT'S ALIVE! Signed-off-by: Cody Littley --- api/clients/request_signer_test.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/api/clients/request_signer_test.go b/api/clients/request_signer_test.go index 8dc1545ac..40d44525e 100644 --- a/api/clients/request_signer_test.go +++ b/api/clients/request_signer_test.go @@ -158,9 +158,16 @@ func ParseSignature( return nil, fmt.Errorf("trailing data after signature (%d bytes)", len(rest)) } + rBytes := si.R.Bytes() + sBytes := si.S.Bytes() + rPadded := make([]byte, 32) + sPadded := make([]byte, 32) + copy(rPadded[32-len(rBytes):], rBytes) + copy(sPadded[32-len(sBytes):], sBytes) + result := make([]byte, 65) - copy(result[0:32], si.R.Bytes()) - copy(result[32:64], si.S.Bytes()) + copy(result[0:32], rPadded) + copy(result[32:64], sPadded) recoveryID, err := ComputeRecoveryID(hash, si.R, si.S, publicKey) if err != nil { From 306746ecc713b9e20090f5e9a27ce1713c57896b Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Tue, 17 Dec 2024 09:13:13 -0600 Subject: [PATCH 33/94] Partial cleanup. Signed-off-by: Cody Littley --- api/clients/request_signer_test.go | 67 +++++++++++------------------- node/auth/request_signing.go | 2 - 2 files changed, 25 insertions(+), 44 deletions(-) diff --git a/api/clients/request_signer_test.go b/api/clients/request_signer_test.go index 40d44525e..ff761f140 100644 --- a/api/clients/request_signer_test.go +++ b/api/clients/request_signer_test.go @@ -109,38 +109,33 @@ type signatureInfo struct { S *big.Int } -// TODO this was generated by AI, verify correctness and clean up -func ComputeRecoveryID(hash []byte, r, s *big.Int, pubKey *ecdsa.PublicKey) (int, error) { - rBytes := r.Bytes() - sBytes := s.Bytes() - - // Ensure R and S are 32 bytes each - fmt.Printf(" len of rBytes: %d, %x\n", len(rBytes), rBytes) // TODO remove - fmt.Printf(" len of sBytes: %d, %x\n", len(sBytes), sBytes) // TODO remove - - // TODO necessary? - rPadded := make([]byte, 32) - sPadded := make([]byte, 32) - copy(rPadded[32-len(rBytes):], rBytes) - copy(sPadded[32-len(sBytes):], sBytes) - +// AddRecoveryID computes the recovery ID for a given signature and public key and adds it to the signature. +func AddRecoveryID(hash []byte, pubKey *ecdsa.PublicKey, partialSignature []byte) error { for v := 0; v < 4; v++ { - sig := make([]byte, 65) - copy(sig[0:32], rPadded) - copy(sig[32:64], sPadded) - sig[64] = byte(v) - recoveredPubKey, err := secp256k1.RecoverPubkey(hash, sig) + partialSignature[64] = byte(v) + recoveredPubKey, err := secp256k1.RecoverPubkey(hash, partialSignature) if err != nil { - return -1, fmt.Errorf("failed to recover public key: %w", err) + return fmt.Errorf("failed to recover public key: %w", err) } x, y := elliptic.Unmarshal(secp256k1.S256(), recoveredPubKey) if x.Cmp(pubKey.X) == 0 && y.Cmp(pubKey.Y) == 0 { - return v, nil + return nil } } - return -1, fmt.Errorf("no valid recovery ID found") + return fmt.Errorf("no valid recovery ID found") +} + +// pad32 pads a byte slice to 32 bytes, inserting zeros at the beginning if necessary. +func pad32(bytes []byte) []byte { + if len(bytes) == 32 { + return bytes + } + + padded := make([]byte, 32) + copy(padded[32-len(bytes):], bytes) + return padded } // ParseSignature parses a signature from AWS into eth format @@ -158,24 +153,18 @@ func ParseSignature( return nil, fmt.Errorf("trailing data after signature (%d bytes)", len(rest)) } - rBytes := si.R.Bytes() - sBytes := si.S.Bytes() - rPadded := make([]byte, 32) - sPadded := make([]byte, 32) - copy(rPadded[32-len(rBytes):], rBytes) - copy(sPadded[32-len(sBytes):], sBytes) + rBytes := pad32(si.R.Bytes()) + sBytes := pad32(si.S.Bytes()) result := make([]byte, 65) - copy(result[0:32], rPadded) - copy(result[32:64], sPadded) + copy(result[0:32], rBytes) + copy(result[32:64], sBytes) - recoveryID, err := ComputeRecoveryID(hash, si.R, si.S, publicKey) + err = AddRecoveryID(hash, publicKey, result) if err != nil { return nil, fmt.Errorf("failed to compute recovery ID: %w", err) } - result[64] = byte(recoveryID) - return result, nil } @@ -189,9 +178,7 @@ func TestRequestSigning(t *testing.T) { BaseEndpoint: aws.String(localstackHost), }) - for i := 0; i < 100; i++ { - fmt.Printf("key iteration %d\n", i) // TODO remove - + for i := 0; i < 10; i++ { createKeyOutput, err := keyManager.CreateKey(context.Background(), &kms.CreateKeyInput{ KeySpec: types.KeySpecEccSecgP256k1, KeyUsage: types.KeyUsageTypeSignVerify, @@ -210,16 +197,12 @@ func TestRequestSigning(t *testing.T) { publicAddress := crypto.PubkeyToAddress(*key) - for j := 0; j < 100; j++ { - fmt.Printf(" request iteration %d\n", j) // TODO remove - + for j := 0; j < 10; j++ { request := auth.RandomStoreChunksRequest(rand) request.Signature = nil signer := NewRequestSigner(region, localstackHost, keyID) - fmt.Printf(" >public key: %x\n", key) // TODO remove - // Test a valid signature. signature, err := signer.SignStoreChunksRequest(context.Background(), request) require.NoError(t, err) diff --git a/node/auth/request_signing.go b/node/auth/request_signing.go index da09c9933..24ca80b8b 100644 --- a/node/auth/request_signing.go +++ b/node/auth/request_signing.go @@ -36,8 +36,6 @@ func VerifyStoreChunksRequest(key gethcommon.Address, request *grpc.StoreChunksR return fmt.Errorf("failed to recover public key from signature: %w", err) } - fmt.Printf(" public key: %v\n", signingPublicKey) - signingAddress := crypto.PubkeyToAddress(*signingPublicKey) if key.Cmp(signingAddress) != 0 { From b41d72952b002098affa3d0e4d94209a7d6980cc Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Tue, 17 Dec 2024 09:27:16 -0600 Subject: [PATCH 34/94] Move code to proper locations. Signed-off-by: Cody Littley --- api/clients/request_signer.go | 27 ++++++- api/clients/request_signer_test.go | 115 +---------------------------- node/auth/request_signing.go | 105 ++++++++++++++++++++++++++ 3 files changed, 132 insertions(+), 115 deletions(-) diff --git a/api/clients/request_signer.go b/api/clients/request_signer.go index eecc2ce5b..67da5d4b8 100644 --- a/api/clients/request_signer.go +++ b/api/clients/request_signer.go @@ -2,6 +2,7 @@ package clients import ( "context" + "crypto/ecdsa" "fmt" grpc "github.com/Layr-Labs/eigenda/api/grpc/node/v2" "github.com/Layr-Labs/eigenda/node/auth" @@ -21,24 +22,39 @@ var _ RequestSigner = &requestSigner{} type requestSigner struct { keyID string + publicKey *ecdsa.PublicKey keyManager *kms.Client } // NewRequestSigner creates a new RequestSigner. func NewRequestSigner( + ctx context.Context, region string, endpoint string, - keyID string) RequestSigner { + keyID string) (RequestSigner, error) { keyManager := kms.New(kms.Options{ Region: region, BaseEndpoint: aws.String(endpoint), }) + getPublicKeyOutput, err := keyManager.GetPublicKey(ctx, &kms.GetPublicKeyInput{ + KeyId: aws.String(keyID), + }) + if err != nil { + return nil, fmt.Errorf("failed to get public key: %w", err) + } + + publicKey, err := auth.ParseKMSPublicKey(getPublicKeyOutput.PublicKey) + if err != nil { + return nil, fmt.Errorf("failed to parse public key: %w", err) + } + return &requestSigner{ keyID: keyID, + publicKey: publicKey, keyManager: keyManager, - } + }, nil } func (s *requestSigner) SignStoreChunksRequest(ctx context.Context, request *grpc.StoreChunksRequest) ([]byte, error) { @@ -57,5 +73,10 @@ func (s *requestSigner) SignStoreChunksRequest(ctx context.Context, request *grp return nil, fmt.Errorf("failed to sign request: %w", err) } - return signOutput.Signature, nil + signature, err := auth.ParseKMSSignature(s.publicKey, hash, signOutput.Signature) + if err != nil { + return nil, fmt.Errorf("failed to parse signature: %w", err) + } + + return signature, nil } diff --git a/api/clients/request_signer_test.go b/api/clients/request_signer_test.go index ff761f140..9862dbf12 100644 --- a/api/clients/request_signer_test.go +++ b/api/clients/request_signer_test.go @@ -2,12 +2,6 @@ package clients import ( "context" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/x509/pkix" - "encoding/asn1" - "errors" - "fmt" "github.com/Layr-Labs/eigenda/common/testutils/random" "github.com/Layr-Labs/eigenda/inabox/deploy" "github.com/Layr-Labs/eigenda/node/auth" @@ -15,12 +9,9 @@ import ( "github.com/aws/aws-sdk-go-v2/service/kms" "github.com/aws/aws-sdk-go-v2/service/kms/types" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/crypto/secp256k1" "github.com/ory/dockertest/v3" "github.com/stretchr/testify/require" - "golang.org/x/crypto/cryptobyte" "log" - "math/big" "os" "path/filepath" "runtime" @@ -73,101 +64,6 @@ func teardown() { } } -type publicKeyInfo struct { - Raw asn1.RawContent - Algorithm pkix.AlgorithmIdentifier - PublicKey asn1.BitString -} - -func ParsePublicKey(keyBytes []byte) (*ecdsa.PublicKey, error) { - pki := publicKeyInfo{} - rest, err := asn1.Unmarshal(keyBytes, &pki) - - if err != nil { - return nil, err - } - if len(rest) > 0 { - return nil, fmt.Errorf("trailing data after public key (%d bytes)", len(rest)) - } - - rightAlignedKey := cryptobyte.String(pki.PublicKey.RightAlign()) - - x, y := elliptic.Unmarshal(crypto.S256(), rightAlignedKey) - if x == nil { - return nil, errors.New("x509: failed to unmarshal elliptic curve point") - } - - return &ecdsa.PublicKey{ - Curve: crypto.S256(), - X: x, - Y: y, - }, nil -} - -type signatureInfo struct { - R *big.Int - S *big.Int -} - -// AddRecoveryID computes the recovery ID for a given signature and public key and adds it to the signature. -func AddRecoveryID(hash []byte, pubKey *ecdsa.PublicKey, partialSignature []byte) error { - for v := 0; v < 4; v++ { - partialSignature[64] = byte(v) - recoveredPubKey, err := secp256k1.RecoverPubkey(hash, partialSignature) - if err != nil { - return fmt.Errorf("failed to recover public key: %w", err) - } - - x, y := elliptic.Unmarshal(secp256k1.S256(), recoveredPubKey) - if x.Cmp(pubKey.X) == 0 && y.Cmp(pubKey.Y) == 0 { - return nil - } - } - - return fmt.Errorf("no valid recovery ID found") -} - -// pad32 pads a byte slice to 32 bytes, inserting zeros at the beginning if necessary. -func pad32(bytes []byte) []byte { - if len(bytes) == 32 { - return bytes - } - - padded := make([]byte, 32) - copy(padded[32-len(bytes):], bytes) - return padded -} - -// ParseSignature parses a signature from AWS into eth format -func ParseSignature( - publicKey *ecdsa.PublicKey, - hash []byte, - signatureBytes []byte) ([]byte, error) { - - si := signatureInfo{} - rest, err := asn1.Unmarshal(signatureBytes, &si) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal signature: %w", err) - } - if len(rest) > 0 { - return nil, fmt.Errorf("trailing data after signature (%d bytes)", len(rest)) - } - - rBytes := pad32(si.R.Bytes()) - sBytes := pad32(si.S.Bytes()) - - result := make([]byte, 65) - copy(result[0:32], rBytes) - copy(result[32:64], sBytes) - - err = AddRecoveryID(hash, publicKey, result) - if err != nil { - return nil, fmt.Errorf("failed to compute recovery ID: %w", err) - } - - return result, nil -} - func TestRequestSigning(t *testing.T) { rand := random.NewTestRandom(t) setup(t) @@ -192,7 +88,7 @@ func TestRequestSigning(t *testing.T) { }) require.NoError(t, err) - key, err := ParsePublicKey(getPublicKeyOutput.PublicKey) + key, err := auth.ParseKMSPublicKey(getPublicKeyOutput.PublicKey) require.NoError(t, err) publicAddress := crypto.PubkeyToAddress(*key) @@ -201,18 +97,13 @@ func TestRequestSigning(t *testing.T) { request := auth.RandomStoreChunksRequest(rand) request.Signature = nil - signer := NewRequestSigner(region, localstackHost, keyID) + signer, err := NewRequestSigner(context.Background(), region, localstackHost, keyID) + require.NoError(t, err) // Test a valid signature. signature, err := signer.SignStoreChunksRequest(context.Background(), request) require.NoError(t, err) - // TODO this doesn't belong here - signature, err = ParseSignature( - key, - auth.HashStoreChunksRequest(request), - signature) - require.Nil(t, request.Signature) request.Signature = signature err = auth.VerifyStoreChunksRequest(publicAddress, request) diff --git a/node/auth/request_signing.go b/node/auth/request_signing.go index 24ca80b8b..2815a3031 100644 --- a/node/auth/request_signing.go +++ b/node/auth/request_signing.go @@ -2,17 +2,122 @@ package auth import ( "crypto/ecdsa" + "crypto/elliptic" + "crypto/x509/pkix" + "encoding/asn1" "encoding/binary" + "errors" "fmt" commonv1 "github.com/Layr-Labs/eigenda/api/grpc/common" common "github.com/Layr-Labs/eigenda/api/grpc/common/v2" grpc "github.com/Layr-Labs/eigenda/api/grpc/node/v2" gethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto/secp256k1" + "golang.org/x/crypto/cryptobyte" "golang.org/x/crypto/sha3" "hash" + "math/big" ) +type signatureInfo struct { + R *big.Int + S *big.Int +} + +// AddRecoveryID computes the recovery ID for a given signature and public key and adds it to the signature. +func addRecoveryID(hash []byte, pubKey *ecdsa.PublicKey, partialSignature []byte) error { + for v := 0; v < 4; v++ { + partialSignature[64] = byte(v) + recoveredPubKey, err := secp256k1.RecoverPubkey(hash, partialSignature) + if err != nil { + return fmt.Errorf("failed to recover public key: %w", err) + } + + x, y := elliptic.Unmarshal(secp256k1.S256(), recoveredPubKey) + if x.Cmp(pubKey.X) == 0 && y.Cmp(pubKey.Y) == 0 { + return nil + } + } + + return fmt.Errorf("no valid recovery ID found") +} + +// pad32 pads a byte slice to 32 bytes, inserting zeros at the beginning if necessary. +func pad32(bytes []byte) []byte { + if len(bytes) == 32 { + return bytes + } + + padded := make([]byte, 32) + copy(padded[32-len(bytes):], bytes) + return padded +} + +// ParseKMSSignature parses a signature (KeySpecEccSecgP256k1) in the format returned by amazon KMS into the +// 65-byte format used by Ethereum. +func ParseKMSSignature( + publicKey *ecdsa.PublicKey, + hash []byte, + signatureBytes []byte) ([]byte, error) { + + si := signatureInfo{} + rest, err := asn1.Unmarshal(signatureBytes, &si) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal signature: %w", err) + } + if len(rest) > 0 { + return nil, fmt.Errorf("trailing data after signature (%d bytes)", len(rest)) + } + + rBytes := pad32(si.R.Bytes()) + sBytes := pad32(si.S.Bytes()) + + result := make([]byte, 65) + copy(result[0:32], rBytes) + copy(result[32:64], sBytes) + + err = addRecoveryID(hash, publicKey, result) + if err != nil { + return nil, fmt.Errorf("failed to compute recovery ID: %w", err) + } + + return result, nil +} + +type publicKeyInfo struct { + Raw asn1.RawContent + Algorithm pkix.AlgorithmIdentifier + PublicKey asn1.BitString +} + +// ParseKMSPublicKey parses a public key (KeySpecEccSecgP256k1) in the format returned by amazon KMS +// into an ecdsa.PublicKey. +func ParseKMSPublicKey(keyBytes []byte) (*ecdsa.PublicKey, error) { + pki := publicKeyInfo{} + rest, err := asn1.Unmarshal(keyBytes, &pki) + + if err != nil { + return nil, err + } + if len(rest) > 0 { + return nil, fmt.Errorf("trailing data after public key (%d bytes)", len(rest)) + } + + rightAlignedKey := cryptobyte.String(pki.PublicKey.RightAlign()) + + x, y := elliptic.Unmarshal(crypto.S256(), rightAlignedKey) + if x == nil { + return nil, errors.New("x509: failed to unmarshal elliptic curve point") + } + + return &ecdsa.PublicKey{ + Curve: crypto.S256(), + X: x, + Y: y, + }, nil +} + // SignStoreChunksRequest signs the given StoreChunksRequest with the given private key. Does not // write the signature into the request. func SignStoreChunksRequest(key *ecdsa.PrivateKey, request *grpc.StoreChunksRequest) ([]byte, error) { From d709520e27190a50b68aeafed47d8ce8c290d44b Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Tue, 17 Dec 2024 15:08:53 -0600 Subject: [PATCH 35/94] Copy eigensdk-go implementation of kms parsing. Signed-off-by: Cody Littley --- api/clients/request_signer.go | 30 +----- api/clients/request_signer_test.go | 8 +- common/kms.go | 158 ++++++++++++++++++++++++++++ contracts/lib/eigenlayer-middleware | 2 +- disperser/cmd/controller/main.go | 6 +- node/auth/request_signing.go | 105 ------------------ 6 files changed, 171 insertions(+), 138 deletions(-) create mode 100644 common/kms.go diff --git a/api/clients/request_signer.go b/api/clients/request_signer.go index 67da5d4b8..645a2ab5b 100644 --- a/api/clients/request_signer.go +++ b/api/clients/request_signer.go @@ -5,10 +5,10 @@ import ( "crypto/ecdsa" "fmt" grpc "github.com/Layr-Labs/eigenda/api/grpc/node/v2" + "github.com/Layr-Labs/eigenda/common" "github.com/Layr-Labs/eigenda/node/auth" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/kms" - "github.com/aws/aws-sdk-go-v2/service/kms/types" ) // RequestSigner encapsulates the logic for signing GetChunks requests. @@ -38,21 +38,14 @@ func NewRequestSigner( BaseEndpoint: aws.String(endpoint), }) - getPublicKeyOutput, err := keyManager.GetPublicKey(ctx, &kms.GetPublicKeyInput{ - KeyId: aws.String(keyID), - }) - if err != nil { - return nil, fmt.Errorf("failed to get public key: %w", err) - } - - publicKey, err := auth.ParseKMSPublicKey(getPublicKeyOutput.PublicKey) + key, err := common.LoadPublicKeyKMS(ctx, keyManager, keyID) if err != nil { - return nil, fmt.Errorf("failed to parse public key: %w", err) + return nil, fmt.Errorf("failed to get ecdsa public key: %w", err) } return &requestSigner{ keyID: keyID, - publicKey: publicKey, + publicKey: key, keyManager: keyManager, }, nil } @@ -60,23 +53,10 @@ func NewRequestSigner( func (s *requestSigner) SignStoreChunksRequest(ctx context.Context, request *grpc.StoreChunksRequest) ([]byte, error) { hash := auth.HashStoreChunksRequest(request) - signOutput, err := s.keyManager.Sign( - ctx, - &kms.SignInput{ - KeyId: aws.String(s.keyID), - Message: hash, - SigningAlgorithm: types.SigningAlgorithmSpecEcdsaSha256, - MessageType: types.MessageTypeDigest, - }) - + signature, err := common.SignKMS(ctx, s.keyManager, s.keyID, s.publicKey, hash) if err != nil { return nil, fmt.Errorf("failed to sign request: %w", err) } - signature, err := auth.ParseKMSSignature(s.publicKey, hash, signOutput.Signature) - if err != nil { - return nil, fmt.Errorf("failed to parse signature: %w", err) - } - return signature, nil } diff --git a/api/clients/request_signer_test.go b/api/clients/request_signer_test.go index 9862dbf12..98452f457 100644 --- a/api/clients/request_signer_test.go +++ b/api/clients/request_signer_test.go @@ -2,6 +2,7 @@ package clients import ( "context" + "github.com/Layr-Labs/eigenda/common" "github.com/Layr-Labs/eigenda/common/testutils/random" "github.com/Layr-Labs/eigenda/inabox/deploy" "github.com/Layr-Labs/eigenda/node/auth" @@ -83,12 +84,7 @@ func TestRequestSigning(t *testing.T) { keyID := *createKeyOutput.KeyMetadata.KeyId - getPublicKeyOutput, err := keyManager.GetPublicKey(context.Background(), &kms.GetPublicKeyInput{ - KeyId: aws.String(keyID), - }) - require.NoError(t, err) - - key, err := auth.ParseKMSPublicKey(getPublicKeyOutput.PublicKey) + key, err := common.LoadPublicKeyKMS(context.Background(), keyManager, keyID) require.NoError(t, err) publicAddress := crypto.PubkeyToAddress(*key) diff --git a/common/kms.go b/common/kms.go new file mode 100644 index 000000000..e61e09b6d --- /dev/null +++ b/common/kms.go @@ -0,0 +1,158 @@ +package common + +import ( + "bytes" + "context" + "crypto/ecdsa" + "encoding/asn1" + "encoding/hex" + "errors" + "fmt" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/kms" + "github.com/aws/aws-sdk-go-v2/service/kms/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto/secp256k1" + "math/big" +) + +// This file contains utility methods for working with AWS KMS using ecdsa on the KeySpecEccSecgP256k1 curve. +// This code was adapted from code in https://github.com/Layr-Labs/eigensdk-go/tree/dev/signerv2 + +var secp256k1N = crypto.S256().Params().N +var secp256k1HalfN = new(big.Int).Div(secp256k1N, big.NewInt(2)) + +type asn1EcPublicKey struct { + EcPublicKeyInfo asn1EcPublicKeyInfo + PublicKey asn1.BitString +} + +type asn1EcPublicKeyInfo struct { + Algorithm asn1.ObjectIdentifier + Parameters asn1.ObjectIdentifier +} + +type asn1EcSig struct { + R asn1.RawValue + S asn1.RawValue +} + +// LoadPublicKeyKMS loads the public key from AWS KMS. +func LoadPublicKeyKMS( + ctx context.Context, + client *kms.Client, + keyId string) (*ecdsa.PublicKey, error) { + + getPubKeyOutput, err := client.GetPublicKey(ctx, &kms.GetPublicKeyInput{ + KeyId: aws.String(keyId), + }) + if err != nil { + return nil, fmt.Errorf("failed to get public key for KeyId=%s: %w", keyId, err) + } + + key, err := ParsePublicKeyKMS(getPubKeyOutput.PublicKey) + if err != nil { + return nil, fmt.Errorf("failed to parse public key for KeyId=%s: %w", keyId, err) + } + + return key, nil +} + +// ParsePublicKeyKMS parses the public key from AWS KMS format into an ecdsa.PublicKey. +func ParsePublicKeyKMS(bytes []byte) (*ecdsa.PublicKey, error) { + var asn1pubk asn1EcPublicKey + _, err := asn1.Unmarshal(bytes, &asn1pubk) + if err != nil { + return nil, fmt.Errorf("asn1.Uunmarshal failed: %w", err) + } + + key, err := crypto.UnmarshalPubkey(asn1pubk.PublicKey.Bytes) + if err != nil { + return nil, fmt.Errorf("crypto.UnmarshalPubkey failed: %w", err) + } + + return key, nil +} + +func adjustSignatureLength(buffer []byte) []byte { + buffer = bytes.TrimLeft(buffer, "\x00") + for len(buffer) < 32 { + zeroBuf := []byte{0} + buffer = append(zeroBuf, buffer...) + } + return buffer +} + +// SignKMS signs a hash using the provided public using AWS KMS. +// The signature is returned in the 65-byte format used by Ethereum. +func SignKMS( + ctx context.Context, + client *kms.Client, + keyId string, + publicKey *ecdsa.PublicKey, + hash []byte) ([]byte, error) { + + signOutput, err := client.Sign(ctx, &kms.SignInput{ + KeyId: aws.String(keyId), + SigningAlgorithm: types.SigningAlgorithmSpecEcdsaSha256, + MessageType: types.MessageTypeDigest, + Message: hash, + }) + if err != nil { + return nil, fmt.Errorf("failed to sign hash: %w", err) + } + + signature, err := ParseSignatureKMS(publicKey, hash, signOutput.Signature) + if err != nil { + return nil, fmt.Errorf("failed to parse signature: %w", err) + } + + return signature, nil +} + +// ParseSignatureKMS parses a signature (KeySpecEccSecgP256k1) in the format returned by amazon KMS into the +// 65-byte format used by Ethereum. +func ParseSignatureKMS( + publicKey *ecdsa.PublicKey, + hash []byte, + bytes []byte) ([]byte, error) { + + publicKeyBytes := secp256k1.S256().Marshal(publicKey.X, publicKey.Y) + + var sigAsn1 asn1EcSig + _, err := asn1.Unmarshal(bytes, &sigAsn1) + if err != nil { + return nil, fmt.Errorf("asn1.Unmarshal failed: %w", err) + } + + r := sigAsn1.R.Bytes + s := sigAsn1.S.Bytes + + // Adjust S value from signature according to Ethereum standard + sBigInt := new(big.Int).SetBytes(s) + if sBigInt.Cmp(secp256k1HalfN) > 0 { + s = new(big.Int).Sub(secp256k1N, sBigInt).Bytes() + } + + rsSignature := append(adjustSignatureLength(r), adjustSignatureLength(s)...) + signature := append(rsSignature, []byte{0}...) + + recoveredPublicKeyBytes, err := crypto.Ecrecover(hash, signature) + if err != nil { + return nil, err + } + + if hex.EncodeToString(recoveredPublicKeyBytes) != hex.EncodeToString(publicKeyBytes) { + signature = append(rsSignature, []byte{1}...) + recoveredPublicKeyBytes, err = crypto.Ecrecover(hash, signature) + if err != nil { + return nil, err + } + + if hex.EncodeToString(recoveredPublicKeyBytes) != hex.EncodeToString(publicKeyBytes) { + return nil, errors.New("can not reconstruct public key from sig") + } + } + + return signature, nil +} diff --git a/contracts/lib/eigenlayer-middleware b/contracts/lib/eigenlayer-middleware index b42aa0b7a..91400d977 160000 --- a/contracts/lib/eigenlayer-middleware +++ b/contracts/lib/eigenlayer-middleware @@ -1 +1 @@ -Subproject commit b42aa0b7aefb5b30721c3f4b9ad7ab02e8215648 +Subproject commit 91400d9776d4f7276a2807a53559f49f7edf5378 diff --git a/disperser/cmd/controller/main.go b/disperser/cmd/controller/main.go index 8e8b18782..d726242d2 100644 --- a/disperser/cmd/controller/main.go +++ b/disperser/cmd/controller/main.go @@ -150,10 +150,14 @@ func RunController(ctx *cli.Context) error { } } - requestSigner := clients.NewRequestSigner( + requestSigner, err := clients.NewRequestSigner( + context.Background(), config.AwsClientConfig.Region, config.AwsClientConfig.EndpointURL, config.DisperserSigningKeyName) + if err != nil { + return fmt.Errorf("failed to create request signer: %v", err) + } nodeClientManager, err := controller.NewNodeClientManager(config.NodeClientCacheSize, requestSigner, logger) if err != nil { diff --git a/node/auth/request_signing.go b/node/auth/request_signing.go index 2815a3031..24ca80b8b 100644 --- a/node/auth/request_signing.go +++ b/node/auth/request_signing.go @@ -2,122 +2,17 @@ package auth import ( "crypto/ecdsa" - "crypto/elliptic" - "crypto/x509/pkix" - "encoding/asn1" "encoding/binary" - "errors" "fmt" commonv1 "github.com/Layr-Labs/eigenda/api/grpc/common" common "github.com/Layr-Labs/eigenda/api/grpc/common/v2" grpc "github.com/Layr-Labs/eigenda/api/grpc/node/v2" gethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/crypto/secp256k1" - "golang.org/x/crypto/cryptobyte" "golang.org/x/crypto/sha3" "hash" - "math/big" ) -type signatureInfo struct { - R *big.Int - S *big.Int -} - -// AddRecoveryID computes the recovery ID for a given signature and public key and adds it to the signature. -func addRecoveryID(hash []byte, pubKey *ecdsa.PublicKey, partialSignature []byte) error { - for v := 0; v < 4; v++ { - partialSignature[64] = byte(v) - recoveredPubKey, err := secp256k1.RecoverPubkey(hash, partialSignature) - if err != nil { - return fmt.Errorf("failed to recover public key: %w", err) - } - - x, y := elliptic.Unmarshal(secp256k1.S256(), recoveredPubKey) - if x.Cmp(pubKey.X) == 0 && y.Cmp(pubKey.Y) == 0 { - return nil - } - } - - return fmt.Errorf("no valid recovery ID found") -} - -// pad32 pads a byte slice to 32 bytes, inserting zeros at the beginning if necessary. -func pad32(bytes []byte) []byte { - if len(bytes) == 32 { - return bytes - } - - padded := make([]byte, 32) - copy(padded[32-len(bytes):], bytes) - return padded -} - -// ParseKMSSignature parses a signature (KeySpecEccSecgP256k1) in the format returned by amazon KMS into the -// 65-byte format used by Ethereum. -func ParseKMSSignature( - publicKey *ecdsa.PublicKey, - hash []byte, - signatureBytes []byte) ([]byte, error) { - - si := signatureInfo{} - rest, err := asn1.Unmarshal(signatureBytes, &si) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal signature: %w", err) - } - if len(rest) > 0 { - return nil, fmt.Errorf("trailing data after signature (%d bytes)", len(rest)) - } - - rBytes := pad32(si.R.Bytes()) - sBytes := pad32(si.S.Bytes()) - - result := make([]byte, 65) - copy(result[0:32], rBytes) - copy(result[32:64], sBytes) - - err = addRecoveryID(hash, publicKey, result) - if err != nil { - return nil, fmt.Errorf("failed to compute recovery ID: %w", err) - } - - return result, nil -} - -type publicKeyInfo struct { - Raw asn1.RawContent - Algorithm pkix.AlgorithmIdentifier - PublicKey asn1.BitString -} - -// ParseKMSPublicKey parses a public key (KeySpecEccSecgP256k1) in the format returned by amazon KMS -// into an ecdsa.PublicKey. -func ParseKMSPublicKey(keyBytes []byte) (*ecdsa.PublicKey, error) { - pki := publicKeyInfo{} - rest, err := asn1.Unmarshal(keyBytes, &pki) - - if err != nil { - return nil, err - } - if len(rest) > 0 { - return nil, fmt.Errorf("trailing data after public key (%d bytes)", len(rest)) - } - - rightAlignedKey := cryptobyte.String(pki.PublicKey.RightAlign()) - - x, y := elliptic.Unmarshal(crypto.S256(), rightAlignedKey) - if x == nil { - return nil, errors.New("x509: failed to unmarshal elliptic curve point") - } - - return &ecdsa.PublicKey{ - Curve: crypto.S256(), - X: x, - Y: y, - }, nil -} - // SignStoreChunksRequest signs the given StoreChunksRequest with the given private key. Does not // write the signature into the request. func SignStoreChunksRequest(key *ecdsa.PrivateKey, request *grpc.StoreChunksRequest) ([]byte, error) { From 2e44b02e8777c32b7f6fccb0044487b7980a374e Mon Sep 17 00:00:00 2001 From: anupsv Date: Wed, 18 Dec 2024 18:55:07 +0530 Subject: [PATCH 36/94] Update kms.go --- common/kms.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/common/kms.go b/common/kms.go index e61e09b6d..bec69ab30 100644 --- a/common/kms.go +++ b/common/kms.go @@ -75,6 +75,11 @@ func ParsePublicKeyKMS(bytes []byte) (*ecdsa.PublicKey, error) { } func adjustSignatureLength(buffer []byte) []byte { + + if len(buffer) > 32 { + buffer = buffer[len(buffer)-32:] // Take last 32 bytes + } + buffer = bytes.TrimLeft(buffer, "\x00") for len(buffer) < 32 { zeroBuf := []byte{0} @@ -117,6 +122,10 @@ func ParseSignatureKMS( hash []byte, bytes []byte) ([]byte, error) { + if !secp256k1.S256().IsOnCurve(publicKey.X, publicKey.Y) { + return nil, errors.New("public key is not on curve") + } + publicKeyBytes := secp256k1.S256().Marshal(publicKey.X, publicKey.Y) var sigAsn1 asn1EcSig From aebda81c1bd99f236eddaee338c41b3e7b8d5eb4 Mon Sep 17 00:00:00 2001 From: anupsv Date: Wed, 18 Dec 2024 18:55:58 +0530 Subject: [PATCH 37/94] Create kms_fuzz_test.go adding fuzz tests for kms key methods. --- common/kms_fuzz_test.go | 245 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100644 common/kms_fuzz_test.go diff --git a/common/kms_fuzz_test.go b/common/kms_fuzz_test.go new file mode 100644 index 000000000..158ddd8ac --- /dev/null +++ b/common/kms_fuzz_test.go @@ -0,0 +1,245 @@ +package common + +import ( + "bytes" + "crypto/ecdsa" + "crypto/sha256" + "encoding/asn1" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/crypto" +) + +// ecdsaSignature defines the ASN.1 structure for ECDSA signatures. +type ecdsaSignature struct { + R, S *big.Int +} + +// generateValidSignature generates a valid ECDSA signature and returns the public key, hash, and DER signature. +func generateValidSignature() (*ecdsa.PublicKey, []byte, []byte, error) { + // Generate a secp256k1 ECDSA key pair. + privateKey, err := crypto.GenerateKey() + if err != nil { + return nil, nil, nil, err + } + publicKey := &privateKey.PublicKey + + // Define a message and compute its SHA-256 hash. + message := "Test message for ECDSA signature" + hash := sha256.Sum256([]byte(message)) + + // Sign the hash using the private key. + signatureBytes, err := crypto.Sign(hash[:], privateKey) + if err != nil { + return nil, nil, nil, err + } + + // Convert the signature to DER format. + r := new(big.Int).SetBytes(signatureBytes[:32]) + s := new(big.Int).SetBytes(signatureBytes[32:64]) + + // Marshal R and S into ASN.1 DER format. + derSignature, err := asn1.Marshal(ecdsaSignature{R: r, S: s}) + if err != nil { + return nil, nil, nil, err + } + + return publicKey, hash[:], derSignature, nil +} + +// defineEdgeCases returns a slice of tuples containing publicKeyBytes, hashBytes, derSignatureBytes +func defineEdgeCases() [][3][]byte { + var edgeCases [][3][]byte + + // Helper: Generate a valid signature to obtain a public key. + pubKeyValidBytes, hashValid, derSigValid, err := generateValidSignature() + if err != nil { + panic("Failed to generate valid signature for edge cases") + } + publicKeyValid := crypto.FromECDSAPub(pubKeyValidBytes) + + // 1. Malformed Public Keys + + // a. Incorrect length (too short) + publicKeyShort := []byte{0x04, 0x01, 0x02} + derSignatureValid := derSigValid + edgeCases = append(edgeCases, [3][]byte{publicKeyShort, hashValid, derSignatureValid}) + + // b. Incorrect prefix + publicKeyBadPrefix := make([]byte, 65) + publicKeyBadPrefix[0] = 0x05 // Invalid prefix + copy(publicKeyBadPrefix[1:], bytes.Repeat([]byte{0x01}, 64)) + edgeCases = append(edgeCases, [3][]byte{publicKeyBadPrefix, hashValid, derSignatureValid}) + + // c. Coordinates not on curve (invalid X, Y) + publicKeyInvalidXY := make([]byte, 65) + publicKeyInvalidXY[0] = 0x04 + // Set X and Y to values that are not on the curve + copy(publicKeyInvalidXY[1:], bytes.Repeat([]byte{0xFF}, 64)) + edgeCases = append(edgeCases, [3][]byte{publicKeyInvalidXY, hashValid, derSignatureValid}) + + // 2. Malformed Signatures + + // a. Invalid DER encoding (truncated) + derSignatureInvalidDER := []byte{0x30, 0x00} // Incomplete DER + edgeCases = append(edgeCases, [3][]byte{publicKeyValid, hashValid, derSignatureInvalidDER}) + + // b. R too long (33 bytes with leading zero) + derSignatureRTooLong := []byte{ + 0x30, 0x46, // SEQUENCE, length 70 + 0x02, 0x21, // INTEGER, length 33 + 0x00, // Leading zero + } + derSignatureRTooLong = append(derSignatureRTooLong, bytes.Repeat([]byte{0x01}, 32)...) // R + derSignatureRTooLong = append(derSignatureRTooLong, 0x02, 0x20) // S INTEGER, length 32 + derSignatureRTooLong = append(derSignatureRTooLong, bytes.Repeat([]byte{0x02}, 32)...) // S + edgeCases = append(edgeCases, [3][]byte{publicKeyValid, hashValid, derSignatureRTooLong}) + + // c. S too short (31 bytes) + derSignatureSTooShort := []byte{ + 0x30, 0x44, // SEQUENCE, length 68 + 0x02, 0x20, // INTEGER, length 32 + } + derSignatureSTooShort = append(derSignatureSTooShort, bytes.Repeat([]byte{0x03}, 32)...) // R + derSignatureSTooShort = append(derSignatureSTooShort, 0x02, 0x1F) // S INTEGER, length 31 + derSignatureSTooShort = append(derSignatureSTooShort, bytes.Repeat([]byte{0x04}, 31)...) // S + edgeCases = append(edgeCases, [3][]byte{publicKeyValid, hashValid, derSignatureSTooShort}) + + // 3. Invalid Hashes + + // a. Incorrect hash length (too short) + hashTooShort := make([]byte, 16) + edgeCases = append(edgeCases, [3][]byte{publicKeyValid, hashTooShort, derSignatureValid}) + + // b. Empty hash + hashEmpty := []byte{} + edgeCases = append(edgeCases, [3][]byte{publicKeyValid, hashEmpty, derSignatureValid}) + + // 4. Random Data + + // a. Completely random bytes + randomPublicKey := bytes.Repeat([]byte{0xAB}, 65) + randomHash := bytes.Repeat([]byte{0xCD}, 32) + randomSignature := bytes.Repeat([]byte{0xEF}, 70) + edgeCases = append(edgeCases, [3][]byte{randomPublicKey, randomHash, randomSignature}) + + // 5. Boundary Conditions + + // a. R equals zero + derSignatureRZero, _ := asn1.Marshal(ecdsaSignature{R: big.NewInt(0), S: big.NewInt(1)}) + edgeCases = append(edgeCases, [3][]byte{publicKeyValid, hashValid, derSignatureRZero}) + + // b. S equals N (curve order) + secp256k1N := crypto.S256().Params().N + derSignatureSEqualsN, _ := asn1.Marshal(ecdsaSignature{R: big.NewInt(1), S: new(big.Int).Set(secp256k1N)}) + edgeCases = append(edgeCases, [3][]byte{publicKeyValid, hashValid, derSignatureSEqualsN}) + + // c. S just above N/2 + secp256k1HalfN := new(big.Int).Div(crypto.S256().Params().N, big.NewInt(2)) + sAboveHalfN := new(big.Int).Add(secp256k1HalfN, big.NewInt(1)) + derSignatureSAboveHalfN, _ := asn1.Marshal(ecdsaSignature{R: big.NewInt(1), S: sAboveHalfN}) + edgeCases = append(edgeCases, [3][]byte{publicKeyValid, hashValid, derSignatureSAboveHalfN}) + + // d. S just below N/2 + sBelowHalfN := new(big.Int).Sub(secp256k1HalfN, big.NewInt(1)) + derSignatureSBelowHalfN, _ := asn1.Marshal(ecdsaSignature{R: big.NewInt(1), S: sBelowHalfN}) + edgeCases = append(edgeCases, [3][]byte{publicKeyValid, hashValid, derSignatureSBelowHalfN}) + + // 6. Extra Data + + // a. Extra bytes appended to the signature + derSignatureExtra := append(derSignatureValid, 0x00, 0x01, 0x02) + edgeCases = append(edgeCases, [3][]byte{publicKeyValid, hashValid, derSignatureExtra}) + + // b. Missing bytes in the signature + if len(derSignatureValid) > 2 { + derSignatureMissing := derSignatureValid[:len(derSignatureValid)-2] + edgeCases = append(edgeCases, [3][]byte{publicKeyValid, hashValid, derSignatureMissing}) + } + + return edgeCases +} + +// FuzzParseSignatureKMS tests the ParseSignatureKMS function with various inputs, including edge cases. +func FuzzParseSignatureKMS(f *testing.F) { + // Generate multiple valid seed inputs + for i := 0; i < 5; i++ { + publicKey, hash, derSignature, err := generateValidSignature() + if err != nil { + f.Fatalf("Failed to generate valid signature: %v", err) + } + publicKeyBytes := crypto.FromECDSAPub(publicKey) + f.Add(publicKeyBytes, hash, derSignature) + } + + // Incorporate edge cases into the fuzz corpus + edgeCases := defineEdgeCases() + for _, ec := range edgeCases { + f.Add(ec[0], ec[1], ec[2]) + } + + // Define the fuzzing function + f.Fuzz(func(t *testing.T, publicKeyBytes []byte, hashBytes []byte, derSignatureBytes []byte) { + // Skip iteration if publicKeyBytes is not the correct length + if len(publicKeyBytes) != 65 { + return + } + + // Attempt to parse the public key + pubKey, err := ParsePublicKeyKMS(publicKeyBytes) + if err != nil { + // Invalid public key; acceptable for fuzzing + return + } + + // Attempt to parse the signature + signature, err := ParseSignatureKMS(pubKey, hashBytes, derSignatureBytes) + if err != nil { + // Parsing failed; acceptable for fuzzing + return + } + + // Validate that the signature is exactly 65 bytes + if len(signature) != 65 { + t.Errorf("Expected signature length 65 bytes, got %d bytes", len(signature)) + } + + // if the code made it this far, then the pubkey and signature are valid so recovery must work. + recoveredPubBytes, err := crypto.Ecrecover(hashBytes, signature) + if err != nil { + t.Errorf("Ecrecover failed: %v", err) + return + } + + // Compare the recovered public key with the original + if !bytes.Equal(recoveredPubBytes, publicKeyBytes) { + // Attempt with the possible V values + signatureCheck := false + if signature[64] == 27 { + recoveredPubBytes, err = crypto.Ecrecover(hashBytes, signature) + if err != nil { + t.Errorf("Ecrecover failed with V=27: %v", err) + } else if !bytes.Equal(recoveredPubBytes, publicKeyBytes) { + t.Errorf("Recovered public key does not match original") + } else { + signatureCheck = true + } + } + + if !signatureCheck { + signature[64] = 28 + recoveredPubBytes, err = crypto.Ecrecover(hashBytes, signature) + if err != nil { + t.Errorf("Ecrecover failed with V=28: %v", err) + return + } + + if !bytes.Equal(recoveredPubBytes, publicKeyBytes) { + t.Errorf("Recovered public key does not match original") + return + } + } + } + }) +} From 260bf7925209f4dceff59046a9f4e4d53d79ecf7 Mon Sep 17 00:00:00 2001 From: anupsv Date: Wed, 18 Dec 2024 19:02:44 +0530 Subject: [PATCH 38/94] Update Makefile for fuzz tests --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index 033741a38..f09256087 100644 --- a/Makefile +++ b/Makefile @@ -59,6 +59,9 @@ dataapi-build: unit-tests: ./test.sh +fuzz-tests: + go test --fuzz=FuzzParseSignatureKMS -fuzztime=5m ./common + integration-tests-churner: go test -v ./churner/tests From dcd3e06f279bf2e2b86951dc4d909582ae153e86 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Wed, 18 Dec 2024 11:40:55 -0600 Subject: [PATCH 39/94] Disable request signing if KMS key name is not provided. Signed-off-by: Cody Littley --- api/clients/v2/node_client.go | 12 +++++++----- disperser/cmd/controller/main.go | 17 ++++++++++------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/api/clients/v2/node_client.go b/api/clients/v2/node_client.go index 4d2b7097f..5880022af 100644 --- a/api/clients/v2/node_client.go +++ b/api/clients/v2/node_client.go @@ -73,12 +73,14 @@ func (c *nodeClient) StoreChunks(ctx context.Context, batch *corev2.Batch) (*cor DisperserID: 0, // this will need to be updated dispersers are decentralized } - // Sign the request to store chunks - signature, err := c.requestSigner.SignStoreChunksRequest(ctx, request) - if err != nil { - return nil, fmt.Errorf("failed to sign store chunks request: %v", err) + if c.requestSigner != nil { + // Sign the request to store chunks + signature, err := c.requestSigner.SignStoreChunksRequest(ctx, request) + if err != nil { + return nil, fmt.Errorf("failed to sign store chunks request: %v", err) + } + request.Signature = signature } - request.Signature = signature // Call the gRPC method to store chunks response, err := c.dispersalClient.StoreChunks(ctx, request) diff --git a/disperser/cmd/controller/main.go b/disperser/cmd/controller/main.go index 7fa605622..aa0ec068d 100644 --- a/disperser/cmd/controller/main.go +++ b/disperser/cmd/controller/main.go @@ -150,13 +150,16 @@ func RunController(ctx *cli.Context) error { } } - requestSigner, err := clients.NewRequestSigner( - context.Background(), - config.AwsClientConfig.Region, - config.AwsClientConfig.EndpointURL, - config.DisperserSigningKeyName) - if err != nil { - return fmt.Errorf("failed to create request signer: %v", err) + var requestSigner clients.RequestSigner + if config.DisperserSigningKeyName != "" { + requestSigner, err = clients.NewRequestSigner( + context.Background(), + config.AwsClientConfig.Region, + config.AwsClientConfig.EndpointURL, + config.DisperserSigningKeyName) + if err != nil { + return fmt.Errorf("failed to create request signer: %v", err) + } } nodeClientManager, err := controller.NewNodeClientManager(config.NodeClientCacheSize, requestSigner, logger) From f6b008ff39f458989dcbddfafb1b0382fa3d055d Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Wed, 18 Dec 2024 13:25:35 -0600 Subject: [PATCH 40/94] Tie into bindings. Signed-off-by: Cody Littley --- core/eth/reader.go | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/core/eth/reader.go b/core/eth/reader.go index fb1da3adc..3360e6b3f 100644 --- a/core/eth/reader.go +++ b/core/eth/reader.go @@ -3,6 +3,7 @@ package eth import ( "context" "crypto/ecdsa" + "fmt" "math/big" "strings" @@ -10,6 +11,7 @@ import ( avsdir "github.com/Layr-Labs/eigenda/contracts/bindings/AVSDirectory" blsapkreg "github.com/Layr-Labs/eigenda/contracts/bindings/BLSApkRegistry" delegationmgr "github.com/Layr-Labs/eigenda/contracts/bindings/DelegationManager" + disperserreg "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDADisperserRegistry" relayreg "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDARelayRegistry" eigendasrvmg "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDAServiceManager" thresholdreg "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDAThresholdRegistry" @@ -45,6 +47,7 @@ type ContractBindings struct { PaymentVault *paymentvault.ContractPaymentVault RelayRegistry *relayreg.ContractEigenDARelayRegistry ThresholdRegistry *thresholdreg.ContractEigenDAThresholdRegistry + DisperserRegistry *disperserreg.ContractEigenDADisperserRegistry } type Reader struct { @@ -225,6 +228,21 @@ func (t *Reader) updateContractBindings(blsOperatorStateRetrieverAddr, eigenDASe } + var contractEigenDADisperserRegistry *disperserreg.ContractEigenDADisperserRegistry + disperserRegistryAddr, err := contractEigenDAServiceManager.EigenDADisperserRegistry(&bind.CallOpts{}) + if err != nil { + t.logger.Error("Failed to fetch EigenDADisperserRegistry address", "err", err) + // TODO(cody-littley): return err when the contract is deployed + // return err + } else { + contractEigenDADisperserRegistry, err = + disperserreg.NewContractEigenDADisperserRegistry(disperserRegistryAddr, t.ethClient) + if err != nil { + t.logger.Error("Failed to fetch EigenDADisperserRegistry contract", "err", err) + return err + } + } + t.bindings = &ContractBindings{ ServiceManagerAddr: eigenDAServiceManagerAddr, RegCoordinatorAddr: registryCoordinatorAddr, @@ -241,6 +259,7 @@ func (t *Reader) updateContractBindings(blsOperatorStateRetrieverAddr, eigenDASe RelayRegistry: contractRelayRegistry, PaymentVault: contractPaymentVault, ThresholdRegistry: contractThresholdRegistry, + DisperserRegistry: contractEigenDADisperserRegistry, } return nil } @@ -918,8 +937,20 @@ func (t *Reader) GetRelayURLs(ctx context.Context) (map[uint32]string, error) { } func (t *Reader) GetDisperserAddress(ctx context.Context, disperserID uint32) (gethcommon.Address, error) { - // TODO(cody-littley/arch): this is just a place holder until we register dispersers on chain - bytes := make([]byte, gethcommon.AddressLength) - address := gethcommon.BytesToAddress(bytes) + registry := t.bindings.DisperserRegistry + if registry == nil { + return gethcommon.Address{}, errors.New("disperser registry not deployed") + } + + address, err := registry.DisperserKeyToAddress( + &bind.CallOpts{ + Context: ctx, + }, + disperserID) + + if err != nil { + return gethcommon.Address{}, fmt.Errorf("failed to get disperser address: %w", err) + } + return address, nil } From 1493ded335816b6be99793d24f8492904027f1cf Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Wed, 18 Dec 2024 13:43:50 -0600 Subject: [PATCH 41/94] Incremental progress. Signed-off-by: Cody Littley --- core/eth/reader.go | 4 ++-- node/grpc/server_test.go | 2 +- test/integration_test.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/eth/reader.go b/core/eth/reader.go index 3360e6b3f..122965264 100644 --- a/core/eth/reader.go +++ b/core/eth/reader.go @@ -915,10 +915,10 @@ func (t *Reader) GetRelayURLs(ctx context.Context) (map[uint32]string, error) { } res := make(map[uint32]string) - for relayKey := uint32(0); relayKey < uint32(numRelays); relayKey++ { + for relayKey := uint32(0); relayKey < numRelays; relayKey++ { url, err := t.bindings.RelayRegistry.RelayKeyToUrl(&bind.CallOpts{ Context: ctx, - }, uint32(relayKey)) + }, relayKey) if err != nil && strings.Contains(err.Error(), "execution reverted") { break diff --git a/node/grpc/server_test.go b/node/grpc/server_test.go index de996504c..b608e91c2 100644 --- a/node/grpc/server_test.go +++ b/node/grpc/server_test.go @@ -85,7 +85,7 @@ func makeConfig(t *testing.T) *node.Config { ID: opID, NumBatchValidators: runtime.GOMAXPROCS(0), EnableV2: false, - DisableDispersalAuthentication: true, // TODO enable + DisableDispersalAuthentication: true, } } diff --git a/test/integration_test.go b/test/integration_test.go index 3f747573b..dfedb6b8e 100644 --- a/test/integration_test.go +++ b/test/integration_test.go @@ -364,7 +364,7 @@ func mustMakeOperators(t *testing.T, cst *coremock.ChainDataMock, logger logging PrivateBls: string(op.KeyPair.GetPubKeyG1().Serialize()), ID: id, QuorumIDList: registeredQuorums, - DisableDispersalAuthentication: true, // TODO enable + DisableDispersalAuthentication: false, // TODO things break when this is enabled } // creating a new instance of encoder instead of sharing enc because enc is not thread safe From 52f235420676fb66f421ac776cc959d74a85ff13 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Thu, 19 Dec 2024 09:09:38 -0600 Subject: [PATCH 42/94] Add debug code. Signed-off-by: Cody Littley --- api/clients/v2/request_signer_test.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/api/clients/v2/request_signer_test.go b/api/clients/v2/request_signer_test.go index 98452f457..2043b87e9 100644 --- a/api/clients/v2/request_signer_test.go +++ b/api/clients/v2/request_signer_test.go @@ -47,7 +47,13 @@ func setup(t *testing.T) { func changeDirectory(path string) { err := os.Chdir(path) if err != nil { - log.Panicf("Failed to change directories. Error: %s", err) + + currentDirectory, err := os.Getwd() + if err != nil { + log.Printf("Failed to get current directory. Error: %s", err) + } + + log.Panicf("Failed to change directories. CWD: %s, Error: %s", currentDirectory, err) } newDir, err := os.Getwd() From 7c5cc9363edc1db7c8abf661f0891054f6c2597d Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Thu, 19 Dec 2024 09:57:57 -0600 Subject: [PATCH 43/94] Fix unit test. Signed-off-by: Cody Littley --- api/clients/v2/request_signer_test.go | 2 +- node/cmd/main.go | 12 ++++++- node/grpc/server_v2.go | 12 +------ node/grpc/server_v2_test.go | 7 ++-- test/integration_test.go | 46 +++++++++++++++------------ 5 files changed, 43 insertions(+), 36 deletions(-) diff --git a/api/clients/v2/request_signer_test.go b/api/clients/v2/request_signer_test.go index 2043b87e9..153ad7eff 100644 --- a/api/clients/v2/request_signer_test.go +++ b/api/clients/v2/request_signer_test.go @@ -34,7 +34,7 @@ func setup(t *testing.T) { deployLocalStack := !(os.Getenv("DEPLOY_LOCALSTACK") == "false") _, b, _, _ := runtime.Caller(0) - rootPath := filepath.Join(filepath.Dir(b), "../..") + rootPath := filepath.Join(filepath.Dir(b), "../../..") changeDirectory(filepath.Join(rootPath, "inabox")) if deployLocalStack { diff --git a/node/cmd/main.go b/node/cmd/main.go index a69b3b724..f4b696fd7 100644 --- a/node/cmd/main.go +++ b/node/cmd/main.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "github.com/Layr-Labs/eigenda/common/geth" + coreeth "github.com/Layr-Labs/eigenda/core/eth" rpccalls "github.com/Layr-Labs/eigensdk-go/metrics/collectors/rpc_calls" "log" "os" @@ -80,6 +81,15 @@ func NodeMain(ctx *cli.Context) error { return fmt.Errorf("cannot create chain.Client: %w", err) } + reader, err := coreeth.NewReader( + logger, + client, + config.BLSOperatorStateRetrieverAddr, + config.EigenDAServiceManagerAddr) + if err != nil { + return fmt.Errorf("cannot create eth.Reader: %w", err) + } + // Create the node. node, err := node.NewNode(reg, config, pubIPProvider, client, logger) if err != nil { @@ -97,7 +107,7 @@ func NodeMain(ctx *cli.Context) error { // TODO(cody-littley): the metrics server is currently started by eigenmetrics, which is in another repo. // When we fully remove v1 support, we need to start the metrics server inside the v2 metrics code. server := nodegrpc.NewServer(config, node, logger, ratelimiter) - serverV2, err := nodegrpc.NewServerV2(context.Background(), config, node, logger, ratelimiter, reg, client) + serverV2, err := nodegrpc.NewServerV2(context.Background(), config, node, logger, ratelimiter, reg, reader) if err != nil { return fmt.Errorf("failed to create server v2: %v", err) } diff --git a/node/grpc/server_v2.go b/node/grpc/server_v2.go index 05c7a42a0..b75f018e1 100644 --- a/node/grpc/server_v2.go +++ b/node/grpc/server_v2.go @@ -10,7 +10,6 @@ import ( "github.com/Layr-Labs/eigenda/common" "github.com/Layr-Labs/eigenda/common/kvstore" "github.com/Layr-Labs/eigenda/core" - coreeth "github.com/Layr-Labs/eigenda/core/eth" corev2 "github.com/Layr-Labs/eigenda/core/v2" "github.com/Layr-Labs/eigenda/node" "github.com/Layr-Labs/eigenda/node/auth" @@ -43,7 +42,7 @@ func NewServerV2( logger logging.Logger, ratelimiter common.RateLimiter, registry *prometheus.Registry, - client common.EthClient) (*ServerV2, error) { + reader core.Reader) (*ServerV2, error) { metrics, err := NewV2Metrics(logger, registry) if err != nil { @@ -52,15 +51,6 @@ func NewServerV2( var authenticator auth.RequestAuthenticator if !config.DisableDispersalAuthentication { - reader, err := coreeth.NewReader( - logger, - client, - config.BLSOperatorStateRetrieverAddr, - config.EigenDAServiceManagerAddr) - if err != nil { - return nil, fmt.Errorf("cannot create eth.Reader: %w", err) - } - authenticator, err = auth.NewRequestAuthenticator( ctx, reader, diff --git a/node/grpc/server_v2_test.go b/node/grpc/server_v2_test.go index 15b587120..05bcd12db 100644 --- a/node/grpc/server_v2_test.go +++ b/node/grpc/server_v2_test.go @@ -3,6 +3,7 @@ package grpc_test import ( "context" "errors" + coreeth "github.com/Layr-Labs/eigenda/core/eth" "os" "sync/atomic" "testing" @@ -84,7 +85,9 @@ func newTestComponents(t *testing.T, config *node.Config) *testComponents { node.BlobVersionParams.Store(v2.NewBlobVersionParameterMap(blobParamsMap)) // The eth client is only utilized for StoreChunks validation, which is disabled in these tests - var client common.EthClient + // TODO enable auth for these tests + var reader *coreeth.Reader + server, err := grpc.NewServerV2( context.Background(), config, @@ -92,7 +95,7 @@ func newTestComponents(t *testing.T, config *node.Config) *testComponents { logger, ratelimiter, prometheus.NewRegistry(), - client) + reader) require.NoError(t, err) return &testComponents{ diff --git a/test/integration_test.go b/test/integration_test.go index dfedb6b8e..6c93cd382 100644 --- a/test/integration_test.go +++ b/test/integration_test.go @@ -31,6 +31,7 @@ import ( clientsmock "github.com/Layr-Labs/eigenda/api/clients/mock" commonaws "github.com/Layr-Labs/eigenda/common/aws" "github.com/Layr-Labs/eigenda/core/meterer" + coremock "github.com/Layr-Labs/eigenda/core/mock" "github.com/Layr-Labs/eigenda/disperser/apiserver" dispatcher "github.com/Layr-Labs/eigenda/disperser/batcher/grpc" "github.com/Layr-Labs/eigenda/disperser/encoder" @@ -45,7 +46,6 @@ import ( "github.com/Layr-Labs/eigenda/common" commonmock "github.com/Layr-Labs/eigenda/common/mock" "github.com/Layr-Labs/eigenda/core" - coremock "github.com/Layr-Labs/eigenda/core/mock" "github.com/Layr-Labs/eigenda/disperser" "github.com/Layr-Labs/eigenda/disperser/batcher" batchermock "github.com/Layr-Labs/eigenda/disperser/batcher/mock" @@ -351,20 +351,21 @@ func mustMakeOperators(t *testing.T, cst *coremock.ChainDataMock, logger logging } config := &node.Config{ - Hostname: op.Host, - DispersalPort: op.DispersalPort, - RetrievalPort: op.RetrievalPort, - InternalRetrievalPort: op.RetrievalPort, - InternalDispersalPort: op.DispersalPort, - EnableMetrics: false, - Timeout: 10, - ExpirationPollIntervalSec: 10, - DbPath: dbPath, - LogPath: logPath, - PrivateBls: string(op.KeyPair.GetPubKeyG1().Serialize()), - ID: id, - QuorumIDList: registeredQuorums, - DisableDispersalAuthentication: false, // TODO things break when this is enabled + Hostname: op.Host, + DispersalPort: op.DispersalPort, + RetrievalPort: op.RetrievalPort, + InternalRetrievalPort: op.RetrievalPort, + InternalDispersalPort: op.DispersalPort, + EnableMetrics: false, + Timeout: 10, + ExpirationPollIntervalSec: 10, + DbPath: dbPath, + LogPath: logPath, + PrivateBls: string(op.KeyPair.GetPubKeyG1().Serialize()), + ID: id, + QuorumIDList: registeredQuorums, + DispersalAuthenticationKeyCacheSize: 1024, + DisableDispersalAuthentication: false, } // creating a new instance of encoder instead of sharing enc because enc is not thread safe @@ -418,20 +419,23 @@ func mustMakeOperators(t *testing.T, cst *coremock.ChainDataMock, logger logging OperatorSocketsFilterer: mockOperatorSocketsFilterer, } - ratelimiter := &commonmock.NoopRatelimiter{} + rateLimiter := &commonmock.NoopRatelimiter{} - // TODO this needs to be non-null once we enable dispersal authentication - var client common.EthClient + // TODO(cody-littley): Once we switch this test to use the v2 disperser, we will need to properly set up + // the disperser's public/private keys for signing StoreChunks() requests + disperserAddress := gethcommon.Address{} + reader := &coremock.MockWriter{} + reader.On("GetDisperserAddress", uint32(0)).Return(disperserAddress, nil) - serverV1 := nodegrpc.NewServer(config, n, logger, ratelimiter) + serverV1 := nodegrpc.NewServer(config, n, logger, rateLimiter) serverV2, err := nodegrpc.NewServerV2( context.Background(), config, n, logger, - ratelimiter, + rateLimiter, prometheus.NewRegistry(), - client) + reader) require.NoError(t, err) ops[id] = TestOperator{ From c83db79c304f51f2b0dc6d678a3ae25ee5d73a02 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Thu, 19 Dec 2024 12:15:03 -0600 Subject: [PATCH 44/94] Disable request signing for inabox e2e test. Signed-off-by: Cody Littley --- disperser/cmd/controller/config.go | 24 ++++---- disperser/cmd/controller/flags/flags.go | 8 +-- disperser/cmd/controller/main.go | 4 +- inabox/deploy/config.go | 82 +++++++++++++------------ inabox/deploy/env_vars.go | 24 +++++++- 5 files changed, 82 insertions(+), 60 deletions(-) diff --git a/disperser/cmd/controller/config.go b/disperser/cmd/controller/config.go index f833701a9..6c9cbe3bc 100644 --- a/disperser/cmd/controller/config.go +++ b/disperser/cmd/controller/config.go @@ -25,13 +25,13 @@ type Config struct { DynamoDBTableName string - EthClientConfig geth.EthClientConfig - AwsClientConfig aws.ClientConfig - DisperserSigningKeyName string - LoggerConfig common.LoggerConfig - IndexerConfig indexer.Config - ChainStateConfig thegraph.Config - UseGraph bool + EthClientConfig geth.EthClientConfig + AwsClientConfig aws.ClientConfig + DisperserKMSKeyID string + LoggerConfig common.LoggerConfig + IndexerConfig indexer.Config + ChainStateConfig thegraph.Config + UseGraph bool BLSOperatorStateRetrieverAddr string EigenDAServiceManagerAddr string @@ -61,11 +61,11 @@ func NewConfig(ctx *cli.Context) (Config, error) { relays[i] = corev2.RelayKey(relay) } config := Config{ - DynamoDBTableName: ctx.GlobalString(flags.DynamoDBTableNameFlag.Name), - EthClientConfig: ethClientConfig, - AwsClientConfig: aws.ReadClientConfig(ctx, flags.FlagPrefix), - DisperserSigningKeyName: ctx.GlobalString(flags.DisperserSigningKeyNameFlag.Name), - LoggerConfig: *loggerConfig, + DynamoDBTableName: ctx.GlobalString(flags.DynamoDBTableNameFlag.Name), + EthClientConfig: ethClientConfig, + AwsClientConfig: aws.ReadClientConfig(ctx, flags.FlagPrefix), + DisperserKMSKeyID: ctx.GlobalString(flags.DisperserKMSKeyIDFlag.Name), + LoggerConfig: *loggerConfig, EncodingManagerConfig: controller.EncodingManagerConfig{ PullInterval: ctx.GlobalDuration(flags.EncodingPullIntervalFlag.Name), EncodingRequestTimeout: ctx.GlobalDuration(flags.EncodingRequestTimeoutFlag.Name), diff --git a/disperser/cmd/controller/flags/flags.go b/disperser/cmd/controller/flags/flags.go index ac6d5db41..45d5ffac3 100644 --- a/disperser/cmd/controller/flags/flags.go +++ b/disperser/cmd/controller/flags/flags.go @@ -178,11 +178,11 @@ var ( EnvVar: common.PrefixEnvVar(envVarPrefix, "METRICS_PORT"), Value: 9101, } - DisperserSigningKeyNameFlag = cli.StringFlag{ - Name: common.PrefixFlag(FlagPrefix, "disperser-signing-key-name"), + DisperserKMSKeyIDFlag = cli.StringFlag{ + Name: common.PrefixFlag(FlagPrefix, "disperser-kms-key-id"), Usage: "Name of the key used to sign disperser requests (key must be stored in AWS KMS under this name)", Required: true, - EnvVar: common.PrefixEnvVar(envVarPrefix, "DISPERSER_SIGNING_KEY_NAME"), + EnvVar: common.PrefixEnvVar(envVarPrefix, "DISPERSER_KMS_KEY_ID"), } ) @@ -198,7 +198,7 @@ var requiredFlags = []cli.Flag{ DispatcherPullIntervalFlag, NodeRequestTimeoutFlag, NumConnectionsToNodesFlag, - DisperserSigningKeyNameFlag, + DisperserKMSKeyIDFlag, } var optionalFlags = []cli.Flag{ diff --git a/disperser/cmd/controller/main.go b/disperser/cmd/controller/main.go index aa0ec068d..1d792091a 100644 --- a/disperser/cmd/controller/main.go +++ b/disperser/cmd/controller/main.go @@ -151,12 +151,12 @@ func RunController(ctx *cli.Context) error { } var requestSigner clients.RequestSigner - if config.DisperserSigningKeyName != "" { + if config.DisperserKMSKeyID != "" { requestSigner, err = clients.NewRequestSigner( context.Background(), config.AwsClientConfig.Region, config.AwsClientConfig.EndpointURL, - config.DisperserSigningKeyName) + config.DisperserKMSKeyID) if err != nil { return fmt.Errorf("failed to create request signer: %v", err) } diff --git a/inabox/deploy/config.go b/inabox/deploy/config.go index d4f7fe88e..2e5654a1c 100644 --- a/inabox/deploy/config.go +++ b/inabox/deploy/config.go @@ -343,6 +343,7 @@ func (env *Config) generateControllerVars(ind int, graphUrl string) ControllerVa CONTROLLER_AWS_ENDPOINT_URL: "", CONTROLLER_ENCODER_ADDRESS: "0.0.0.0:34001", CONTROLLER_FINALIZATION_BLOCK_DELAY: "0", + CONTROLLER_DISPERSER_KMS_KEY_ID: "", } env.applyDefaults(&v, "CONTROLLER", "controller", ind) @@ -391,46 +392,47 @@ func (env *Config) generateOperatorVars(ind int, name, key, churnerUrl, logPath, ecdsaPassword := env.Pks.EcdsaMap[name].Password v := OperatorVars{ - NODE_HOSTNAME: "", - NODE_DISPERSAL_PORT: dispersalPort, - NODE_RETRIEVAL_PORT: retrievalPort, - NODE_INTERNAL_DISPERSAL_PORT: dispersalPort, - NODE_INTERNAL_RETRIEVAL_PORT: retrievalPort, - NODE_ENABLE_METRICS: "true", - NODE_METRICS_PORT: metricsPort, - NODE_ENABLE_NODE_API: "true", - NODE_API_PORT: nodeApiPort, - NODE_TIMEOUT: "10s", - NODE_QUORUM_ID_LIST: "0,1", - NODE_DB_PATH: dbPath, - NODE_ENABLE_TEST_MODE: "false", // using encrypted key in inabox - NODE_TEST_PRIVATE_BLS: blsKey, - NODE_BLS_KEY_FILE: blsKeyFile, - NODE_ECDSA_KEY_FILE: ecdsaKeyFile, - NODE_BLS_KEY_PASSWORD: blsPassword, - NODE_ECDSA_KEY_PASSWORD: ecdsaPassword, - NODE_BLS_OPERATOR_STATE_RETRIVER: env.EigenDA.OperatorStateRetreiver, - NODE_EIGENDA_SERVICE_MANAGER: env.EigenDA.ServiceManager, - NODE_REGISTER_AT_NODE_START: "true", - NODE_CHURNER_URL: churnerUrl, - NODE_CHURNER_USE_SECURE_GRPC: "false", - NODE_EXPIRATION_POLL_INTERVAL: "10", - NODE_G1_PATH: "", - NODE_G2_PATH: "", - NODE_G2_POWER_OF_2_PATH: "", - NODE_CACHE_PATH: "", - NODE_SRS_ORDER: "", - NODE_SRS_LOAD: "", - NODE_NUM_WORKERS: fmt.Sprint(runtime.GOMAXPROCS(0)), - NODE_VERBOSE: "true", - NODE_CHAIN_RPC: "", - NODE_PRIVATE_KEY: key[2:], - NODE_NUM_BATCH_VALIDATORS: "128", - NODE_PUBLIC_IP_PROVIDER: "mockip", - NODE_PUBLIC_IP_CHECK_INTERVAL: "10s", - NODE_NUM_CONFIRMATIONS: "0", - NODE_ONCHAIN_METRICS_INTERVAL: "-1", - NODE_ENABLE_V2: "true", + NODE_HOSTNAME: "", + NODE_DISPERSAL_PORT: dispersalPort, + NODE_RETRIEVAL_PORT: retrievalPort, + NODE_INTERNAL_DISPERSAL_PORT: dispersalPort, + NODE_INTERNAL_RETRIEVAL_PORT: retrievalPort, + NODE_ENABLE_METRICS: "true", + NODE_METRICS_PORT: metricsPort, + NODE_ENABLE_NODE_API: "true", + NODE_API_PORT: nodeApiPort, + NODE_TIMEOUT: "10s", + NODE_QUORUM_ID_LIST: "0,1", + NODE_DB_PATH: dbPath, + NODE_ENABLE_TEST_MODE: "false", // using encrypted key in inabox + NODE_TEST_PRIVATE_BLS: blsKey, + NODE_BLS_KEY_FILE: blsKeyFile, + NODE_ECDSA_KEY_FILE: ecdsaKeyFile, + NODE_BLS_KEY_PASSWORD: blsPassword, + NODE_ECDSA_KEY_PASSWORD: ecdsaPassword, + NODE_BLS_OPERATOR_STATE_RETRIVER: env.EigenDA.OperatorStateRetreiver, + NODE_EIGENDA_SERVICE_MANAGER: env.EigenDA.ServiceManager, + NODE_REGISTER_AT_NODE_START: "true", + NODE_CHURNER_URL: churnerUrl, + NODE_CHURNER_USE_SECURE_GRPC: "false", + NODE_EXPIRATION_POLL_INTERVAL: "10", + NODE_G1_PATH: "", + NODE_G2_PATH: "", + NODE_G2_POWER_OF_2_PATH: "", + NODE_CACHE_PATH: "", + NODE_SRS_ORDER: "", + NODE_SRS_LOAD: "", + NODE_NUM_WORKERS: fmt.Sprint(runtime.GOMAXPROCS(0)), + NODE_VERBOSE: "true", + NODE_CHAIN_RPC: "", + NODE_PRIVATE_KEY: key[2:], + NODE_NUM_BATCH_VALIDATORS: "128", + NODE_PUBLIC_IP_PROVIDER: "mockip", + NODE_PUBLIC_IP_CHECK_INTERVAL: "10s", + NODE_NUM_CONFIRMATIONS: "0", + NODE_ONCHAIN_METRICS_INTERVAL: "-1", + NODE_ENABLE_V2: "true", + NODE_DISABLE_DISPERSAL_AUTHENTICATION: "true", } env.applyDefaults(&v, "NODE", "opr", ind) diff --git a/inabox/deploy/env_vars.go b/inabox/deploy/env_vars.go index e55984eb0..9e6de5171 100644 --- a/inabox/deploy/env_vars.go +++ b/inabox/deploy/env_vars.go @@ -271,6 +271,10 @@ type EncoderVars struct { DISPERSER_ENCODER_S3_BUCKET_NAME string + DISPERSER_ENCODER_GPU_ENABLE string + + DISPERSER_ENCODER_BACKEND string + DISPERSER_ENCODER_PREVENT_REENCODING string DISPERSER_ENCODER_PPROF_HTTP_PORT string @@ -416,10 +420,20 @@ type OperatorVars struct { NODE_ONCHAIN_STATE_REFRESH_INTERVAL string + NODE_CHUNK_DOWNLOAD_TIMEOUT string + NODE_PPROF_HTTP_PORT string NODE_ENABLE_PPROF string + NODE_DISABLE_DISPERSAL_AUTHENTICATION string + + NODE_DISPERSAL_AUTHENTICATION_KEY_CACHE_SIZE string + + NODE_DISPERSER_KEY_TIMEOUT string + + NODE_DISPERSAL_AUTHENTICATION_TIMEOUT string + NODE_G1_PATH string NODE_G2_PATH string @@ -611,6 +625,8 @@ type ControllerVars struct { CONTROLLER_NUM_CONNECTIONS_TO_NODES string + CONTROLLER_DISPERSER_KMS_KEY_ID string + CONTROLLER_INDEXER_DATA_DIR string CONTROLLER_ENCODING_REQUEST_TIMEOUT string @@ -637,6 +653,8 @@ type ControllerVars struct { CONTROLLER_MAX_BATCH_SIZE string + CONTROLLER_METRICS_PORT string + CONTROLLER_CHAIN_RPC string CONTROLLER_CHAIN_RPC_FALLBACK string @@ -716,6 +734,8 @@ type RelayVars struct { RELAY_CHUNK_MAX_CONCURRENCY string + RELAY_MAX_KEYS_PER_GET_CHUNKS_REQUEST string + RELAY_MAX_GET_BLOB_OPS_PER_SECOND string RELAY_GET_BLOB_OPS_BURSTINESS string @@ -746,8 +766,6 @@ type RelayVars struct { RELAY_MAX_CONCURRENT_GET_CHUNK_OPS_CLIENT string - RELAY_INDEXER_PULL_INTERVAL string - RELAY_AUTHENTICATION_KEY_CACHE_SIZE string RELAY_AUTHENTICATION_TIMEOUT string @@ -768,6 +786,8 @@ type RelayVars struct { RELAY_ONCHAIN_STATE_REFRESH_INTERVAL string + RELAY_METRICS_PORT string + RELAY_LOG_LEVEL string RELAY_LOG_PATH string From e5cb69c0af0b2067552527b374acd1e7a8f6e4b4 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Thu, 19 Dec 2024 12:51:26 -0600 Subject: [PATCH 45/94] Added flag for disabling signing. Signed-off-by: Cody Littley --- disperser/cmd/controller/config.go | 26 ++++++++------- disperser/cmd/controller/flags/flags.go | 11 +++++-- disperser/cmd/controller/main.go | 2 +- inabox/deploy/config.go | 42 ++++++++++++------------- inabox/deploy/env_vars.go | 6 ++-- 5 files changed, 49 insertions(+), 38 deletions(-) diff --git a/disperser/cmd/controller/config.go b/disperser/cmd/controller/config.go index 6c9cbe3bc..b07b6d60e 100644 --- a/disperser/cmd/controller/config.go +++ b/disperser/cmd/controller/config.go @@ -25,13 +25,14 @@ type Config struct { DynamoDBTableName string - EthClientConfig geth.EthClientConfig - AwsClientConfig aws.ClientConfig - DisperserKMSKeyID string - LoggerConfig common.LoggerConfig - IndexerConfig indexer.Config - ChainStateConfig thegraph.Config - UseGraph bool + EthClientConfig geth.EthClientConfig + AwsClientConfig aws.ClientConfig + DisperserStoreChunksSigningDisabled bool + DisperserKMSKeyID string + LoggerConfig common.LoggerConfig + IndexerConfig indexer.Config + ChainStateConfig thegraph.Config + UseGraph bool BLSOperatorStateRetrieverAddr string EigenDAServiceManagerAddr string @@ -61,11 +62,12 @@ func NewConfig(ctx *cli.Context) (Config, error) { relays[i] = corev2.RelayKey(relay) } config := Config{ - DynamoDBTableName: ctx.GlobalString(flags.DynamoDBTableNameFlag.Name), - EthClientConfig: ethClientConfig, - AwsClientConfig: aws.ReadClientConfig(ctx, flags.FlagPrefix), - DisperserKMSKeyID: ctx.GlobalString(flags.DisperserKMSKeyIDFlag.Name), - LoggerConfig: *loggerConfig, + DynamoDBTableName: ctx.GlobalString(flags.DynamoDBTableNameFlag.Name), + EthClientConfig: ethClientConfig, + AwsClientConfig: aws.ReadClientConfig(ctx, flags.FlagPrefix), + DisperserStoreChunksSigningDisabled: ctx.GlobalBool(flags.DisperserStoreChunksSigningDisabledFlag.Name), + DisperserKMSKeyID: ctx.GlobalString(flags.DisperserKMSKeyIDFlag.Name), + LoggerConfig: *loggerConfig, EncodingManagerConfig: controller.EncodingManagerConfig{ PullInterval: ctx.GlobalDuration(flags.EncodingPullIntervalFlag.Name), EncodingRequestTimeout: ctx.GlobalDuration(flags.EncodingRequestTimeoutFlag.Name), diff --git a/disperser/cmd/controller/flags/flags.go b/disperser/cmd/controller/flags/flags.go index 45d5ffac3..96c7edc8b 100644 --- a/disperser/cmd/controller/flags/flags.go +++ b/disperser/cmd/controller/flags/flags.go @@ -178,10 +178,16 @@ var ( EnvVar: common.PrefixEnvVar(envVarPrefix, "METRICS_PORT"), Value: 9101, } + DisperserStoreChunksSigningDisabledFlag = cli.BoolFlag{ + Name: common.PrefixFlag(FlagPrefix, "disperser-store-chunks-signing-disabled"), + Usage: "Whether to disable signing of store chunks requests", + Required: false, + EnvVar: common.PrefixEnvVar(envVarPrefix, "DISPERSER_STORE_CHUNKS_SIGNING_DISABLED"), + } DisperserKMSKeyIDFlag = cli.StringFlag{ Name: common.PrefixFlag(FlagPrefix, "disperser-kms-key-id"), Usage: "Name of the key used to sign disperser requests (key must be stored in AWS KMS under this name)", - Required: true, + Required: false, EnvVar: common.PrefixEnvVar(envVarPrefix, "DISPERSER_KMS_KEY_ID"), } ) @@ -198,7 +204,6 @@ var requiredFlags = []cli.Flag{ DispatcherPullIntervalFlag, NodeRequestTimeoutFlag, NumConnectionsToNodesFlag, - DisperserKMSKeyIDFlag, } var optionalFlags = []cli.Flag{ @@ -217,6 +222,8 @@ var optionalFlags = []cli.Flag{ NodeClientCacheNumEntriesFlag, MaxBatchSizeFlag, MetricsPortFlag, + DisperserStoreChunksSigningDisabledFlag, + DisperserKMSKeyIDFlag, } var Flags []cli.Flag diff --git a/disperser/cmd/controller/main.go b/disperser/cmd/controller/main.go index 1d792091a..9ba3aed83 100644 --- a/disperser/cmd/controller/main.go +++ b/disperser/cmd/controller/main.go @@ -151,7 +151,7 @@ func RunController(ctx *cli.Context) error { } var requestSigner clients.RequestSigner - if config.DisperserKMSKeyID != "" { + if !config.DisperserStoreChunksSigningDisabled { requestSigner, err = clients.NewRequestSigner( context.Background(), config.AwsClientConfig.Region, diff --git a/inabox/deploy/config.go b/inabox/deploy/config.go index 2e5654a1c..1943ca5ab 100644 --- a/inabox/deploy/config.go +++ b/inabox/deploy/config.go @@ -323,27 +323,27 @@ func (env *Config) generateEncoderV2Vars(ind int, grpcPort string) EncoderVars { func (env *Config) generateControllerVars(ind int, graphUrl string) ControllerVars { v := ControllerVars{ - CONTROLLER_DYNAMODB_TABLE_NAME: "test-BlobMetadata-v2", - CONTROLLER_BLS_OPERATOR_STATE_RETRIVER: env.EigenDA.OperatorStateRetreiver, - CONTROLLER_EIGENDA_SERVICE_MANAGER: env.EigenDA.ServiceManager, - CONTROLLER_USE_GRAPH: "true", - CONTROLLER_GRAPH_URL: graphUrl, - CONTROLLER_ENCODING_PULL_INTERVAL: "1s", - CONTROLLER_AVAILABLE_RELAYS: "0,1,2,3", - CONTROLLER_DISPATCHER_PULL_INTERVAL: "3s", - CONTROLLER_NODE_REQUEST_TIMEOUT: "5s", - CONTROLLER_NUM_CONNECTIONS_TO_NODES: "10", - CONTROLLER_CHAIN_RPC: "", - CONTROLLER_PRIVATE_KEY: "123", - CONTROLLER_NUM_CONFIRMATIONS: "0", - CONTROLLER_INDEXER_PULL_INTERVAL: "1s", - CONTROLLER_AWS_REGION: "", - CONTROLLER_AWS_ACCESS_KEY_ID: "", - CONTROLLER_AWS_SECRET_ACCESS_KEY: "", - CONTROLLER_AWS_ENDPOINT_URL: "", - CONTROLLER_ENCODER_ADDRESS: "0.0.0.0:34001", - CONTROLLER_FINALIZATION_BLOCK_DELAY: "0", - CONTROLLER_DISPERSER_KMS_KEY_ID: "", + CONTROLLER_DYNAMODB_TABLE_NAME: "test-BlobMetadata-v2", + CONTROLLER_BLS_OPERATOR_STATE_RETRIVER: env.EigenDA.OperatorStateRetreiver, + CONTROLLER_EIGENDA_SERVICE_MANAGER: env.EigenDA.ServiceManager, + CONTROLLER_USE_GRAPH: "true", + CONTROLLER_GRAPH_URL: graphUrl, + CONTROLLER_ENCODING_PULL_INTERVAL: "1s", + CONTROLLER_AVAILABLE_RELAYS: "0,1,2,3", + CONTROLLER_DISPATCHER_PULL_INTERVAL: "3s", + CONTROLLER_NODE_REQUEST_TIMEOUT: "5s", + CONTROLLER_NUM_CONNECTIONS_TO_NODES: "10", + CONTROLLER_CHAIN_RPC: "", + CONTROLLER_PRIVATE_KEY: "123", + CONTROLLER_NUM_CONFIRMATIONS: "0", + CONTROLLER_INDEXER_PULL_INTERVAL: "1s", + CONTROLLER_AWS_REGION: "", + CONTROLLER_AWS_ACCESS_KEY_ID: "", + CONTROLLER_AWS_SECRET_ACCESS_KEY: "", + CONTROLLER_AWS_ENDPOINT_URL: "", + CONTROLLER_ENCODER_ADDRESS: "0.0.0.0:34001", + CONTROLLER_FINALIZATION_BLOCK_DELAY: "0", + CONTROLLER_DISPERSER_STORE_CHUNKS_SIGNING_DISABLED: "true", } env.applyDefaults(&v, "CONTROLLER", "controller", ind) diff --git a/inabox/deploy/env_vars.go b/inabox/deploy/env_vars.go index 9e6de5171..19ee4b9df 100644 --- a/inabox/deploy/env_vars.go +++ b/inabox/deploy/env_vars.go @@ -625,8 +625,6 @@ type ControllerVars struct { CONTROLLER_NUM_CONNECTIONS_TO_NODES string - CONTROLLER_DISPERSER_KMS_KEY_ID string - CONTROLLER_INDEXER_DATA_DIR string CONTROLLER_ENCODING_REQUEST_TIMEOUT string @@ -655,6 +653,10 @@ type ControllerVars struct { CONTROLLER_METRICS_PORT string + CONTROLLER_DISPERSER_STORE_CHUNKS_SIGNING_DISABLED string + + CONTROLLER_DISPERSER_KMS_KEY_ID string + CONTROLLER_CHAIN_RPC string CONTROLLER_CHAIN_RPC_FALLBACK string From 20216c4e8c31bf4966fcc6064a2697cbbf6a9fd5 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Thu, 19 Dec 2024 13:06:48 -0600 Subject: [PATCH 46/94] Cleanup. Signed-off-by: Cody Littley --- disperser/controller/node_client_manager_test.go | 2 -- node/grpc/server_v2_test.go | 1 - 2 files changed, 3 deletions(-) diff --git a/disperser/controller/node_client_manager_test.go b/disperser/controller/node_client_manager_test.go index c394340fe..070ff1490 100644 --- a/disperser/controller/node_client_manager_test.go +++ b/disperser/controller/node_client_manager_test.go @@ -45,5 +45,3 @@ func TestNodeClientManager(t *testing.T) { require.NotSame(t, client0, client4) } - -// TODO add test for request signing diff --git a/node/grpc/server_v2_test.go b/node/grpc/server_v2_test.go index 05bcd12db..b1784a7a6 100644 --- a/node/grpc/server_v2_test.go +++ b/node/grpc/server_v2_test.go @@ -85,7 +85,6 @@ func newTestComponents(t *testing.T, config *node.Config) *testComponents { node.BlobVersionParams.Store(v2.NewBlobVersionParameterMap(blobParamsMap)) // The eth client is only utilized for StoreChunks validation, which is disabled in these tests - // TODO enable auth for these tests var reader *coreeth.Reader server, err := grpc.NewServerV2( From 3c325ef68a20fd6327d1368644a461a5b1406343 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Fri, 20 Dec 2024 08:56:11 -0600 Subject: [PATCH 47/94] Made suggested changes. Signed-off-by: Cody Littley --- api/clients/mock/static_request_signer.go | 6 +++--- api/clients/v2/node_client.go | 7 ++++--- api/clients/v2/request_signer.go | 12 ++++++------ api/clients/v2/request_signer_test.go | 2 +- core/eth/reader.go | 6 +++++- disperser/cmd/controller/config.go | 3 +++ disperser/cmd/controller/main.go | 8 +++++--- disperser/controller/node_client_manager.go | 4 ++-- node/auth/authenticator.go | 2 +- node/auth/request_signing.go | 3 +++ node/flags/flags.go | 2 +- 11 files changed, 34 insertions(+), 21 deletions(-) diff --git a/api/clients/mock/static_request_signer.go b/api/clients/mock/static_request_signer.go index 44a7b3095..cb05368bc 100644 --- a/api/clients/mock/static_request_signer.go +++ b/api/clients/mock/static_request_signer.go @@ -8,15 +8,15 @@ import ( "github.com/Layr-Labs/eigenda/node/auth" ) -var _ clients.RequestSigner = &staticRequestSigner{} +var _ clients.DispersalRequestSigner = &staticRequestSigner{} -// StaticRequestSigner is a RequestSigner that signs requests with a static key (i.e. it doesn't use AWS KMS). +// StaticRequestSigner is a DispersalRequestSigner that signs requests with a static key (i.e. it doesn't use AWS KMS). // Useful for testing. type staticRequestSigner struct { key *ecdsa.PrivateKey } -func NewStaticRequestSigner(key *ecdsa.PrivateKey) clients.RequestSigner { +func NewStaticRequestSigner(key *ecdsa.PrivateKey) clients.DispersalRequestSigner { return &staticRequestSigner{ key: key, } diff --git a/api/clients/v2/node_client.go b/api/clients/v2/node_client.go index 5880022af..675901192 100644 --- a/api/clients/v2/node_client.go +++ b/api/clients/v2/node_client.go @@ -3,6 +3,7 @@ package clients import ( "context" "fmt" + "github.com/Layr-Labs/eigenda/node/auth" "sync" commonpb "github.com/Layr-Labs/eigenda/api/grpc/common/v2" @@ -27,14 +28,14 @@ type nodeClient struct { config *NodeClientConfig initOnce sync.Once conn *grpc.ClientConn - requestSigner RequestSigner + requestSigner DispersalRequestSigner dispersalClient nodegrpc.DispersalClient } var _ NodeClient = (*nodeClient)(nil) -func NewNodeClient(config *NodeClientConfig, requestSigner RequestSigner) (NodeClient, error) { +func NewNodeClient(config *NodeClientConfig, requestSigner DispersalRequestSigner) (NodeClient, error) { if config == nil || config.Hostname == "" || config.Port == "" { return nil, fmt.Errorf("invalid config: %v", config) } @@ -70,7 +71,7 @@ func (c *nodeClient) StoreChunks(ctx context.Context, batch *corev2.Batch) (*cor }, BlobCertificates: blobCerts, }, - DisperserID: 0, // this will need to be updated dispersers are decentralized + DisperserID: auth.EigenLabsDisperserID, // this will need to be updated when dispersers are decentralized } if c.requestSigner != nil { diff --git a/api/clients/v2/request_signer.go b/api/clients/v2/request_signer.go index 645a2ab5b..e00b6b3d6 100644 --- a/api/clients/v2/request_signer.go +++ b/api/clients/v2/request_signer.go @@ -11,14 +11,14 @@ import ( "github.com/aws/aws-sdk-go-v2/service/kms" ) -// RequestSigner encapsulates the logic for signing GetChunks requests. -type RequestSigner interface { +// DispersalRequestSigner encapsulates the logic for signing GetChunks requests. +type DispersalRequestSigner interface { // SignStoreChunksRequest signs a StoreChunksRequest. Does not modify the request // (i.e. it does not insert the signature). SignStoreChunksRequest(ctx context.Context, request *grpc.StoreChunksRequest) ([]byte, error) } -var _ RequestSigner = &requestSigner{} +var _ DispersalRequestSigner = &requestSigner{} type requestSigner struct { keyID string @@ -26,12 +26,12 @@ type requestSigner struct { keyManager *kms.Client } -// NewRequestSigner creates a new RequestSigner. -func NewRequestSigner( +// NewDispersalRequestSigner creates a new DispersalRequestSigner. +func NewDispersalRequestSigner( ctx context.Context, region string, endpoint string, - keyID string) (RequestSigner, error) { + keyID string) (DispersalRequestSigner, error) { keyManager := kms.New(kms.Options{ Region: region, diff --git a/api/clients/v2/request_signer_test.go b/api/clients/v2/request_signer_test.go index 153ad7eff..98e82b7a0 100644 --- a/api/clients/v2/request_signer_test.go +++ b/api/clients/v2/request_signer_test.go @@ -99,7 +99,7 @@ func TestRequestSigning(t *testing.T) { request := auth.RandomStoreChunksRequest(rand) request.Signature = nil - signer, err := NewRequestSigner(context.Background(), region, localstackHost, keyID) + signer, err := NewDispersalRequestSigner(context.Background(), region, localstackHost, keyID) require.NoError(t, err) // Test a valid signature. diff --git a/core/eth/reader.go b/core/eth/reader.go index 122965264..2c06264a3 100644 --- a/core/eth/reader.go +++ b/core/eth/reader.go @@ -948,8 +948,12 @@ func (t *Reader) GetDisperserAddress(ctx context.Context, disperserID uint32) (g }, disperserID) + var defaultAddress gethcommon.Address if err != nil { - return gethcommon.Address{}, fmt.Errorf("failed to get disperser address: %w", err) + return defaultAddress, fmt.Errorf("failed to get disperser address: %w", err) + } + if address == defaultAddress { + return defaultAddress, fmt.Errorf("disperser with id %d not found", disperserID) } return address, nil diff --git a/disperser/cmd/controller/config.go b/disperser/cmd/controller/config.go index b07b6d60e..5a81b5f51 100644 --- a/disperser/cmd/controller/config.go +++ b/disperser/cmd/controller/config.go @@ -97,5 +97,8 @@ func NewConfig(ctx *cli.Context) (Config, error) { EigenDAServiceManagerAddr: ctx.GlobalString(flags.EigenDAServiceManagerFlag.Name), MetricsPort: ctx.GlobalInt(flags.MetricsPortFlag.Name), } + if !config.DisperserStoreChunksSigningDisabled && config.DisperserKMSKeyID == "" { + return Config{}, fmt.Errorf("DisperserKMSKeyID is required when StoreChunks() signing is enabled") + } return config, nil } diff --git a/disperser/cmd/controller/main.go b/disperser/cmd/controller/main.go index 9ba3aed83..c6d4b5f7d 100644 --- a/disperser/cmd/controller/main.go +++ b/disperser/cmd/controller/main.go @@ -150,9 +150,11 @@ func RunController(ctx *cli.Context) error { } } - var requestSigner clients.RequestSigner - if !config.DisperserStoreChunksSigningDisabled { - requestSigner, err = clients.NewRequestSigner( + var requestSigner clients.DispersalRequestSigner + if config.DisperserStoreChunksSigningDisabled { + logger.Warn("StoreChunks() signing is disabled") + } else { + requestSigner, err = clients.NewDispersalRequestSigner( context.Background(), config.AwsClientConfig.Region, config.AwsClientConfig.EndpointURL, diff --git a/disperser/controller/node_client_manager.go b/disperser/controller/node_client_manager.go index ef8624ec1..a1669ba00 100644 --- a/disperser/controller/node_client_manager.go +++ b/disperser/controller/node_client_manager.go @@ -15,7 +15,7 @@ type NodeClientManager interface { type nodeClientManager struct { // nodeClients is a cache of node clients keyed by socket address nodeClients *lru.Cache[string, clients.NodeClient] - requestSigner clients.RequestSigner + requestSigner clients.DispersalRequestSigner logger logging.Logger } @@ -23,7 +23,7 @@ var _ NodeClientManager = (*nodeClientManager)(nil) func NewNodeClientManager( cacheSize int, - requestSigner clients.RequestSigner, + requestSigner clients.DispersalRequestSigner, logger logging.Logger) (NodeClientManager, error) { closeClient := func(socket string, value clients.NodeClient) { diff --git a/node/auth/authenticator.go b/node/auth/authenticator.go index 87621ec17..2d8eb75b7 100644 --- a/node/auth/authenticator.go +++ b/node/auth/authenticator.go @@ -91,7 +91,7 @@ func NewRequestAuthenticator( func (a *requestAuthenticator) preloadCache(ctx context.Context, now time.Time) error { // this will need to be updated for decentralized dispersers - _, err := a.getDisperserKey(ctx, now, 0) + _, err := a.getDisperserKey(ctx, now, EigenLabsDisperserID) if err != nil { return fmt.Errorf("failed to get operator key: %w", err) } diff --git a/node/auth/request_signing.go b/node/auth/request_signing.go index 24ca80b8b..76be13c21 100644 --- a/node/auth/request_signing.go +++ b/node/auth/request_signing.go @@ -13,6 +13,9 @@ import ( "hash" ) +// EigenLabsDisperserID is the ID of the disperser that is managed by Eigen Labs. +const EigenLabsDisperserID = 0 + // SignStoreChunksRequest signs the given StoreChunksRequest with the given private key. Does not // write the signature into the request. func SignStoreChunksRequest(key *ecdsa.PrivateKey, request *grpc.StoreChunksRequest) ([]byte, error) { diff --git a/node/flags/flags.go b/node/flags/flags.go index a472d6e08..0494b3a4f 100644 --- a/node/flags/flags.go +++ b/node/flags/flags.go @@ -263,7 +263,7 @@ var ( Usage: "The duration for which a disperser authentication is valid", Required: false, EnvVar: common.PrefixEnvVar(EnvVarPrefix, "DISPERSAL_AUTHENTICATION_TIMEOUT"), - Value: 5 * time.Minute, + Value: time.Minute, } // Test only, DO NOT USE the following flags in production From d4b705119cdf14e0ab335072efc8513af4652d4e Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Fri, 20 Dec 2024 10:52:23 -0600 Subject: [PATCH 48/94] Start messing around with external KMS keys. Signed-off-by: Cody Littley --- api/clients/v2/request_signer_test.go | 77 +++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/api/clients/v2/request_signer_test.go b/api/clients/v2/request_signer_test.go index 98e82b7a0..6a74a8b20 100644 --- a/api/clients/v2/request_signer_test.go +++ b/api/clients/v2/request_signer_test.go @@ -127,3 +127,80 @@ func TestRequestSigning(t *testing.T) { } } } + +// This is more an exercise with KMS than it is a new way of testing request signing. Verify that we can +// generate keys outside of KMS and use them to sign requests within KMS. +func TestRequestSigningWithFixedKeys(t *testing.T) { + rand := random.NewTestRandom(t) + setup(t) + defer teardown() + + // TODO the results of KMS signing using these keys directly + public, private := rand.ECDSA() + + keyManager := kms.New(kms.Options{ + Region: region, + BaseEndpoint: aws.String(localstackHost), + }) + + keyID := rand.String(32) + + // Prepare to upload a key + parameters, err := keyManager.GetParametersForImport(context.Background(), &kms.GetParametersForImportInput{ + KeyId: aws.String(keyID), + WrappingAlgorithm: types.AlgorithmSpecRsaAesKeyWrapSha256, + WrappingKeySpec: types.WrappingKeySpecRsa2048, + }) + require.NoError(t, err) + require.Equal(t, keyID, *parameters.KeyId) + + importToken := parameters.ImportToken + wrapperPublicKey := parameters.PublicKey + + // TODO convert key into expected format + // TODO encrypt the key + + // Upload the key + _, err = keyManager.ImportKeyMaterial(context.Background(), &kms.ImportKeyMaterialInput{ + ImportToken: importToken, + KeyId: aws.String(keyID), + EncryptedKeyMaterial: nil, + }) + require.NoError(t, err) + + ////////////////////////// + + key, err := common.LoadPublicKeyKMS(context.Background(), keyManager, keyID) + require.NoError(t, err) + + publicAddress := crypto.PubkeyToAddress(*key) + + request := auth.RandomStoreChunksRequest(rand) + request.Signature = nil + + signer, err := NewDispersalRequestSigner(context.Background(), region, localstackHost, keyID) + require.NoError(t, err) + + // Test a valid signature. + signature, err := signer.SignStoreChunksRequest(context.Background(), request) + require.NoError(t, err) + + require.Nil(t, request.Signature) + request.Signature = signature + err = auth.VerifyStoreChunksRequest(publicAddress, request) + require.NoError(t, err) + + // Changing a byte in the middle of the signature should make the verification fail + badSignature := make([]byte, len(signature)) + copy(badSignature, signature) + badSignature[10] = badSignature[10] + 1 + request.Signature = badSignature + err = auth.VerifyStoreChunksRequest(publicAddress, request) + require.Error(t, err) + + // Changing a byte in the middle of the request should make the verification fail + request.DisperserID = request.DisperserID + 1 + request.Signature = signature + err = auth.VerifyStoreChunksRequest(publicAddress, request) + require.Error(t, err) +} From 02ed38a7dad04258f29ad03ff1d079295bfed397 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Fri, 20 Dec 2024 11:20:42 -0600 Subject: [PATCH 49/94] tweak docker build Signed-off-by: Cody Littley --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index 62ba07305..c12ae2ba5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -94,6 +94,7 @@ RUN --mount=type=cache,target=/go/pkg/mod \ # Controller build stage FROM common-builder AS controller-builder +COPY node/auth /app/node/auth WORKDIR /app/disperser RUN --mount=type=cache,target=/go/pkg/mod \ --mount=type=cache,target=/root/.cache/go-build \ From 8560b8b455ae592bc0094203b25f44e66df4a141 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Fri, 20 Dec 2024 12:24:34 -0600 Subject: [PATCH 50/94] Remove test. Signed-off-by: Cody Littley --- api/clients/v2/request_signer_test.go | 77 --------------------------- 1 file changed, 77 deletions(-) diff --git a/api/clients/v2/request_signer_test.go b/api/clients/v2/request_signer_test.go index 6a74a8b20..98e82b7a0 100644 --- a/api/clients/v2/request_signer_test.go +++ b/api/clients/v2/request_signer_test.go @@ -127,80 +127,3 @@ func TestRequestSigning(t *testing.T) { } } } - -// This is more an exercise with KMS than it is a new way of testing request signing. Verify that we can -// generate keys outside of KMS and use them to sign requests within KMS. -func TestRequestSigningWithFixedKeys(t *testing.T) { - rand := random.NewTestRandom(t) - setup(t) - defer teardown() - - // TODO the results of KMS signing using these keys directly - public, private := rand.ECDSA() - - keyManager := kms.New(kms.Options{ - Region: region, - BaseEndpoint: aws.String(localstackHost), - }) - - keyID := rand.String(32) - - // Prepare to upload a key - parameters, err := keyManager.GetParametersForImport(context.Background(), &kms.GetParametersForImportInput{ - KeyId: aws.String(keyID), - WrappingAlgorithm: types.AlgorithmSpecRsaAesKeyWrapSha256, - WrappingKeySpec: types.WrappingKeySpecRsa2048, - }) - require.NoError(t, err) - require.Equal(t, keyID, *parameters.KeyId) - - importToken := parameters.ImportToken - wrapperPublicKey := parameters.PublicKey - - // TODO convert key into expected format - // TODO encrypt the key - - // Upload the key - _, err = keyManager.ImportKeyMaterial(context.Background(), &kms.ImportKeyMaterialInput{ - ImportToken: importToken, - KeyId: aws.String(keyID), - EncryptedKeyMaterial: nil, - }) - require.NoError(t, err) - - ////////////////////////// - - key, err := common.LoadPublicKeyKMS(context.Background(), keyManager, keyID) - require.NoError(t, err) - - publicAddress := crypto.PubkeyToAddress(*key) - - request := auth.RandomStoreChunksRequest(rand) - request.Signature = nil - - signer, err := NewDispersalRequestSigner(context.Background(), region, localstackHost, keyID) - require.NoError(t, err) - - // Test a valid signature. - signature, err := signer.SignStoreChunksRequest(context.Background(), request) - require.NoError(t, err) - - require.Nil(t, request.Signature) - request.Signature = signature - err = auth.VerifyStoreChunksRequest(publicAddress, request) - require.NoError(t, err) - - // Changing a byte in the middle of the signature should make the verification fail - badSignature := make([]byte, len(signature)) - copy(badSignature, signature) - badSignature[10] = badSignature[10] + 1 - request.Signature = badSignature - err = auth.VerifyStoreChunksRequest(publicAddress, request) - require.Error(t, err) - - // Changing a byte in the middle of the request should make the verification fail - request.DisperserID = request.DisperserID + 1 - request.Signature = signature - err = auth.VerifyStoreChunksRequest(publicAddress, request) - require.Error(t, err) -} From 613bd201b4293dcb870a144cd11fadc98d8a9174 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Fri, 20 Dec 2024 12:37:28 -0600 Subject: [PATCH 51/94] Generate kms key pair. Signed-off-by: Cody Littley --- inabox/deploy/config.go | 50 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/inabox/deploy/config.go b/inabox/deploy/config.go index 1943ca5ab..84cbce962 100644 --- a/inabox/deploy/config.go +++ b/inabox/deploy/config.go @@ -1,9 +1,16 @@ package deploy import ( + "context" "crypto/rand" "errors" "fmt" + "github.com/Layr-Labs/eigenda/common" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/kms" + "github.com/aws/aws-sdk-go-v2/service/kms/types" + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" "log" "math/big" "path/filepath" @@ -321,7 +328,11 @@ func (env *Config) generateEncoderV2Vars(ind int, grpcPort string) EncoderVars { return v } -func (env *Config) generateControllerVars(ind int, graphUrl string) ControllerVars { +func (env *Config) generateControllerVars( + ind int, + graphUrl string, + disperserKeyID string) ControllerVars { + v := ControllerVars{ CONTROLLER_DYNAMODB_TABLE_NAME: "test-BlobMetadata-v2", CONTROLLER_BLS_OPERATOR_STATE_RETRIVER: env.EigenDA.OperatorStateRetreiver, @@ -344,6 +355,7 @@ func (env *Config) generateControllerVars(ind int, graphUrl string) ControllerVa CONTROLLER_ENCODER_ADDRESS: "0.0.0.0:34001", CONTROLLER_FINALIZATION_BLOCK_DELAY: "0", CONTROLLER_DISPERSER_STORE_CHUNKS_SIGNING_DISABLED: "true", + CONTROLLER_DISPERSER_KMS_KEY_ID: disperserKeyID, } env.applyDefaults(&v, "CONTROLLER", "controller", ind) @@ -567,6 +579,33 @@ func (env *Config) getKey(name string) (key, address string) { return } +// GenerateDisperserKeypair generates a disperser keypair using AWS KMS. Returns the key ID and the public address. +func generateDisperserKeypair() (string, gethcommon.Address, error) { + keyManager := kms.New(kms.Options{ + Region: "us-east-1", + BaseEndpoint: aws.String("0.0.0.0:4570"), // TODO don't hard code this + }) + + createKeyOutput, err := keyManager.CreateKey(context.Background(), &kms.CreateKeyInput{ + KeySpec: types.KeySpecEccSecgP256k1, + KeyUsage: types.KeyUsageTypeSignVerify, + }) + if err != nil { + return "", gethcommon.Address{}, err + } + + keyID := *createKeyOutput.KeyMetadata.KeyId + + key, err := common.LoadPublicKeyKMS(context.Background(), keyManager, keyID) + if err != nil { + return "", gethcommon.Address{}, err + } + + publicAddress := crypto.PubkeyToAddress(*key) + + return keyID, publicAddress, nil +} + // GenerateAllVariables all of the config for the test environment. // Returns an object that corresponds to the participants of the // current experiment. @@ -583,6 +622,13 @@ func (env *Config) GenerateAllVariables() { // keys := strings.Split(string(keyData), "\n") // id := 1 + disperserKeyID, disperserAddress, err := generateDisperserKeypair() + if err != nil { + log.Fatalf("Error generating disperser keypair: %v", err) + } + // TODO do something more with the key ID + log.Printf("Disperser key ID: %s, address: %s", disperserKeyID, disperserAddress.Hex()) + // Create compose file composeFile := env.Path + "/docker-compose.yml" servicesMap := make(map[string]map[string]interface{}) @@ -728,7 +774,7 @@ func (env *Config) GenerateAllVariables() { // Controller name = "controller0" _, _, _, envFile = env.getPaths(name) - controllerConfig := env.generateControllerVars(0, graphUrl) + controllerConfig := env.generateControllerVars(0, graphUrl, disperserKeyID) writeEnv(controllerConfig.getEnvMap(), envFile) env.Controller = controllerConfig From 4912547a590d12368b2d8c1e70f23a27727fb382 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Fri, 20 Dec 2024 12:45:42 -0600 Subject: [PATCH 52/94] Fix localstack url Signed-off-by: Cody Littley --- inabox/deploy/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inabox/deploy/config.go b/inabox/deploy/config.go index 84cbce962..430c6b032 100644 --- a/inabox/deploy/config.go +++ b/inabox/deploy/config.go @@ -583,7 +583,7 @@ func (env *Config) getKey(name string) (key, address string) { func generateDisperserKeypair() (string, gethcommon.Address, error) { keyManager := kms.New(kms.Options{ Region: "us-east-1", - BaseEndpoint: aws.String("0.0.0.0:4570"), // TODO don't hard code this + BaseEndpoint: aws.String("http://0.0.0.0:4570"), // TODO don't hard code this }) createKeyOutput, err := keyManager.CreateKey(context.Background(), &kms.CreateKeyInput{ From 80c7e6d3a4579415991e647bdd786ba47134d2f3 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Fri, 20 Dec 2024 14:18:59 -0600 Subject: [PATCH 53/94] Make requested changes. Signed-off-by: Cody Littley --- api/clients/v2/node_client.go | 4 +- api/clients/v2/relay_client.go | 4 +- api/clients/v2/request_signer.go | 4 +- api/constants.go | 4 ++ api/hashing/node_hashing.go | 60 ++++++++++++++++++++++++ api/hashing/relay_hashing.go | 38 +++++++++++++++ api/hashing/utils.go | 20 ++++++++ node/auth/authenticator.go | 3 +- node/auth/request_signing.go | 74 ++---------------------------- node/auth/request_signing_test.go | 39 ++++++++-------- relay/auth/authenticator.go | 3 +- relay/auth/request_signing.go | 47 +------------------ relay/auth/request_signing_test.go | 11 +++-- 13 files changed, 163 insertions(+), 148 deletions(-) create mode 100644 api/constants.go create mode 100644 api/hashing/node_hashing.go create mode 100644 api/hashing/relay_hashing.go create mode 100644 api/hashing/utils.go diff --git a/api/clients/v2/node_client.go b/api/clients/v2/node_client.go index 675901192..0a379b7bf 100644 --- a/api/clients/v2/node_client.go +++ b/api/clients/v2/node_client.go @@ -3,7 +3,7 @@ package clients import ( "context" "fmt" - "github.com/Layr-Labs/eigenda/node/auth" + "github.com/Layr-Labs/eigenda/api" "sync" commonpb "github.com/Layr-Labs/eigenda/api/grpc/common/v2" @@ -71,7 +71,7 @@ func (c *nodeClient) StoreChunks(ctx context.Context, batch *corev2.Batch) (*cor }, BlobCertificates: blobCerts, }, - DisperserID: auth.EigenLabsDisperserID, // this will need to be updated when dispersers are decentralized + DisperserID: api.EigenLabsDisperserID, // this will need to be updated when dispersers are decentralized } if c.requestSigner != nil { diff --git a/api/clients/v2/relay_client.go b/api/clients/v2/relay_client.go index d6be36064..eb559aa78 100644 --- a/api/clients/v2/relay_client.go +++ b/api/clients/v2/relay_client.go @@ -4,8 +4,8 @@ import ( "context" "errors" "fmt" + "github.com/Layr-Labs/eigenda/api/hashing" "github.com/Layr-Labs/eigenda/core" - "github.com/Layr-Labs/eigenda/relay/auth" "sync" relaygrpc "github.com/Layr-Labs/eigenda/api/grpc/relay" @@ -115,7 +115,7 @@ func (c *relayClient) signGetChunksRequest(ctx context.Context, request *relaygr return errors.New("no message signer provided in config, cannot sign get chunks request") } - hash := auth.HashGetChunksRequest(request) + hash := hashing.HashGetChunksRequest(request) hashArray := [32]byte{} copy(hashArray[:], hash) signature, err := c.config.MessageSigner(ctx, hashArray) diff --git a/api/clients/v2/request_signer.go b/api/clients/v2/request_signer.go index e00b6b3d6..e0d8f47e0 100644 --- a/api/clients/v2/request_signer.go +++ b/api/clients/v2/request_signer.go @@ -5,8 +5,8 @@ import ( "crypto/ecdsa" "fmt" grpc "github.com/Layr-Labs/eigenda/api/grpc/node/v2" + "github.com/Layr-Labs/eigenda/api/hashing" "github.com/Layr-Labs/eigenda/common" - "github.com/Layr-Labs/eigenda/node/auth" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/kms" ) @@ -51,7 +51,7 @@ func NewDispersalRequestSigner( } func (s *requestSigner) SignStoreChunksRequest(ctx context.Context, request *grpc.StoreChunksRequest) ([]byte, error) { - hash := auth.HashStoreChunksRequest(request) + hash := hashing.HashStoreChunksRequest(request) signature, err := common.SignKMS(ctx, s.keyManager, s.keyID, s.publicKey, hash) if err != nil { diff --git a/api/constants.go b/api/constants.go new file mode 100644 index 000000000..0f3772cce --- /dev/null +++ b/api/constants.go @@ -0,0 +1,4 @@ +package api + +// EigenLabsDisperserID is the ID of the disperser that is managed by Eigen Labs. +const EigenLabsDisperserID = uint32(0) diff --git a/api/hashing/node_hashing.go b/api/hashing/node_hashing.go new file mode 100644 index 000000000..fafb11872 --- /dev/null +++ b/api/hashing/node_hashing.go @@ -0,0 +1,60 @@ +package hashing + +import ( + commonv1 "github.com/Layr-Labs/eigenda/api/grpc/common" + common "github.com/Layr-Labs/eigenda/api/grpc/common/v2" + grpc "github.com/Layr-Labs/eigenda/api/grpc/node/v2" + "golang.org/x/crypto/sha3" + "hash" +) + +// This file contains code for hashing gRPC messages that are sent to the DA node. + +// HashStoreChunksRequest hashes the given StoreChunksRequest. +func HashStoreChunksRequest(request *grpc.StoreChunksRequest) []byte { + hasher := sha3.NewLegacyKeccak256() + + hashBatchHeader(hasher, request.Batch.Header) + for _, blobCertificate := range request.Batch.BlobCertificates { + hashBlobCertificate(hasher, blobCertificate) + } + hashUint32(hasher, request.DisperserID) + + return hasher.Sum(nil) +} + +func hashBlobCertificate(hasher hash.Hash, blobCertificate *common.BlobCertificate) { + hashBlobHeader(hasher, blobCertificate.BlobHeader) + for _, relayID := range blobCertificate.Relays { + hashUint32(hasher, relayID) + } +} + +func hashBlobHeader(hasher hash.Hash, header *common.BlobHeader) { + hashUint32(hasher, header.Version) + for _, quorum := range header.QuorumNumbers { + hashUint32(hasher, quorum) + } + hashBlobCommitment(hasher, header.Commitment) + hashPaymentHeader(hasher, header.PaymentHeader) + hasher.Write(header.Signature) +} + +func hashBatchHeader(hasher hash.Hash, header *common.BatchHeader) { + hasher.Write(header.BatchRoot) + hashUint64(hasher, header.ReferenceBlockNumber) +} + +func hashBlobCommitment(hasher hash.Hash, commitment *commonv1.BlobCommitment) { + hasher.Write(commitment.Commitment) + hasher.Write(commitment.LengthCommitment) + hasher.Write(commitment.LengthProof) + hashUint32(hasher, commitment.Length) +} + +func hashPaymentHeader(hasher hash.Hash, header *commonv1.PaymentHeader) { + hasher.Write([]byte(header.AccountId)) + hashUint32(hasher, header.ReservationPeriod) + hasher.Write(header.CumulativePayment) + hashUint32(hasher, header.Salt) +} diff --git a/api/hashing/relay_hashing.go b/api/hashing/relay_hashing.go new file mode 100644 index 000000000..2dad5ecad --- /dev/null +++ b/api/hashing/relay_hashing.go @@ -0,0 +1,38 @@ +package hashing + +import ( + pb "github.com/Layr-Labs/eigenda/api/grpc/relay" + "golang.org/x/crypto/sha3" +) + +// This file contains code for hashing gRPC messages that are sent to the relay. + +var ( + iByte = []byte{0x69} + rByte = []byte{0x72} +) + +// HashGetChunksRequest hashes the given GetChunksRequest. +func HashGetChunksRequest(request *pb.GetChunksRequest) []byte { + hasher := sha3.NewLegacyKeccak256() + + hasher.Write(request.GetOperatorId()) + for _, chunkRequest := range request.GetChunkRequests() { + if chunkRequest.GetByIndex() != nil { + getByIndex := chunkRequest.GetByIndex() + hasher.Write(iByte) + hasher.Write(getByIndex.BlobKey) + for _, index := range getByIndex.ChunkIndices { + hashUint32(hasher, index) + } + } else { + getByRange := chunkRequest.GetByRange() + hasher.Write(rByte) + hasher.Write(getByRange.BlobKey) + hashUint32(hasher, getByRange.StartIndex) + hashUint32(hasher, getByRange.EndIndex) + } + } + + return hasher.Sum(nil) +} diff --git a/api/hashing/utils.go b/api/hashing/utils.go new file mode 100644 index 000000000..bc6f8d289 --- /dev/null +++ b/api/hashing/utils.go @@ -0,0 +1,20 @@ +package hashing + +import ( + "encoding/binary" + "hash" +) + +// hashUint32 hashes the given uint32 value. +func hashUint32(hasher hash.Hash, value uint32) { + bytes := make([]byte, 4) + binary.BigEndian.PutUint32(bytes, value) + hasher.Write(bytes) +} + +// hashUint64 hashes the given uint64 value. +func hashUint64(hasher hash.Hash, value uint64) { + bytes := make([]byte, 8) + binary.BigEndian.PutUint64(bytes, value) + hasher.Write(bytes) +} diff --git a/node/auth/authenticator.go b/node/auth/authenticator.go index 2d8eb75b7..3cb25e8c9 100644 --- a/node/auth/authenticator.go +++ b/node/auth/authenticator.go @@ -3,6 +3,7 @@ package auth import ( "context" "fmt" + "github.com/Layr-Labs/eigenda/api" grpc "github.com/Layr-Labs/eigenda/api/grpc/node/v2" "github.com/Layr-Labs/eigenda/core" gethcommon "github.com/ethereum/go-ethereum/common" @@ -91,7 +92,7 @@ func NewRequestAuthenticator( func (a *requestAuthenticator) preloadCache(ctx context.Context, now time.Time) error { // this will need to be updated for decentralized dispersers - _, err := a.getDisperserKey(ctx, now, EigenLabsDisperserID) + _, err := a.getDisperserKey(ctx, now, api.EigenLabsDisperserID) if err != nil { return fmt.Errorf("failed to get operator key: %w", err) } diff --git a/node/auth/request_signing.go b/node/auth/request_signing.go index 76be13c21..6e660bbfa 100644 --- a/node/auth/request_signing.go +++ b/node/auth/request_signing.go @@ -2,24 +2,17 @@ package auth import ( "crypto/ecdsa" - "encoding/binary" "fmt" - commonv1 "github.com/Layr-Labs/eigenda/api/grpc/common" - common "github.com/Layr-Labs/eigenda/api/grpc/common/v2" grpc "github.com/Layr-Labs/eigenda/api/grpc/node/v2" + "github.com/Layr-Labs/eigenda/api/hashing" gethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "golang.org/x/crypto/sha3" - "hash" ) -// EigenLabsDisperserID is the ID of the disperser that is managed by Eigen Labs. -const EigenLabsDisperserID = 0 - // SignStoreChunksRequest signs the given StoreChunksRequest with the given private key. Does not // write the signature into the request. func SignStoreChunksRequest(key *ecdsa.PrivateKey, request *grpc.StoreChunksRequest) ([]byte, error) { - requestHash := HashStoreChunksRequest(request) + requestHash := hashing.HashStoreChunksRequest(request) signature, err := crypto.Sign(requestHash, key) if err != nil { @@ -32,7 +25,7 @@ func SignStoreChunksRequest(key *ecdsa.PrivateKey, request *grpc.StoreChunksRequ // VerifyStoreChunksRequest verifies the given signature of the given StoreChunksRequest with the given // public key. func VerifyStoreChunksRequest(key gethcommon.Address, request *grpc.StoreChunksRequest) error { - requestHash := HashStoreChunksRequest(request) + requestHash := hashing.HashStoreChunksRequest(request) signingPublicKey, err := crypto.SigToPub(requestHash, request.Signature) if err != nil { @@ -46,64 +39,3 @@ func VerifyStoreChunksRequest(key gethcommon.Address, request *grpc.StoreChunksR } return nil } - -// HashStoreChunksRequest hashes the given StoreChunksRequest. -func HashStoreChunksRequest(request *grpc.StoreChunksRequest) []byte { - hasher := sha3.NewLegacyKeccak256() - - hashBatchHeader(hasher, request.Batch.Header) - for _, blobCertificate := range request.Batch.BlobCertificates { - hashBlobCertificate(hasher, blobCertificate) - } - hashUint32(hasher, request.DisperserID) - - return hasher.Sum(nil) -} - -func hashBlobCertificate(hasher hash.Hash, blobCertificate *common.BlobCertificate) { - hashBlobHeader(hasher, blobCertificate.BlobHeader) - for _, relayID := range blobCertificate.Relays { - hashUint32(hasher, relayID) - } -} - -func hashBlobHeader(hasher hash.Hash, header *common.BlobHeader) { - hashUint32(hasher, header.Version) - for _, quorum := range header.QuorumNumbers { - hashUint32(hasher, quorum) - } - hashBlobCommitment(hasher, header.Commitment) - hashPaymentHeader(hasher, header.PaymentHeader) - hasher.Write(header.Signature) -} - -func hashBatchHeader(hasher hash.Hash, header *common.BatchHeader) { - hasher.Write(header.BatchRoot) - hashUint64(hasher, header.ReferenceBlockNumber) -} - -func hashBlobCommitment(hasher hash.Hash, commitment *commonv1.BlobCommitment) { - hasher.Write(commitment.Commitment) - hasher.Write(commitment.LengthCommitment) - hasher.Write(commitment.LengthProof) - hashUint32(hasher, commitment.Length) -} - -func hashPaymentHeader(hasher hash.Hash, header *commonv1.PaymentHeader) { - hasher.Write([]byte(header.AccountId)) - hashUint32(hasher, header.ReservationPeriod) - hasher.Write(header.CumulativePayment) - hashUint32(hasher, header.Salt) -} - -func hashUint32(hasher hash.Hash, value uint32) { - bytes := make([]byte, 4) - binary.BigEndian.PutUint32(bytes, value) - hasher.Write(bytes) -} - -func hashUint64(hasher hash.Hash, value uint64) { - bytes := make([]byte, 8) - binary.BigEndian.PutUint64(bytes, value) - hasher.Write(bytes) -} diff --git a/node/auth/request_signing_test.go b/node/auth/request_signing_test.go index f43ac75ca..e4122c18b 100644 --- a/node/auth/request_signing_test.go +++ b/node/auth/request_signing_test.go @@ -1,6 +1,7 @@ package auth import ( + "github.com/Layr-Labs/eigenda/api/hashing" "github.com/Layr-Labs/eigenda/common/testutils/random" "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/require" @@ -11,32 +12,32 @@ func TestHashing(t *testing.T) { rand := random.NewTestRandom(t) request := RandomStoreChunksRequest(rand) - originalRequestHash := HashStoreChunksRequest(request) + originalRequestHash := hashing.HashStoreChunksRequest(request) // modifying the signature should not change the hash request.Signature = rand.Bytes(32) - hash := HashStoreChunksRequest(request) + hash := hashing.HashStoreChunksRequest(request) require.Equal(t, originalRequestHash, hash) // modify the disperser id rand.Reset() request = RandomStoreChunksRequest(rand) request.DisperserID = request.DisperserID + 1 - hash = HashStoreChunksRequest(request) + hash = hashing.HashStoreChunksRequest(request) require.NotEqual(t, originalRequestHash, hash) // remove a blob cert rand.Reset() request = RandomStoreChunksRequest(rand) request.Batch.BlobCertificates = request.Batch.BlobCertificates[:len(request.Batch.BlobCertificates)-1] - hash = HashStoreChunksRequest(request) + hash = hashing.HashStoreChunksRequest(request) require.NotEqual(t, originalRequestHash, hash) // within a blob cert, modify a relay rand.Reset() request = RandomStoreChunksRequest(rand) request.Batch.BlobCertificates[0].Relays[0] = request.Batch.BlobCertificates[0].Relays[0] + 1 - hash = HashStoreChunksRequest(request) + hash = hashing.HashStoreChunksRequest(request) require.NotEqual(t, originalRequestHash, hash) // within a blob cert, remove a relay @@ -44,14 +45,14 @@ func TestHashing(t *testing.T) { request = RandomStoreChunksRequest(rand) request.Batch.BlobCertificates[0].Relays = request.Batch.BlobCertificates[0].Relays[:len(request.Batch.BlobCertificates[0].Relays)-1] - hash = HashStoreChunksRequest(request) + hash = hashing.HashStoreChunksRequest(request) require.NotEqual(t, originalRequestHash, hash) // within a blob cert, add a relay rand.Reset() request = RandomStoreChunksRequest(rand) request.Batch.BlobCertificates[0].Relays = append(request.Batch.BlobCertificates[0].Relays, rand.Uint32()) - hash = HashStoreChunksRequest(request) + hash = hashing.HashStoreChunksRequest(request) require.NotEqual(t, originalRequestHash, hash) // within a blob cert, modify a quorum number @@ -59,7 +60,7 @@ func TestHashing(t *testing.T) { request = RandomStoreChunksRequest(rand) request.Batch.BlobCertificates[0].BlobHeader.QuorumNumbers[0] = request.Batch.BlobCertificates[0].BlobHeader.QuorumNumbers[0] + 1 - hash = HashStoreChunksRequest(request) + hash = hashing.HashStoreChunksRequest(request) require.NotEqual(t, originalRequestHash, hash) // within a blob cert, remove a quorum number @@ -68,7 +69,7 @@ func TestHashing(t *testing.T) { request.Batch.BlobCertificates[0].BlobHeader.QuorumNumbers = request.Batch.BlobCertificates[0].BlobHeader.QuorumNumbers[:len( request.Batch.BlobCertificates[0].BlobHeader.QuorumNumbers)-1] - hash = HashStoreChunksRequest(request) + hash = hashing.HashStoreChunksRequest(request) require.NotEqual(t, originalRequestHash, hash) // within a blob cert, add a quorum number @@ -76,70 +77,70 @@ func TestHashing(t *testing.T) { request = RandomStoreChunksRequest(rand) request.Batch.BlobCertificates[0].BlobHeader.QuorumNumbers = append( request.Batch.BlobCertificates[0].BlobHeader.QuorumNumbers, rand.Uint32()) - hash = HashStoreChunksRequest(request) + hash = hashing.HashStoreChunksRequest(request) require.NotEqual(t, originalRequestHash, hash) // within a blob cert, modify the Commitment.Commitment rand.Reset() request = RandomStoreChunksRequest(rand) request.Batch.BlobCertificates[0].BlobHeader.Commitment.Commitment = rand.Bytes(32) - hash = HashStoreChunksRequest(request) + hash = hashing.HashStoreChunksRequest(request) require.NotEqual(t, originalRequestHash, hash) // within a blob cert, modify the Commitment.LengthCommitment rand.Reset() request = RandomStoreChunksRequest(rand) request.Batch.BlobCertificates[0].BlobHeader.Commitment.LengthCommitment = rand.Bytes(32) - hash = HashStoreChunksRequest(request) + hash = hashing.HashStoreChunksRequest(request) require.NotEqual(t, originalRequestHash, hash) // within a blob cert, modify the Commitment.LengthProof rand.Reset() request = RandomStoreChunksRequest(rand) request.Batch.BlobCertificates[0].BlobHeader.Commitment.LengthProof = rand.Bytes(32) - hash = HashStoreChunksRequest(request) + hash = hashing.HashStoreChunksRequest(request) require.NotEqual(t, originalRequestHash, hash) // within a blob cert, modify the Commitment.Length rand.Reset() request = RandomStoreChunksRequest(rand) request.Batch.BlobCertificates[0].BlobHeader.Commitment.Length = rand.Uint32() - hash = HashStoreChunksRequest(request) + hash = hashing.HashStoreChunksRequest(request) require.NotEqual(t, originalRequestHash, hash) // within a blob cert, modify the PaymentHeader.AccountId rand.Reset() request = RandomStoreChunksRequest(rand) request.Batch.BlobCertificates[0].BlobHeader.PaymentHeader.AccountId = rand.String(32) - hash = HashStoreChunksRequest(request) + hash = hashing.HashStoreChunksRequest(request) require.NotEqual(t, originalRequestHash, hash) // within a blob cert, modify the PaymentHeader.ReservationPeriod rand.Reset() request = RandomStoreChunksRequest(rand) request.Batch.BlobCertificates[0].BlobHeader.PaymentHeader.ReservationPeriod = rand.Uint32() - hash = HashStoreChunksRequest(request) + hash = hashing.HashStoreChunksRequest(request) require.NotEqual(t, originalRequestHash, hash) // within a blob cert, modify the PaymentHeader.CumulativePayment rand.Reset() request = RandomStoreChunksRequest(rand) request.Batch.BlobCertificates[0].BlobHeader.PaymentHeader.CumulativePayment = rand.Bytes(32) - hash = HashStoreChunksRequest(request) + hash = hashing.HashStoreChunksRequest(request) require.NotEqual(t, originalRequestHash, hash) // within a blob cert, modify the PaymentHeader.Salt rand.Reset() request = RandomStoreChunksRequest(rand) request.Batch.BlobCertificates[0].BlobHeader.PaymentHeader.Salt = rand.Uint32() - hash = HashStoreChunksRequest(request) + hash = hashing.HashStoreChunksRequest(request) require.NotEqual(t, originalRequestHash, hash) // within a blob cert, modify the Signature rand.Reset() request = RandomStoreChunksRequest(rand) request.Batch.BlobCertificates[0].BlobHeader.Signature = rand.Bytes(32) - hash = HashStoreChunksRequest(request) + hash = hashing.HashStoreChunksRequest(request) require.NotEqual(t, originalRequestHash, hash) } diff --git a/relay/auth/authenticator.go b/relay/auth/authenticator.go index 664979d36..caf611b16 100644 --- a/relay/auth/authenticator.go +++ b/relay/auth/authenticator.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "github.com/Layr-Labs/eigenda/api/hashing" "sync" "time" @@ -129,7 +130,7 @@ func (a *requestAuthenticator) AuthenticateGetChunksRequest( G1Point: g1Point, } - hash := HashGetChunksRequest(request) + hash := hashing.HashGetChunksRequest(request) isValid := signature.Verify(key, ([32]byte)(hash)) if !isValid { diff --git a/relay/auth/request_signing.go b/relay/auth/request_signing.go index 149992bce..f08666122 100644 --- a/relay/auth/request_signing.go +++ b/relay/auth/request_signing.go @@ -1,58 +1,15 @@ package auth import ( - "encoding/binary" pb "github.com/Layr-Labs/eigenda/api/grpc/relay" + "github.com/Layr-Labs/eigenda/api/hashing" "github.com/Layr-Labs/eigenda/core" - "golang.org/x/crypto/sha3" ) -var ( - iByte = []byte{0x69} - rByte = []byte{0x72} -) - -// HashGetChunksRequest hashes the given GetChunksRequest. -func HashGetChunksRequest(request *pb.GetChunksRequest) []byte { - - // Protobuf serialization is non-deterministic, so we can't just hash the - // serialized bytes. Instead, we have to define our own hashing function. - - hasher := sha3.NewLegacyKeccak256() - - hasher.Write(request.GetOperatorId()) - for _, chunkRequest := range request.GetChunkRequests() { - if chunkRequest.GetByIndex() != nil { - getByIndex := chunkRequest.GetByIndex() - hasher.Write(iByte) - hasher.Write(getByIndex.BlobKey) - for _, index := range getByIndex.ChunkIndices { - indexBytes := make([]byte, 4) - binary.BigEndian.PutUint32(indexBytes, index) - hasher.Write(indexBytes) - } - } else { - getByRange := chunkRequest.GetByRange() - hasher.Write(rByte) - hasher.Write(getByRange.BlobKey) - - startBytes := make([]byte, 4) - binary.BigEndian.PutUint32(startBytes, getByRange.StartIndex) - hasher.Write(startBytes) - - endBytes := make([]byte, 4) - binary.BigEndian.PutUint32(endBytes, getByRange.EndIndex) - hasher.Write(endBytes) - } - } - - return hasher.Sum(nil) -} - // SignGetChunksRequest signs the given GetChunksRequest with the given private key. Does not // write the signature into the request. func SignGetChunksRequest(keys *core.KeyPair, request *pb.GetChunksRequest) []byte { - hash := HashGetChunksRequest(request) + hash := hashing.HashGetChunksRequest(request) signature := keys.SignMessage(([32]byte)(hash)) return signature.G1Point.Serialize() } diff --git a/relay/auth/request_signing_test.go b/relay/auth/request_signing_test.go index 3c0518851..968052cf1 100644 --- a/relay/auth/request_signing_test.go +++ b/relay/auth/request_signing_test.go @@ -2,6 +2,7 @@ package auth import ( pb "github.com/Layr-Labs/eigenda/api/grpc/relay" + "github.com/Layr-Labs/eigenda/api/hashing" tu "github.com/Layr-Labs/eigenda/common/testutils" "github.com/stretchr/testify/require" "golang.org/x/exp/rand" @@ -51,21 +52,21 @@ func TestHashGetChunksRequest(t *testing.T) { requestB := randomGetChunksRequest() // Hashing the same request twice should yield the same hash - hashA := HashGetChunksRequest(requestA) - hashAA := HashGetChunksRequest(requestA) + hashA := hashing.HashGetChunksRequest(requestA) + hashAA := hashing.HashGetChunksRequest(requestA) require.Equal(t, hashA, hashAA) // Hashing different requests should yield different hashes - hashB := HashGetChunksRequest(requestB) + hashB := hashing.HashGetChunksRequest(requestB) require.NotEqual(t, hashA, hashB) // Adding a signature should not affect the hash requestA.OperatorSignature = tu.RandomBytes(32) - hashAA = HashGetChunksRequest(requestA) + hashAA = hashing.HashGetChunksRequest(requestA) require.Equal(t, hashA, hashAA) // Changing the requester ID should change the hash requestA.OperatorId = tu.RandomBytes(32) - hashAA = HashGetChunksRequest(requestA) + hashAA = hashing.HashGetChunksRequest(requestA) require.NotEqual(t, hashA, hashAA) } From 2b6cbb2cc8b2ced8a8dee1b05c6475d79c48841c Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Fri, 20 Dec 2024 14:42:33 -0600 Subject: [PATCH 54/94] Made suggested changes. Signed-off-by: Cody Littley --- .../v2/{request_signer.go => dispersal_request_signer.go} | 0 ...uest_signer_test.go => dispersal_request_signer_test.go} | 0 node/auth/authenticator.go | 6 +++--- 3 files changed, 3 insertions(+), 3 deletions(-) rename api/clients/v2/{request_signer.go => dispersal_request_signer.go} (100%) rename api/clients/v2/{request_signer_test.go => dispersal_request_signer_test.go} (100%) diff --git a/api/clients/v2/request_signer.go b/api/clients/v2/dispersal_request_signer.go similarity index 100% rename from api/clients/v2/request_signer.go rename to api/clients/v2/dispersal_request_signer.go diff --git a/api/clients/v2/request_signer_test.go b/api/clients/v2/dispersal_request_signer_test.go similarity index 100% rename from api/clients/v2/request_signer_test.go rename to api/clients/v2/dispersal_request_signer_test.go diff --git a/node/auth/authenticator.go b/node/auth/authenticator.go index 3cb25e8c9..3a3be7139 100644 --- a/node/auth/authenticator.go +++ b/node/auth/authenticator.go @@ -121,7 +121,7 @@ func (a *requestAuthenticator) AuthenticateStoreChunksRequest( return fmt.Errorf("failed to verify request: %w", err) } - a.saveAuthenticationResult(now, origin) + a.cacheAuthenticationResult(now, origin) return nil } @@ -151,8 +151,8 @@ func (a *requestAuthenticator) getDisperserKey( return &address, nil } -// saveAuthenticationResult saves the result of an auth. -func (a *requestAuthenticator) saveAuthenticationResult(now time.Time, origin string) { +// cacheAuthenticationResult saves the result of an auth. +func (a *requestAuthenticator) cacheAuthenticationResult(now time.Time, origin string) { if a.authenticationTimeoutDuration == 0 { // Authentication saving is disabled. return From a57140cdf766a1c7bd3768e9d79a8df4420ce711 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Thu, 2 Jan 2025 10:07:02 -0600 Subject: [PATCH 55/94] Add debug stack trace. Signed-off-by: Cody Littley --- inabox/deploy/config.go | 2 +- inabox/deploy/deploy.go | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/inabox/deploy/config.go b/inabox/deploy/config.go index 430c6b032..d2ab28f67 100644 --- a/inabox/deploy/config.go +++ b/inabox/deploy/config.go @@ -444,7 +444,7 @@ func (env *Config) generateOperatorVars(ind int, name, key, churnerUrl, logPath, NODE_NUM_CONFIRMATIONS: "0", NODE_ONCHAIN_METRICS_INTERVAL: "-1", NODE_ENABLE_V2: "true", - NODE_DISABLE_DISPERSAL_AUTHENTICATION: "true", + NODE_DISABLE_DISPERSAL_AUTHENTICATION: "false", } env.applyDefaults(&v, "NODE", "opr", ind) diff --git a/inabox/deploy/deploy.go b/inabox/deploy/deploy.go index be81c26a3..5c096bf40 100644 --- a/inabox/deploy/deploy.go +++ b/inabox/deploy/deploy.go @@ -9,6 +9,7 @@ import ( "math/big" "os" "path/filepath" + "runtime/debug" "strconv" "github.com/Layr-Labs/eigenda/common" @@ -155,6 +156,10 @@ func (env *Config) DeployExperiment() { env.deploySubgraphs(startBlock) } + // TODO remove + debug.PrintStack() + log.Print(debug.Stack()) + fmt.Println("Generating variables") env.GenerateAllVariables() From 11e2ffa348f63adf4b20c58f0f57b3dea96e369f Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Thu, 2 Jan 2025 10:14:27 -0600 Subject: [PATCH 56/94] Tweak stack trace. Signed-off-by: Cody Littley --- inabox/deploy/deploy.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/inabox/deploy/deploy.go b/inabox/deploy/deploy.go index 5c096bf40..d3cb765d1 100644 --- a/inabox/deploy/deploy.go +++ b/inabox/deploy/deploy.go @@ -9,7 +9,7 @@ import ( "math/big" "os" "path/filepath" - "runtime/debug" + "runtime" "strconv" "github.com/Layr-Labs/eigenda/common" @@ -157,8 +157,10 @@ func (env *Config) DeployExperiment() { } // TODO remove - debug.PrintStack() - log.Print(debug.Stack()) + b := make([]byte, 2048) // adjust buffer size to be larger than expected stack + n := runtime.Stack(b, false) + s := string(b[:n]) + log.Printf("Stack trace:\n %s", s) fmt.Println("Generating variables") env.GenerateAllVariables() From ad893960333d58b1f491a384fab43764327ac62b Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Thu, 2 Jan 2025 10:33:59 -0600 Subject: [PATCH 57/94] Tweak aws URL Signed-off-by: Cody Littley --- inabox/deploy/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inabox/deploy/config.go b/inabox/deploy/config.go index d2ab28f67..fc229f04f 100644 --- a/inabox/deploy/config.go +++ b/inabox/deploy/config.go @@ -583,7 +583,7 @@ func (env *Config) getKey(name string) (key, address string) { func generateDisperserKeypair() (string, gethcommon.Address, error) { keyManager := kms.New(kms.Options{ Region: "us-east-1", - BaseEndpoint: aws.String("http://0.0.0.0:4570"), // TODO don't hard code this + BaseEndpoint: aws.String("http://localhost:4570"), // TODO don't hard code this }) createKeyOutput, err := keyManager.CreateKey(context.Background(), &kms.CreateKeyInput{ From a2b602e8d7e9df2f744841c6f71343bc8cad7c28 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Thu, 2 Jan 2025 10:51:32 -0600 Subject: [PATCH 58/94] Manually start localstack. Signed-off-by: Cody Littley --- inabox/deploy/config.go | 2 +- operators/churner/tests/churner_test.go | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/inabox/deploy/config.go b/inabox/deploy/config.go index fc229f04f..780096455 100644 --- a/inabox/deploy/config.go +++ b/inabox/deploy/config.go @@ -444,7 +444,7 @@ func (env *Config) generateOperatorVars(ind int, name, key, churnerUrl, logPath, NODE_NUM_CONFIRMATIONS: "0", NODE_ONCHAIN_METRICS_INTERVAL: "-1", NODE_ENABLE_V2: "true", - NODE_DISABLE_DISPERSAL_AUTHENTICATION: "false", + NODE_DISABLE_DISPERSAL_AUTHENTICATION: "true", } env.applyDefaults(&v, "NODE", "opr", ind) diff --git a/operators/churner/tests/churner_test.go b/operators/churner/tests/churner_test.go index ba9f11c52..ae71ede7c 100644 --- a/operators/churner/tests/churner_test.go +++ b/operators/churner/tests/churner_test.go @@ -66,6 +66,13 @@ func setup(m *testing.M) { } } + // TODO this probably doesn't belong here + localStackPort := "4570" + _, _, err := deploy.StartDockertestWithLocalstackContainer(localStackPort) + if err != nil { + log.Panicf("Failed to start dockertest with localstack container: %v", err) + } + testConfig = deploy.NewTestConfig(testName, rootPath) testConfig.Deployers[0].DeploySubgraphs = false From db018f0b7d6a22a5c524c4d71df035b73a6b4763 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Thu, 2 Jan 2025 11:34:58 -0600 Subject: [PATCH 59/94] Don't start localstack for churner test. Signed-off-by: Cody Littley --- inabox/deploy/config.go | 5 ----- operators/churner/tests/churner_test.go | 7 ------- 2 files changed, 12 deletions(-) diff --git a/inabox/deploy/config.go b/inabox/deploy/config.go index 780096455..99758ab69 100644 --- a/inabox/deploy/config.go +++ b/inabox/deploy/config.go @@ -617,11 +617,6 @@ func (env *Config) GenerateAllVariables() { createDirectory(env.Path + "/envs") changeDirectory(env.rootPath + "/inabox") - // Gather keys - // keyData := readFile(gethPrivateKeys) - // keys := strings.Split(string(keyData), "\n") - // id := 1 - disperserKeyID, disperserAddress, err := generateDisperserKeypair() if err != nil { log.Fatalf("Error generating disperser keypair: %v", err) diff --git a/operators/churner/tests/churner_test.go b/operators/churner/tests/churner_test.go index ae71ede7c..ba9f11c52 100644 --- a/operators/churner/tests/churner_test.go +++ b/operators/churner/tests/churner_test.go @@ -66,13 +66,6 @@ func setup(m *testing.M) { } } - // TODO this probably doesn't belong here - localStackPort := "4570" - _, _, err := deploy.StartDockertestWithLocalstackContainer(localStackPort) - if err != nil { - log.Panicf("Failed to start dockertest with localstack container: %v", err) - } - testConfig = deploy.NewTestConfig(testName, rootPath) testConfig.Deployers[0].DeploySubgraphs = false From d1ac4a8df032e5916d65ec8660a908d6dce7e812 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Thu, 2 Jan 2025 11:41:14 -0600 Subject: [PATCH 60/94] Skip key generation when localstack is not running. Signed-off-by: Cody Littley --- inabox/deploy/config.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/inabox/deploy/config.go b/inabox/deploy/config.go index 99758ab69..fc77d5f56 100644 --- a/inabox/deploy/config.go +++ b/inabox/deploy/config.go @@ -586,11 +586,17 @@ func generateDisperserKeypair() (string, gethcommon.Address, error) { BaseEndpoint: aws.String("http://localhost:4570"), // TODO don't hard code this }) + log.Printf("Generating disperser keypair") + createKeyOutput, err := keyManager.CreateKey(context.Background(), &kms.CreateKeyInput{ KeySpec: types.KeySpecEccSecgP256k1, KeyUsage: types.KeyUsageTypeSignVerify, }) if err != nil { + if strings.Contains(err.Error(), "connect: connection refused") { + log.Printf("Unable to reach local stack, skipping disperser keypair generation. Error: %v", err) + err = nil + } return "", gethcommon.Address{}, err } @@ -603,6 +609,8 @@ func generateDisperserKeypair() (string, gethcommon.Address, error) { publicAddress := crypto.PubkeyToAddress(*key) + log.Printf("Generated disperser keypair: key ID: %s, address: %s", keyID, publicAddress.Hex()) + return keyID, publicAddress, nil } From 822ab3d9d7a41c785dafe9839770c1d5109bbec5 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Thu, 2 Jan 2025 12:49:26 -0600 Subject: [PATCH 61/94] Write code that sets disperser address. Signed-off-by: Cody Littley --- core/eth/writer.go | 33 ++++++++++++++++++++++++++ inabox/deploy/config.go | 3 +-- inabox/deploy/config_types.go | 4 ++++ inabox/tests/integration_suite_test.go | 5 ++++ 4 files changed, 43 insertions(+), 2 deletions(-) diff --git a/core/eth/writer.go b/core/eth/writer.go index d088b6871..486d948ce 100644 --- a/core/eth/writer.go +++ b/core/eth/writer.go @@ -6,6 +6,9 @@ import ( "encoding/hex" "encoding/json" "fmt" + "github.com/Layr-Labs/eigenda/api" + contractEigenDADisperserRegistry "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDADisperserRegistry" + "log" "math/big" "github.com/Layr-Labs/eigenda/api/grpc/churner" @@ -329,3 +332,33 @@ func (t *Writer) ConfirmBatch(ctx context.Context, batchHeader *core.BatchHeader } return receipt, nil } + +// SetDisperserAddress sets the address of the disperser. Since there is currently only one disperser, this function +// can only be used to set the address of that disperser. +func (t *Writer) SetDisperserAddress(ctx context.Context, address gethcommon.Address) error { + registry := t.bindings.DisperserRegistry + if registry == nil { + return errors.New("disperser registry not deployed") + } + + log.Printf("Setting disperser %d address to %s", api.EigenLabsDisperserID, address.String()) + + options, err := t.ethClient.GetNoSendTransactOpts() + if err != nil { + t.logger.Error("Failed to generate transact opts", "err", err) + return err + } + options.Context = ctx + + _, err = registry.SetDisperserInfo( + options, + api.EigenLabsDisperserID, + contractEigenDADisperserRegistry.DisperserInfo{ + DisperserAddress: address, + }) + if err != nil { + return fmt.Errorf("failed to set disperser address: %w", err) + } + + return nil +} diff --git a/inabox/deploy/config.go b/inabox/deploy/config.go index fc77d5f56..f6609aef5 100644 --- a/inabox/deploy/config.go +++ b/inabox/deploy/config.go @@ -629,8 +629,7 @@ func (env *Config) GenerateAllVariables() { if err != nil { log.Fatalf("Error generating disperser keypair: %v", err) } - // TODO do something more with the key ID - log.Printf("Disperser key ID: %s, address: %s", disperserKeyID, disperserAddress.Hex()) + env.DisperserAddress = disperserAddress // Create compose file composeFile := env.Path + "/docker-compose.yml" diff --git a/inabox/deploy/config_types.go b/inabox/deploy/config_types.go index 2d1f04d5e..193a59cd2 100644 --- a/inabox/deploy/config_types.go +++ b/inabox/deploy/config_types.go @@ -2,6 +2,7 @@ package deploy import ( "encoding/json" + "github.com/ethereum/go-ethereum/common" "log" "os" "path/filepath" @@ -181,6 +182,9 @@ type Config struct { Retriever RetrieverVars Controller ControllerVars Relays []RelayVars + + // DisperserAddress is the address of disperser 0 (aka the only disperser at the current time) + DisperserAddress common.Address } func (c Config) IsEigenDADeployed() bool { diff --git a/inabox/tests/integration_suite_test.go b/inabox/tests/integration_suite_test.go index 9eb1703e8..8bbc2de42 100644 --- a/inabox/tests/integration_suite_test.go +++ b/inabox/tests/integration_suite_test.go @@ -167,6 +167,11 @@ func setupRetrievalClient(testConfig *deploy.Config) error { return err } + err = tx.SetDisperserAddress(context.Background(), testConfig.DisperserAddress) + if err != nil { + return fmt.Errorf("could not set disperser address: %w", err) + } + cs := eth.NewChainState(tx, ethClient) agn := &core.StdAssignmentCoordinator{} nodeClient := clients.NewNodeClient(20 * time.Second) From ee262bc5c603eb3aafa27e52ab77e67669dcd1f7 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Thu, 2 Jan 2025 13:12:16 -0600 Subject: [PATCH 62/94] Enable StoreChunks() signing Signed-off-by: Cody Littley --- inabox/deploy/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inabox/deploy/config.go b/inabox/deploy/config.go index f6609aef5..96cdef041 100644 --- a/inabox/deploy/config.go +++ b/inabox/deploy/config.go @@ -444,7 +444,7 @@ func (env *Config) generateOperatorVars(ind int, name, key, churnerUrl, logPath, NODE_NUM_CONFIRMATIONS: "0", NODE_ONCHAIN_METRICS_INTERVAL: "-1", NODE_ENABLE_V2: "true", - NODE_DISABLE_DISPERSAL_AUTHENTICATION: "true", + NODE_DISABLE_DISPERSAL_AUTHENTICATION: "false", } env.applyDefaults(&v, "NODE", "opr", ind) From 262e1a9d45be645e163acf67460295f0b5c635a4 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Thu, 2 Jan 2025 13:41:50 -0600 Subject: [PATCH 63/94] Change where the disperser address is written to contract. Signed-off-by: Cody Littley --- core/eth/writer.go | 1 + inabox/tests/integration_suite_test.go | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/core/eth/writer.go b/core/eth/writer.go index 486d948ce..bf66146fa 100644 --- a/core/eth/writer.go +++ b/core/eth/writer.go @@ -338,6 +338,7 @@ func (t *Writer) ConfirmBatch(ctx context.Context, batchHeader *core.BatchHeader func (t *Writer) SetDisperserAddress(ctx context.Context, address gethcommon.Address) error { registry := t.bindings.DisperserRegistry if registry == nil { + log.Printf("disperser registry not deployed") return errors.New("disperser registry not deployed") } diff --git a/inabox/tests/integration_suite_test.go b/inabox/tests/integration_suite_test.go index 8bbc2de42..f9fbffb22 100644 --- a/inabox/tests/integration_suite_test.go +++ b/inabox/tests/integration_suite_test.go @@ -132,6 +132,9 @@ var _ = BeforeSuite(func() { fmt.Println("Registering blob versions and relays") relays = testConfig.RegisterBlobVersionAndRelays(ethClient) + fmt.Println("Updating disperser address") + updateDisperserAddress() + fmt.Println("Starting binaries") testConfig.StartBinaries() @@ -142,6 +145,18 @@ var _ = BeforeSuite(func() { } }) +// updateDisperserAddress updates the disperser address in the retriever contract +func updateDisperserAddress() { + tx, err := eth.NewWriter( + logger, + ethClient, + testConfig.Retriever.RETRIEVER_BLS_OPERATOR_STATE_RETRIVER, + testConfig.Retriever.RETRIEVER_EIGENDA_SERVICE_MANAGER) + Expect(err).To(BeNil()) + err = tx.SetDisperserAddress(context.Background(), testConfig.DisperserAddress) + Expect(err).To(BeNil()) +} + func setupRetrievalClient(testConfig *deploy.Config) error { ethClientConfig := geth.EthClientConfig{ RPCURLs: []string{testConfig.Deployers[0].RPC}, From d32d0a8ec01b9e9bc1ab6af8ddeb20eff5db08ba Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Fri, 3 Jan 2025 08:34:47 -0600 Subject: [PATCH 64/94] Wait for disperser address transaction to be handled. Signed-off-by: Cody Littley --- inabox/tests/integration_suite_test.go | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/inabox/tests/integration_suite_test.go b/inabox/tests/integration_suite_test.go index f9fbffb22..32d55e121 100644 --- a/inabox/tests/integration_suite_test.go +++ b/inabox/tests/integration_suite_test.go @@ -147,14 +147,30 @@ var _ = BeforeSuite(func() { // updateDisperserAddress updates the disperser address in the retriever contract func updateDisperserAddress() { - tx, err := eth.NewWriter( + writer, err := eth.NewWriter( logger, ethClient, testConfig.Retriever.RETRIEVER_BLS_OPERATOR_STATE_RETRIVER, testConfig.Retriever.RETRIEVER_EIGENDA_SERVICE_MANAGER) Expect(err).To(BeNil()) - err = tx.SetDisperserAddress(context.Background(), testConfig.DisperserAddress) + err = writer.SetDisperserAddress(context.Background(), testConfig.DisperserAddress) Expect(err).To(BeNil()) + + maxWaitTime := time.Minute + start := time.Now() + ticker := time.NewTicker(time.Second) + for time.Since(start) < maxWaitTime { + address, err := writer.GetDisperserAddress(context.Background(), 0) + if err == nil { + Expect(address).To(Equal(testConfig.DisperserAddress)) + return + } + + fmt.Printf("Error reading disperser address. Will keep retrying for %v. %s\n", + maxWaitTime-time.Since(start), err) + <-ticker.C + } + Fail("Failed to get disperser address") } func setupRetrievalClient(testConfig *deploy.Config) error { @@ -182,11 +198,6 @@ func setupRetrievalClient(testConfig *deploy.Config) error { return err } - err = tx.SetDisperserAddress(context.Background(), testConfig.DisperserAddress) - if err != nil { - return fmt.Errorf("could not set disperser address: %w", err) - } - cs := eth.NewChainState(tx, ethClient) agn := &core.StdAssignmentCoordinator{} nodeClient := clients.NewNodeClient(20 * time.Second) From 1f42f9d0ed1293c925b0e29fcab15aea9df6d82a Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Fri, 3 Jan 2025 08:51:03 -0600 Subject: [PATCH 65/94] Actually submit transaction. Signed-off-by: Cody Littley --- core/eth/writer.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/eth/writer.go b/core/eth/writer.go index bf66146fa..aaac1e01a 100644 --- a/core/eth/writer.go +++ b/core/eth/writer.go @@ -351,12 +351,17 @@ func (t *Writer) SetDisperserAddress(ctx context.Context, address gethcommon.Add } options.Context = ctx - _, err = registry.SetDisperserInfo( + transaction, err := registry.SetDisperserInfo( options, api.EigenLabsDisperserID, contractEigenDADisperserRegistry.DisperserInfo{ DisperserAddress: address, }) + if err != nil { + return fmt.Errorf("failed to create transaction for setting disperser address: %w", err) + } + + _, err = t.ethClient.EstimateGasPriceAndLimitAndSendTx(ctx, transaction, "SetDisperserAddress", nil) if err != nil { return fmt.Errorf("failed to set disperser address: %w", err) } From 7caac2c538edce4b476228d87a423448ed4f49cf Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Fri, 3 Jan 2025 10:10:52 -0600 Subject: [PATCH 66/94] Create a block every second. Signed-off-by: Cody Littley --- inabox/bin.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inabox/bin.sh b/inabox/bin.sh index e77bc581e..d97919db7 100755 --- a/inabox/bin.sh +++ b/inabox/bin.sh @@ -237,7 +237,7 @@ function stop_detached { function start_anvil { echo "Starting anvil server ....." - anvil --host 0.0.0.0 > /dev/null & + anvil --host 0.0.0.0 --block-time 1 > /dev/null & anvil_pid=$! echo "Anvil server started ....." From 20fd4da1ddc44cd7913978a93a4507ab37c172fd Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Fri, 3 Jan 2025 13:01:28 -0600 Subject: [PATCH 67/94] Use client with no confirmations. Signed-off-by: Cody Littley --- inabox/tests/integration_suite_test.go | 32 +++++++++++++++++--------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/inabox/tests/integration_suite_test.go b/inabox/tests/integration_suite_test.go index 32d55e121..1c90fa982 100644 --- a/inabox/tests/integration_suite_test.go +++ b/inabox/tests/integration_suite_test.go @@ -116,16 +116,7 @@ var _ = BeforeSuite(func() { logger, err = common.NewLogger(loggerConfig) Expect(err).To(BeNil()) - pk := testConfig.Pks.EcdsaMap["default"].PrivateKey - pk = strings.TrimPrefix(pk, "0x") - pk = strings.TrimPrefix(pk, "0X") - ethClient, err = geth.NewMultiHomingClient(geth.EthClientConfig{ - RPCURLs: []string{testConfig.Deployers[0].RPC}, - PrivateKeyString: pk, - NumConfirmations: numConfirmations, - NumRetries: numRetries, - }, gcommon.Address{}, logger) - Expect(err).To(BeNil()) + ethClient = buildEthClient(numConfirmations) rpcClient, err = ethrpc.Dial(testConfig.Deployers[0].RPC) Expect(err).To(BeNil()) @@ -145,11 +136,30 @@ var _ = BeforeSuite(func() { } }) +// buildEthClient builds an Ethereum client with the given number of required confirmations +func buildEthClient(confirmations int) common.EthClient { + pk := testConfig.Pks.EcdsaMap["default"].PrivateKey + pk = strings.TrimPrefix(pk, "0x") + pk = strings.TrimPrefix(pk, "0X") + + ec, err := geth.NewMultiHomingClient(geth.EthClientConfig{ + RPCURLs: []string{testConfig.Deployers[0].RPC}, + PrivateKeyString: pk, + NumConfirmations: confirmations, + NumRetries: numRetries, + }, gcommon.Address{}, logger) + + Expect(err).To(BeNil()) + return ec +} + // updateDisperserAddress updates the disperser address in the retriever contract func updateDisperserAddress() { + nonConfirmingClient := buildEthClient(0) + writer, err := eth.NewWriter( logger, - ethClient, + nonConfirmingClient, testConfig.Retriever.RETRIEVER_BLS_OPERATOR_STATE_RETRIVER, testConfig.Retriever.RETRIEVER_EIGENDA_SERVICE_MANAGER) Expect(err).To(BeNil()) From b09a43e1f354c8385dcaaa7754b46b74e3757cd8 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Fri, 3 Jan 2025 13:20:59 -0600 Subject: [PATCH 68/94] Revert anvil change Signed-off-by: Cody Littley --- inabox/bin.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inabox/bin.sh b/inabox/bin.sh index d97919db7..e77bc581e 100755 --- a/inabox/bin.sh +++ b/inabox/bin.sh @@ -237,7 +237,7 @@ function stop_detached { function start_anvil { echo "Starting anvil server ....." - anvil --host 0.0.0.0 --block-time 1 > /dev/null & + anvil --host 0.0.0.0 > /dev/null & anvil_pid=$! echo "Anvil server started ....." From 6f9e242a23025a48cd8a16f54cad96fe1fb67329 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Fri, 3 Jan 2025 13:40:24 -0600 Subject: [PATCH 69/94] Change where disperser address is updated. Signed-off-by: Cody Littley --- inabox/tests/integration_suite_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/inabox/tests/integration_suite_test.go b/inabox/tests/integration_suite_test.go index 1c90fa982..fe4d026d6 100644 --- a/inabox/tests/integration_suite_test.go +++ b/inabox/tests/integration_suite_test.go @@ -116,6 +116,9 @@ var _ = BeforeSuite(func() { logger, err = common.NewLogger(loggerConfig) Expect(err).To(BeNil()) + fmt.Println("Updating disperser address") + updateDisperserAddress() + ethClient = buildEthClient(numConfirmations) rpcClient, err = ethrpc.Dial(testConfig.Deployers[0].RPC) Expect(err).To(BeNil()) @@ -123,9 +126,6 @@ var _ = BeforeSuite(func() { fmt.Println("Registering blob versions and relays") relays = testConfig.RegisterBlobVersionAndRelays(ethClient) - fmt.Println("Updating disperser address") - updateDisperserAddress() - fmt.Println("Starting binaries") testConfig.StartBinaries() From 0a3e35bea628456a00dd8ba6d22d71092d47b8ce Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Mon, 6 Jan 2025 08:20:03 -0600 Subject: [PATCH 70/94] Add stack trace. Signed-off-by: Cody Littley --- core/eth/reader.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/core/eth/reader.go b/core/eth/reader.go index d2227106f..546ee0fbb 100644 --- a/core/eth/reader.go +++ b/core/eth/reader.go @@ -5,6 +5,7 @@ import ( "crypto/ecdsa" "fmt" "math/big" + "runtime" "strings" "github.com/Layr-Labs/eigenda/common" @@ -434,7 +435,13 @@ func (t *Reader) GetOperatorStakesForQuorums(ctx context.Context, quorums []core Context: ctx, }, t.bindings.RegCoordinatorAddr, quorumBytes, blockNumber) if err != nil { - t.logger.Error("Failed to fetch operator state", "err", err) + + // TODO don't merge this + b := make([]byte, 2048) // adjust buffer size to be larger than expected stack + n := runtime.Stack(b, false) + s := string(b[:n]) + + t.logger.Error("Failed to fetch operator state", "err", err, "stackTrace", s) return nil, err } From d92252de67773998d64bf4c334ae91918db28e7d Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Mon, 6 Jan 2025 08:38:00 -0600 Subject: [PATCH 71/94] Increase stack trace size. Signed-off-by: Cody Littley --- core/eth/reader.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/eth/reader.go b/core/eth/reader.go index 546ee0fbb..eec3e765c 100644 --- a/core/eth/reader.go +++ b/core/eth/reader.go @@ -437,7 +437,7 @@ func (t *Reader) GetOperatorStakesForQuorums(ctx context.Context, quorums []core if err != nil { // TODO don't merge this - b := make([]byte, 2048) // adjust buffer size to be larger than expected stack + b := make([]byte, 1024*32) // adjust buffer size to be larger than expected stack n := runtime.Stack(b, false) s := string(b[:n]) From 524b22a4fbd5d04fffd6614f88e4f24b1e3aca68 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Mon, 6 Jan 2025 08:39:39 -0600 Subject: [PATCH 72/94] Tweak stack trace. Signed-off-by: Cody Littley --- core/eth/reader.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/eth/reader.go b/core/eth/reader.go index eec3e765c..98a6999cd 100644 --- a/core/eth/reader.go +++ b/core/eth/reader.go @@ -441,7 +441,7 @@ func (t *Reader) GetOperatorStakesForQuorums(ctx context.Context, quorums []core n := runtime.Stack(b, false) s := string(b[:n]) - t.logger.Error("Failed to fetch operator state", "err", err, "stackTrace", s) + t.logger.Errorf("Failed to fetch operator state: %s Stack trace:\n%s", err, s) return nil, err } From 7971b135fb855395d2171759214926975c0aebed Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Mon, 6 Jan 2025 08:53:35 -0600 Subject: [PATCH 73/94] Multiple log statements. Signed-off-by: Cody Littley --- core/eth/reader.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/core/eth/reader.go b/core/eth/reader.go index 98a6999cd..b9f4716be 100644 --- a/core/eth/reader.go +++ b/core/eth/reader.go @@ -437,11 +437,16 @@ func (t *Reader) GetOperatorStakesForQuorums(ctx context.Context, quorums []core if err != nil { // TODO don't merge this - b := make([]byte, 1024*32) // adjust buffer size to be larger than expected stack + b := make([]byte, 1024*64) // adjust buffer size to be larger than expected stack n := runtime.Stack(b, false) s := string(b[:n]) - t.logger.Errorf("Failed to fetch operator state: %s Stack trace:\n%s", err, s) + elements := strings.Split(s, "\n") + for _, element := range elements { + t.logger.Error(element) + } + + t.logger.Errorf("Failed to fetch operator state: %s", err) return nil, err } From e07b734ad8e639b919aaa252b49c0723eed56e18 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Mon, 6 Jan 2025 09:22:43 -0600 Subject: [PATCH 74/94] Update disperser address earlier. Signed-off-by: Cody Littley --- inabox/tests/integration_suite_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/inabox/tests/integration_suite_test.go b/inabox/tests/integration_suite_test.go index fe4d026d6..57325bc0e 100644 --- a/inabox/tests/integration_suite_test.go +++ b/inabox/tests/integration_suite_test.go @@ -109,16 +109,16 @@ var _ = BeforeSuite(func() { testConfig.StartGraphNode() } + fmt.Println("Updating disperser address") + updateDisperserAddress() + fmt.Println("Deploying experiment") - testConfig.DeployExperiment() + testConfig.DeployExperiment() ////////////////////////////////////////// TODO loggerConfig := common.DefaultLoggerConfig() logger, err = common.NewLogger(loggerConfig) Expect(err).To(BeNil()) - fmt.Println("Updating disperser address") - updateDisperserAddress() - ethClient = buildEthClient(numConfirmations) rpcClient, err = ethrpc.Dial(testConfig.Deployers[0].RPC) Expect(err).To(BeNil()) From 8452d0942ac0ca455c4595aae5ed8733a0385690 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Mon, 6 Jan 2025 09:34:54 -0600 Subject: [PATCH 75/94] Initiate logger earlier. Signed-off-by: Cody Littley --- inabox/tests/integration_suite_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/inabox/tests/integration_suite_test.go b/inabox/tests/integration_suite_test.go index 57325bc0e..c34e3792f 100644 --- a/inabox/tests/integration_suite_test.go +++ b/inabox/tests/integration_suite_test.go @@ -109,16 +109,16 @@ var _ = BeforeSuite(func() { testConfig.StartGraphNode() } + loggerConfig := common.DefaultLoggerConfig() + logger, err = common.NewLogger(loggerConfig) + Expect(err).To(BeNil()) + fmt.Println("Updating disperser address") updateDisperserAddress() fmt.Println("Deploying experiment") testConfig.DeployExperiment() ////////////////////////////////////////// TODO - loggerConfig := common.DefaultLoggerConfig() - logger, err = common.NewLogger(loggerConfig) - Expect(err).To(BeNil()) - ethClient = buildEthClient(numConfirmations) rpcClient, err = ethrpc.Dial(testConfig.Deployers[0].RPC) Expect(err).To(BeNil()) From 987b26cef170f6522e224315201ef0472c826740 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Mon, 6 Jan 2025 09:44:23 -0600 Subject: [PATCH 76/94] Made suggested change. Signed-off-by: Cody Littley --- node/auth/authenticator.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/node/auth/authenticator.go b/node/auth/authenticator.go index 3a3be7139..ac79a4f60 100644 --- a/node/auth/authenticator.go +++ b/node/auth/authenticator.go @@ -39,8 +39,10 @@ type requestAuthenticator struct { // chainReader is used to read the chain state. chainReader core.Reader - // keyCache is used to cache the public keys of dispersers. - keyCache *lru.Cache[uint32, *keyWithTimeout] + // keyCache is used to cache the public keys of dispersers. The uint32 map keys are disperser IDs. Disperser + // IDs are serial numbers, with the original EigenDA disperser assigned ID 0. The map values contain + // the public key of the disperser and the time when the local cache of the key will expire. + keyCache *lru.Cache[uint32 /* disperser ID */, *keyWithTimeout] // keyTimeoutDuration is the duration for which a key is cached. After this duration, the key should be // reloaded from the chain state in case the key has been changed. From 525e37f1fb42f58ce77a9fb0cd5423cbcdf69052 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Mon, 6 Jan 2025 10:08:51 -0600 Subject: [PATCH 77/94] Reshuffle code. Signed-off-by: Cody Littley --- inabox/deploy/config.go | 50 +---------- inabox/deploy/config_types.go | 7 +- inabox/deploy/deploy.go | 111 +++++++++++++++++++++++-- inabox/tests/integration_suite_test.go | 34 +------- 4 files changed, 113 insertions(+), 89 deletions(-) diff --git a/inabox/deploy/config.go b/inabox/deploy/config.go index 96cdef041..3ddc995cd 100644 --- a/inabox/deploy/config.go +++ b/inabox/deploy/config.go @@ -1,16 +1,9 @@ package deploy import ( - "context" "crypto/rand" "errors" "fmt" - "github.com/Layr-Labs/eigenda/common" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/kms" - "github.com/aws/aws-sdk-go-v2/service/kms/types" - gethcommon "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" "log" "math/big" "path/filepath" @@ -579,41 +572,6 @@ func (env *Config) getKey(name string) (key, address string) { return } -// GenerateDisperserKeypair generates a disperser keypair using AWS KMS. Returns the key ID and the public address. -func generateDisperserKeypair() (string, gethcommon.Address, error) { - keyManager := kms.New(kms.Options{ - Region: "us-east-1", - BaseEndpoint: aws.String("http://localhost:4570"), // TODO don't hard code this - }) - - log.Printf("Generating disperser keypair") - - createKeyOutput, err := keyManager.CreateKey(context.Background(), &kms.CreateKeyInput{ - KeySpec: types.KeySpecEccSecgP256k1, - KeyUsage: types.KeyUsageTypeSignVerify, - }) - if err != nil { - if strings.Contains(err.Error(), "connect: connection refused") { - log.Printf("Unable to reach local stack, skipping disperser keypair generation. Error: %v", err) - err = nil - } - return "", gethcommon.Address{}, err - } - - keyID := *createKeyOutput.KeyMetadata.KeyId - - key, err := common.LoadPublicKeyKMS(context.Background(), keyManager, keyID) - if err != nil { - return "", gethcommon.Address{}, err - } - - publicAddress := crypto.PubkeyToAddress(*key) - - log.Printf("Generated disperser keypair: key ID: %s, address: %s", keyID, publicAddress.Hex()) - - return keyID, publicAddress, nil -} - // GenerateAllVariables all of the config for the test environment. // Returns an object that corresponds to the participants of the // current experiment. @@ -625,12 +583,6 @@ func (env *Config) GenerateAllVariables() { createDirectory(env.Path + "/envs") changeDirectory(env.rootPath + "/inabox") - disperserKeyID, disperserAddress, err := generateDisperserKeypair() - if err != nil { - log.Fatalf("Error generating disperser keypair: %v", err) - } - env.DisperserAddress = disperserAddress - // Create compose file composeFile := env.Path + "/docker-compose.yml" servicesMap := make(map[string]map[string]interface{}) @@ -776,7 +728,7 @@ func (env *Config) GenerateAllVariables() { // Controller name = "controller0" _, _, _, envFile = env.getPaths(name) - controllerConfig := env.generateControllerVars(0, graphUrl, disperserKeyID) + controllerConfig := env.generateControllerVars(0, graphUrl, env.DisperserKMSKeyID) writeEnv(controllerConfig.getEnvMap(), envFile) env.Controller = controllerConfig diff --git a/inabox/deploy/config_types.go b/inabox/deploy/config_types.go index 193a59cd2..39591c8d4 100644 --- a/inabox/deploy/config_types.go +++ b/inabox/deploy/config_types.go @@ -185,10 +185,13 @@ type Config struct { // DisperserAddress is the address of disperser 0 (aka the only disperser at the current time) DisperserAddress common.Address + + // DisperserKMSKeyID is the KMS key ID used to encrypt disperser data + DisperserKMSKeyID string } -func (c Config) IsEigenDADeployed() bool { - return c.EigenDA.ServiceManager != "" +func (env *Config) IsEigenDADeployed() bool { + return env.EigenDA.ServiceManager != "" } func NewTestConfig(testName, rootPath string) (testEnv *Config) { diff --git a/inabox/deploy/deploy.go b/inabox/deploy/deploy.go index d3cb765d1..a58f8062b 100644 --- a/inabox/deploy/deploy.go +++ b/inabox/deploy/deploy.go @@ -4,6 +4,16 @@ import ( "context" "encoding/json" "fmt" + "github.com/Layr-Labs/eigenda/common" + "github.com/Layr-Labs/eigenda/common/geth" + relayreg "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDARelayRegistry" + eigendasrvmg "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDAServiceManager" + thresholdreg "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDAThresholdRegistry" + "github.com/Layr-Labs/eigenda/core/eth" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/kms" + "github.com/aws/aws-sdk-go-v2/service/kms/types" + "github.com/ethereum/go-ethereum/crypto" "io" "log" "math/big" @@ -11,11 +21,7 @@ import ( "path/filepath" "runtime" "strconv" - - "github.com/Layr-Labs/eigenda/common" - relayreg "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDARelayRegistry" - eigendasrvmg "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDAServiceManager" - thresholdreg "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDAThresholdRegistry" + "strings" "github.com/Layr-Labs/eigenda/core" "github.com/ethereum/go-ethereum/accounts/abi/bind" @@ -151,6 +157,20 @@ func (env *Config) DeployExperiment() { env.deployEigenDAContracts() } + log.Print("Generating disperser keypair") + keyID, disperserAddress, err := generateDisperserKeypair() + if err != nil { + log.Panicf("could not generate disperser keypair: %v", err) + } + env.DisperserAddress = disperserAddress + env.DisperserKMSKeyID = keyID + + log.Print("Updating disperser address") + err = env.updateDisperserAddress() + if err != nil { + log.Panicf("could not update disperser address: %v", err) + } + if deployer, ok := env.GetDeployer(env.EigenDA.Deployer); ok && deployer.DeploySubgraphs { startBlock := GetLatestBlockNumber(env.Deployers[0].RPC) env.deploySubgraphs(startBlock) @@ -168,6 +188,87 @@ func (env *Config) DeployExperiment() { fmt.Println("Test environment has successfully deployed!") } +// GenerateDisperserKeypair generates a disperser keypair using AWS KMS. Returns the key ID and the public address. +func generateDisperserKeypair() (string, gcommon.Address, error) { + keyManager := kms.New(kms.Options{ + Region: "us-east-1", + BaseEndpoint: aws.String("http://localhost:4570"), // TODO don't hard code this + }) + + createKeyOutput, err := keyManager.CreateKey(context.Background(), &kms.CreateKeyInput{ + KeySpec: types.KeySpecEccSecgP256k1, + KeyUsage: types.KeyUsageTypeSignVerify, + }) + if err != nil { + if strings.Contains(err.Error(), "connect: connection refused") { + log.Printf("Unable to reach local stack, skipping disperser keypair generation. Error: %v", err) + err = nil + } + return "", gcommon.Address{}, err + } + + keyID := *createKeyOutput.KeyMetadata.KeyId + + key, err := common.LoadPublicKeyKMS(context.Background(), keyManager, keyID) + if err != nil { + return "", gcommon.Address{}, err + } + + publicAddress := crypto.PubkeyToAddress(*key) + + log.Printf("Generated disperser keypair: key ID: %s, address: %s", keyID, publicAddress.Hex()) + + return keyID, publicAddress, nil +} + +// updateDisperserAddress updates the disperser address in the retriever contract +func (env *Config) updateDisperserAddress() error { + pk := env.Pks.EcdsaMap["default"].PrivateKey + pk = strings.TrimPrefix(pk, "0x") + pk = strings.TrimPrefix(pk, "0X") + + loggerConfig := common.DefaultLoggerConfig() + logger, err := common.NewLogger(loggerConfig) + if err != nil { + return fmt.Errorf("could not create logger: %v", err) + } + + ethClient, err := geth.NewMultiHomingClient(geth.EthClientConfig{ + RPCURLs: []string{env.Deployers[0].RPC}, + PrivateKeyString: pk, + NumConfirmations: 0, + NumRetries: 0, + }, gcommon.Address{}, logger) + if err != nil { + return fmt.Errorf("could not create eth client: %v", err) + } + + writer, err := eth.NewWriter( + logger, + ethClient, + env.Retriever.RETRIEVER_BLS_OPERATOR_STATE_RETRIVER, + env.Retriever.RETRIEVER_EIGENDA_SERVICE_MANAGER) + if err != nil { + return fmt.Errorf("could not create writer: %v", err) + } + + err = writer.SetDisperserAddress(context.Background(), env.DisperserAddress) + if err != nil { + return fmt.Errorf("could not set disperser address: %v", err) + } + + address, err := writer.GetDisperserAddress(context.Background(), 0) + if err != nil { + return fmt.Errorf("could not get disperser address: %v", err) + } + + if address != env.DisperserAddress { + return fmt.Errorf("expected disperser address %s, got %s", env.DisperserAddress, address) + } + + return nil +} + func (env *Config) RegisterBlobVersionAndRelays(ethClient common.EthClient) map[uint32]string { dasmAddr := gcommon.HexToAddress(env.EigenDA.ServiceManager) contractEigenDAServiceManager, err := eigendasrvmg.NewContractEigenDAServiceManager(dasmAddr, ethClient) diff --git a/inabox/tests/integration_suite_test.go b/inabox/tests/integration_suite_test.go index c34e3792f..c16e1970d 100644 --- a/inabox/tests/integration_suite_test.go +++ b/inabox/tests/integration_suite_test.go @@ -113,9 +113,6 @@ var _ = BeforeSuite(func() { logger, err = common.NewLogger(loggerConfig) Expect(err).To(BeNil()) - fmt.Println("Updating disperser address") - updateDisperserAddress() - fmt.Println("Deploying experiment") testConfig.DeployExperiment() ////////////////////////////////////////// TODO @@ -136,6 +133,7 @@ var _ = BeforeSuite(func() { } }) +// TODO revert this change // buildEthClient builds an Ethereum client with the given number of required confirmations func buildEthClient(confirmations int) common.EthClient { pk := testConfig.Pks.EcdsaMap["default"].PrivateKey @@ -153,36 +151,6 @@ func buildEthClient(confirmations int) common.EthClient { return ec } -// updateDisperserAddress updates the disperser address in the retriever contract -func updateDisperserAddress() { - nonConfirmingClient := buildEthClient(0) - - writer, err := eth.NewWriter( - logger, - nonConfirmingClient, - testConfig.Retriever.RETRIEVER_BLS_OPERATOR_STATE_RETRIVER, - testConfig.Retriever.RETRIEVER_EIGENDA_SERVICE_MANAGER) - Expect(err).To(BeNil()) - err = writer.SetDisperserAddress(context.Background(), testConfig.DisperserAddress) - Expect(err).To(BeNil()) - - maxWaitTime := time.Minute - start := time.Now() - ticker := time.NewTicker(time.Second) - for time.Since(start) < maxWaitTime { - address, err := writer.GetDisperserAddress(context.Background(), 0) - if err == nil { - Expect(address).To(Equal(testConfig.DisperserAddress)) - return - } - - fmt.Printf("Error reading disperser address. Will keep retrying for %v. %s\n", - maxWaitTime-time.Since(start), err) - <-ticker.C - } - Fail("Failed to get disperser address") -} - func setupRetrievalClient(testConfig *deploy.Config) error { ethClientConfig := geth.EthClientConfig{ RPCURLs: []string{testConfig.Deployers[0].RPC}, From 87a761016ea5da312f70b29a6bb9cf427a6c4c1e Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Mon, 6 Jan 2025 10:20:17 -0600 Subject: [PATCH 78/94] Tweaks. Signed-off-by: Cody Littley --- inabox/deploy/deploy.go | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/inabox/deploy/deploy.go b/inabox/deploy/deploy.go index a58f8062b..f0ac78a77 100644 --- a/inabox/deploy/deploy.go +++ b/inabox/deploy/deploy.go @@ -158,18 +158,10 @@ func (env *Config) DeployExperiment() { } log.Print("Generating disperser keypair") - keyID, disperserAddress, err := generateDisperserKeypair() + err = env.generateDisperserKeypair() if err != nil { log.Panicf("could not generate disperser keypair: %v", err) } - env.DisperserAddress = disperserAddress - env.DisperserKMSKeyID = keyID - - log.Print("Updating disperser address") - err = env.updateDisperserAddress() - if err != nil { - log.Panicf("could not update disperser address: %v", err) - } if deployer, ok := env.GetDeployer(env.EigenDA.Deployer); ok && deployer.DeploySubgraphs { startBlock := GetLatestBlockNumber(env.Deployers[0].RPC) @@ -189,7 +181,10 @@ func (env *Config) DeployExperiment() { } // GenerateDisperserKeypair generates a disperser keypair using AWS KMS. Returns the key ID and the public address. -func generateDisperserKeypair() (string, gcommon.Address, error) { +func (env *Config) generateDisperserKeypair() error { + + // Generate a keypair in AWS KMS + keyManager := kms.New(kms.Options{ Region: "us-east-1", BaseEndpoint: aws.String("http://localhost:4570"), // TODO don't hard code this @@ -204,25 +199,24 @@ func generateDisperserKeypair() (string, gcommon.Address, error) { log.Printf("Unable to reach local stack, skipping disperser keypair generation. Error: %v", err) err = nil } - return "", gcommon.Address{}, err + return err } - keyID := *createKeyOutput.KeyMetadata.KeyId + env.DisperserKMSKeyID = *createKeyOutput.KeyMetadata.KeyId - key, err := common.LoadPublicKeyKMS(context.Background(), keyManager, keyID) + // Load the public key and convert it to an Ethereum address + + key, err := common.LoadPublicKeyKMS(context.Background(), keyManager, env.DisperserKMSKeyID) if err != nil { - return "", gcommon.Address{}, err + return fmt.Errorf("could not load public key: %v", err) } - publicAddress := crypto.PubkeyToAddress(*key) - - log.Printf("Generated disperser keypair: key ID: %s, address: %s", keyID, publicAddress.Hex()) + env.DisperserAddress = crypto.PubkeyToAddress(*key) + log.Printf("Generated disperser keypair: key ID: %s, address: %s", + env.DisperserKMSKeyID, env.DisperserAddress.Hex()) - return keyID, publicAddress, nil -} + // Write the disperser's public key to on-chain storage -// updateDisperserAddress updates the disperser address in the retriever contract -func (env *Config) updateDisperserAddress() error { pk := env.Pks.EcdsaMap["default"].PrivateKey pk = strings.TrimPrefix(pk, "0x") pk = strings.TrimPrefix(pk, "0X") @@ -257,6 +251,8 @@ func (env *Config) updateDisperserAddress() error { return fmt.Errorf("could not set disperser address: %v", err) } + // Read the disperser's public key from on-chain storage to verify it was written correctly + address, err := writer.GetDisperserAddress(context.Background(), 0) if err != nil { return fmt.Errorf("could not get disperser address: %v", err) From 3852dc73ebd267ab94116e43f29d9ceff8d0e305 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Mon, 6 Jan 2025 10:38:37 -0600 Subject: [PATCH 79/94] Play with ordering. Signed-off-by: Cody Littley --- inabox/deploy/deploy.go | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/inabox/deploy/deploy.go b/inabox/deploy/deploy.go index f0ac78a77..b681c690e 100644 --- a/inabox/deploy/deploy.go +++ b/inabox/deploy/deploy.go @@ -19,7 +19,6 @@ import ( "math/big" "os" "path/filepath" - "runtime" "strconv" "strings" @@ -157,22 +156,17 @@ func (env *Config) DeployExperiment() { env.deployEigenDAContracts() } - log.Print("Generating disperser keypair") - err = env.generateDisperserKeypair() - if err != nil { - log.Panicf("could not generate disperser keypair: %v", err) - } - if deployer, ok := env.GetDeployer(env.EigenDA.Deployer); ok && deployer.DeploySubgraphs { startBlock := GetLatestBlockNumber(env.Deployers[0].RPC) env.deploySubgraphs(startBlock) } - // TODO remove - b := make([]byte, 2048) // adjust buffer size to be larger than expected stack - n := runtime.Stack(b, false) - s := string(b[:n]) - log.Printf("Stack trace:\n %s", s) + // TODO does this need to go before start block? + log.Print("Generating disperser keypair") + err = env.generateDisperserKeypair() + if err != nil { + log.Panicf("could not generate disperser keypair: %v", err) + } fmt.Println("Generating variables") env.GenerateAllVariables() From a305e93eb3e966650817024f6a097f5074659c17 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Mon, 6 Jan 2025 11:11:29 -0600 Subject: [PATCH 80/94] Relocate code. Signed-off-by: Cody Littley --- inabox/deploy/deploy.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/inabox/deploy/deploy.go b/inabox/deploy/deploy.go index b681c690e..c7d87390c 100644 --- a/inabox/deploy/deploy.go +++ b/inabox/deploy/deploy.go @@ -161,16 +161,16 @@ func (env *Config) DeployExperiment() { env.deploySubgraphs(startBlock) } - // TODO does this need to go before start block? + fmt.Println("Generating variables") + env.GenerateAllVariables() + + // TODO this is causing problems log.Print("Generating disperser keypair") err = env.generateDisperserKeypair() if err != nil { log.Panicf("could not generate disperser keypair: %v", err) } - fmt.Println("Generating variables") - env.GenerateAllVariables() - fmt.Println("Test environment has successfully deployed!") } From 568e971faccd33549698092f6190764628651ed7 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Mon, 6 Jan 2025 11:25:36 -0600 Subject: [PATCH 81/94] Try moving disperser setup again. Signed-off-by: Cody Littley --- core/thegraph/state_integration_test.go | 5 +++++ inabox/deploy/deploy.go | 12 ++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/core/thegraph/state_integration_test.go b/core/thegraph/state_integration_test.go index f7326d22b..bc21815ae 100644 --- a/core/thegraph/state_integration_test.go +++ b/core/thegraph/state_integration_test.go @@ -73,6 +73,11 @@ func setup() { } _ = testConfig.RegisterBlobVersionAndRelays(ethClient) + err = testConfig.GenerateDisperserKeypair() + if err != nil { + panic(err) + } + fmt.Println("Starting binaries") testConfig.StartBinaries() } diff --git a/inabox/deploy/deploy.go b/inabox/deploy/deploy.go index c7d87390c..117306ab6 100644 --- a/inabox/deploy/deploy.go +++ b/inabox/deploy/deploy.go @@ -165,17 +165,17 @@ func (env *Config) DeployExperiment() { env.GenerateAllVariables() // TODO this is causing problems - log.Print("Generating disperser keypair") - err = env.generateDisperserKeypair() - if err != nil { - log.Panicf("could not generate disperser keypair: %v", err) - } + //log.Print("Generating disperser keypair") + //err = env.generateDisperserKeypair() + //if err != nil { + // log.Panicf("could not generate disperser keypair: %v", err) + //} fmt.Println("Test environment has successfully deployed!") } // GenerateDisperserKeypair generates a disperser keypair using AWS KMS. Returns the key ID and the public address. -func (env *Config) generateDisperserKeypair() error { +func (env *Config) GenerateDisperserKeypair() error { // Generate a keypair in AWS KMS From c77e59616b395a8d61294c45d78edc6edbc5ff9e Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Mon, 6 Jan 2025 12:11:47 -0600 Subject: [PATCH 82/94] move code Signed-off-by: Cody Littley --- core/thegraph/state_integration_test.go | 5 ----- inabox/tests/integration_suite_test.go | 6 ++++++ 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/core/thegraph/state_integration_test.go b/core/thegraph/state_integration_test.go index bc21815ae..f7326d22b 100644 --- a/core/thegraph/state_integration_test.go +++ b/core/thegraph/state_integration_test.go @@ -73,11 +73,6 @@ func setup() { } _ = testConfig.RegisterBlobVersionAndRelays(ethClient) - err = testConfig.GenerateDisperserKeypair() - if err != nil { - panic(err) - } - fmt.Println("Starting binaries") testConfig.StartBinaries() } diff --git a/inabox/tests/integration_suite_test.go b/inabox/tests/integration_suite_test.go index c16e1970d..afb358340 100644 --- a/inabox/tests/integration_suite_test.go +++ b/inabox/tests/integration_suite_test.go @@ -123,6 +123,12 @@ var _ = BeforeSuite(func() { fmt.Println("Registering blob versions and relays") relays = testConfig.RegisterBlobVersionAndRelays(ethClient) + fmt.Println("Generating disperser keypair") // TODO possibly follow exemple of RegisterBlobVersionAndRelays + err = testConfig.GenerateDisperserKeypair() + if err != nil { + panic(err) + } + fmt.Println("Starting binaries") testConfig.StartBinaries() From 73714979458eb8d063cf6b657e29f4658826c080 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Mon, 6 Jan 2025 15:45:29 -0600 Subject: [PATCH 83/94] Tweaks Signed-off-by: Cody Littley --- core/eth/writer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/eth/writer.go b/core/eth/writer.go index aaac1e01a..28e6fb210 100644 --- a/core/eth/writer.go +++ b/core/eth/writer.go @@ -361,7 +361,7 @@ func (t *Writer) SetDisperserAddress(ctx context.Context, address gethcommon.Add return fmt.Errorf("failed to create transaction for setting disperser address: %w", err) } - _, err = t.ethClient.EstimateGasPriceAndLimitAndSendTx(ctx, transaction, "SetDisperserAddress", nil) + err = t.ethClient.SendTransaction(ctx, transaction) if err != nil { return fmt.Errorf("failed to set disperser address: %w", err) } From eca25b92749ce0a79238d4b591e0ae59053d60c8 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Mon, 6 Jan 2025 16:00:33 -0600 Subject: [PATCH 84/94] Use other eth client. Signed-off-by: Cody Littley --- inabox/deploy/deploy.go | 21 ++++++++++----------- inabox/tests/integration_suite_test.go | 2 +- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/inabox/deploy/deploy.go b/inabox/deploy/deploy.go index 117306ab6..b4e964e27 100644 --- a/inabox/deploy/deploy.go +++ b/inabox/deploy/deploy.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "github.com/Layr-Labs/eigenda/common" - "github.com/Layr-Labs/eigenda/common/geth" relayreg "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDARelayRegistry" eigendasrvmg "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDAServiceManager" thresholdreg "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDAThresholdRegistry" @@ -175,7 +174,7 @@ func (env *Config) DeployExperiment() { } // GenerateDisperserKeypair generates a disperser keypair using AWS KMS. Returns the key ID and the public address. -func (env *Config) GenerateDisperserKeypair() error { +func (env *Config) GenerateDisperserKeypair(ethClient common.EthClient) error { // Generate a keypair in AWS KMS @@ -221,15 +220,15 @@ func (env *Config) GenerateDisperserKeypair() error { return fmt.Errorf("could not create logger: %v", err) } - ethClient, err := geth.NewMultiHomingClient(geth.EthClientConfig{ - RPCURLs: []string{env.Deployers[0].RPC}, - PrivateKeyString: pk, - NumConfirmations: 0, - NumRetries: 0, - }, gcommon.Address{}, logger) - if err != nil { - return fmt.Errorf("could not create eth client: %v", err) - } + //ethClient, err := geth.NewMultiHomingClient(geth.EthClientConfig{ + // RPCURLs: []string{env.Deployers[0].RPC}, + // PrivateKeyString: pk, + // NumConfirmations: 0, + // NumRetries: 0, + //}, gcommon.Address{}, logger) + //if err != nil { + // return fmt.Errorf("could not create eth client: %v", err) + //} writer, err := eth.NewWriter( logger, diff --git a/inabox/tests/integration_suite_test.go b/inabox/tests/integration_suite_test.go index afb358340..adf3a95d8 100644 --- a/inabox/tests/integration_suite_test.go +++ b/inabox/tests/integration_suite_test.go @@ -124,7 +124,7 @@ var _ = BeforeSuite(func() { relays = testConfig.RegisterBlobVersionAndRelays(ethClient) fmt.Println("Generating disperser keypair") // TODO possibly follow exemple of RegisterBlobVersionAndRelays - err = testConfig.GenerateDisperserKeypair() + err = testConfig.GenerateDisperserKeypair(ethClient) if err != nil { panic(err) } From 26ec8662680ed29e5560225d166e492a84325cd7 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Tue, 7 Jan 2025 08:53:39 -0600 Subject: [PATCH 85/94] Made suggested changes. Signed-off-by: Cody Littley --- api/clients/v2/dispersal_request_signer.go | 6 +-- .../v2/dispersal_request_signer_test.go | 4 +- common/{ => aws}/kms.go | 2 +- common/{ => aws}/kms_fuzz_test.go | 2 +- node/auth/authenticator.go | 10 ++++ node/auth/authenticator_test.go | 49 ++++++++++++++++--- node/grpc/server_v2.go | 3 ++ 7 files changed, 62 insertions(+), 14 deletions(-) rename common/{ => aws}/kms.go (99%) rename common/{ => aws}/kms_fuzz_test.go (99%) diff --git a/api/clients/v2/dispersal_request_signer.go b/api/clients/v2/dispersal_request_signer.go index e0d8f47e0..4ab4a9e09 100644 --- a/api/clients/v2/dispersal_request_signer.go +++ b/api/clients/v2/dispersal_request_signer.go @@ -6,7 +6,7 @@ import ( "fmt" grpc "github.com/Layr-Labs/eigenda/api/grpc/node/v2" "github.com/Layr-Labs/eigenda/api/hashing" - "github.com/Layr-Labs/eigenda/common" + aws2 "github.com/Layr-Labs/eigenda/common/aws" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/kms" ) @@ -38,7 +38,7 @@ func NewDispersalRequestSigner( BaseEndpoint: aws.String(endpoint), }) - key, err := common.LoadPublicKeyKMS(ctx, keyManager, keyID) + key, err := aws2.LoadPublicKeyKMS(ctx, keyManager, keyID) if err != nil { return nil, fmt.Errorf("failed to get ecdsa public key: %w", err) } @@ -53,7 +53,7 @@ func NewDispersalRequestSigner( func (s *requestSigner) SignStoreChunksRequest(ctx context.Context, request *grpc.StoreChunksRequest) ([]byte, error) { hash := hashing.HashStoreChunksRequest(request) - signature, err := common.SignKMS(ctx, s.keyManager, s.keyID, s.publicKey, hash) + signature, err := aws2.SignKMS(ctx, s.keyManager, s.keyID, s.publicKey, hash) if err != nil { return nil, fmt.Errorf("failed to sign request: %w", err) } diff --git a/api/clients/v2/dispersal_request_signer_test.go b/api/clients/v2/dispersal_request_signer_test.go index 98e82b7a0..8ef094fe9 100644 --- a/api/clients/v2/dispersal_request_signer_test.go +++ b/api/clients/v2/dispersal_request_signer_test.go @@ -2,7 +2,7 @@ package clients import ( "context" - "github.com/Layr-Labs/eigenda/common" + aws2 "github.com/Layr-Labs/eigenda/common/aws" "github.com/Layr-Labs/eigenda/common/testutils/random" "github.com/Layr-Labs/eigenda/inabox/deploy" "github.com/Layr-Labs/eigenda/node/auth" @@ -90,7 +90,7 @@ func TestRequestSigning(t *testing.T) { keyID := *createKeyOutput.KeyMetadata.KeyId - key, err := common.LoadPublicKeyKMS(context.Background(), keyManager, keyID) + key, err := aws2.LoadPublicKeyKMS(context.Background(), keyManager, keyID) require.NoError(t, err) publicAddress := crypto.PubkeyToAddress(*key) diff --git a/common/kms.go b/common/aws/kms.go similarity index 99% rename from common/kms.go rename to common/aws/kms.go index bec69ab30..b195210c1 100644 --- a/common/kms.go +++ b/common/aws/kms.go @@ -1,4 +1,4 @@ -package common +package aws import ( "bytes" diff --git a/common/kms_fuzz_test.go b/common/aws/kms_fuzz_test.go similarity index 99% rename from common/kms_fuzz_test.go rename to common/aws/kms_fuzz_test.go index 158ddd8ac..a609e9128 100644 --- a/common/kms_fuzz_test.go +++ b/common/aws/kms_fuzz_test.go @@ -1,4 +1,4 @@ -package common +package aws import ( "bytes" diff --git a/node/auth/authenticator.go b/node/auth/authenticator.go index ac79a4f60..a5a1a464a 100644 --- a/node/auth/authenticator.go +++ b/node/auth/authenticator.go @@ -55,6 +55,9 @@ type requestAuthenticator struct { // authenticationTimeoutDuration is the duration for which an auth is valid. // If this is zero, then auth saving is disabled, and each request will be authenticated independently. authenticationTimeoutDuration time.Duration + + // disperserIDFilter is a function that returns true if the given disperser ID is valid. + disperserIDFilter func(uint32) bool } // NewRequestAuthenticator creates a new RequestAuthenticator. @@ -64,6 +67,7 @@ func NewRequestAuthenticator( keyCacheSize int, keyTimeoutDuration time.Duration, authenticationTimeoutDuration time.Duration, + disperserIDFilter func(uint32) bool, now time.Time) (RequestAuthenticator, error) { keyCache, err := lru.New[uint32, *keyWithTimeout](keyCacheSize) @@ -82,6 +86,7 @@ func NewRequestAuthenticator( keyTimeoutDuration: keyTimeoutDuration, authenticatedDispersers: authenticatedDispersers, authenticationTimeoutDuration: authenticationTimeoutDuration, + disperserIDFilter: disperserIDFilter, } err = authenticator.preloadCache(ctx, now) @@ -132,6 +137,11 @@ func (a *requestAuthenticator) getDisperserKey( ctx context.Context, now time.Time, disperserID uint32) (*gethcommon.Address, error) { + + if !a.disperserIDFilter(disperserID) { + return nil, fmt.Errorf("invalid disperser ID: %d", disperserID) + } + key, ok := a.keyCache.Get(disperserID) if ok { expirationTime := key.expiration diff --git a/node/auth/authenticator_test.go b/node/auth/authenticator_test.go index cac6bf891..28ae0122e 100644 --- a/node/auth/authenticator_test.go +++ b/node/auth/authenticator_test.go @@ -9,6 +9,7 @@ import ( wmock "github.com/Layr-Labs/eigenda/core/mock" "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/require" + "sync/atomic" "testing" "time" ) @@ -30,6 +31,7 @@ func TestValidRequest(t *testing.T) { 10, time.Minute, time.Minute, + func(uint32) bool { return true }, start) require.NoError(t, err) @@ -60,6 +62,7 @@ func TestInvalidRequestWrongHash(t *testing.T) { 10, time.Minute, time.Minute, + func(uint32) bool { return true }, start) require.NoError(t, err) @@ -93,6 +96,7 @@ func TestInvalidRequestWrongKey(t *testing.T) { 10, time.Minute, time.Minute, + func(uint32) bool { return true }, start) require.NoError(t, err) @@ -113,33 +117,59 @@ func TestInvalidRequestInvalidDisperserID(t *testing.T) { start := rand.Time() - publicKey, _ := rand.ECDSA() - disperserAddress := crypto.PubkeyToAddress(*publicKey) + publicKey0, privateKey0 := rand.ECDSA() + disperserAddress0 := crypto.PubkeyToAddress(*publicKey0) + + // This disperser will be loaded on chain (simulated), but will fail the valid disperser ID filter. + publicKey1, privateKey1 := rand.ECDSA() + disperserAddress1 := crypto.PubkeyToAddress(*publicKey1) chainReader := wmock.MockWriter{} - chainReader.Mock.On("GetDisperserAddress", uint32(0)).Return(disperserAddress, nil) + chainReader.Mock.On("GetDisperserAddress", uint32(0)).Return(disperserAddress0, nil) + chainReader.Mock.On("GetDisperserAddress", uint32(1)).Return(disperserAddress1, nil) chainReader.Mock.On("GetDisperserAddress", uint32(1234)).Return( nil, errors.New("disperser not found")) + filterCallCount := atomic.Uint32{} + authenticator, err := NewRequestAuthenticator( context.Background(), &chainReader, 10, time.Minute, - time.Minute, + 0, /* disable auth caching */ + func(id uint32) bool { + filterCallCount.Add(1) + return id != uint32(1) + }, start) require.NoError(t, err) + require.Equal(t, uint32(1), filterCallCount.Load()) request := RandomStoreChunksRequest(rand) - request.DisperserID = 1234 + request.DisperserID = 0 + signature, err := SignStoreChunksRequest(privateKey0, request) + require.NoError(t, err) + request.Signature = signature + err = authenticator.AuthenticateStoreChunksRequest(context.Background(), "localhost", request, start) + require.NoError(t, err) + require.Equal(t, uint32(2), filterCallCount.Load()) - _, differentPrivateKey := rand.ECDSA() - signature, err := SignStoreChunksRequest(differentPrivateKey, request) + request.DisperserID = 1 + signature, err = SignStoreChunksRequest(privateKey1, request) require.NoError(t, err) request.Signature = signature + err = authenticator.AuthenticateStoreChunksRequest(context.Background(), "localhost", request, start) + require.Error(t, err) + require.Equal(t, uint32(3), filterCallCount.Load()) + request.DisperserID = 1234 + signature, err = SignStoreChunksRequest(privateKey1, request) + require.NoError(t, err) + request.Signature = signature err = authenticator.AuthenticateStoreChunksRequest(context.Background(), "localhost", request, start) require.Error(t, err) + require.Equal(t, uint32(4), filterCallCount.Load()) } func TestAuthCaching(t *testing.T) { @@ -159,6 +189,7 @@ func TestAuthCaching(t *testing.T) { 10, time.Minute, time.Minute, + func(uint32) bool { return true }, start) require.NoError(t, err) @@ -224,6 +255,7 @@ func TestAuthCachingDisabled(t *testing.T) { 10, time.Minute, 0, // This disables auth caching + func(uint32) bool { return true }, start) require.NoError(t, err) @@ -267,6 +299,7 @@ func TestKeyExpiry(t *testing.T) { 10, time.Minute, time.Minute, + func(uint32) bool { return true }, start) require.NoError(t, err) @@ -321,6 +354,7 @@ func TestAuthCacheSize(t *testing.T) { cacheSize, time.Minute, time.Minute, + func(uint32) bool { return true }, start) require.NoError(t, err) @@ -411,6 +445,7 @@ func TestKeyCacheSize(t *testing.T) { cacheSize, time.Minute, 0, // disable auth caching + func(uint32) bool { return true }, start) require.NoError(t, err) diff --git a/node/grpc/server_v2.go b/node/grpc/server_v2.go index b75f018e1..acbc057f9 100644 --- a/node/grpc/server_v2.go +++ b/node/grpc/server_v2.go @@ -57,6 +57,9 @@ func NewServerV2( config.DispersalAuthenticationKeyCacheSize, config.DisperserKeyTimeout, config.DispersalAuthenticationTimeout, + func(id uint32) bool { + return id == api.EigenLabsDisperserID + }, time.Now()) if err != nil { return nil, fmt.Errorf("failed to create authenticator: %w", err) From 76efb7a3a2afe6fae73c315ebcec49406ddda1c8 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Tue, 7 Jan 2025 09:09:45 -0600 Subject: [PATCH 86/94] Lint Signed-off-by: Cody Littley --- inabox/deploy/deploy.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/inabox/deploy/deploy.go b/inabox/deploy/deploy.go index b4e964e27..e638090dd 100644 --- a/inabox/deploy/deploy.go +++ b/inabox/deploy/deploy.go @@ -210,9 +210,9 @@ func (env *Config) GenerateDisperserKeypair(ethClient common.EthClient) error { // Write the disperser's public key to on-chain storage - pk := env.Pks.EcdsaMap["default"].PrivateKey - pk = strings.TrimPrefix(pk, "0x") - pk = strings.TrimPrefix(pk, "0X") + //pk := env.Pks.EcdsaMap["default"].PrivateKey + //pk = strings.TrimPrefix(pk, "0x") + //pk = strings.TrimPrefix(pk, "0X") loggerConfig := common.DefaultLoggerConfig() logger, err := common.NewLogger(loggerConfig) From afe242f2daa224ea8cff7e099b17c436be3b832d Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Tue, 7 Jan 2025 10:26:07 -0600 Subject: [PATCH 87/94] Debug log Signed-off-by: Cody Littley --- node/auth/request_signing.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/auth/request_signing.go b/node/auth/request_signing.go index 6e660bbfa..1591c4807 100644 --- a/node/auth/request_signing.go +++ b/node/auth/request_signing.go @@ -29,7 +29,7 @@ func VerifyStoreChunksRequest(key gethcommon.Address, request *grpc.StoreChunksR signingPublicKey, err := crypto.SigToPub(requestHash, request.Signature) if err != nil { - return fmt.Errorf("failed to recover public key from signature: %w", err) + return fmt.Errorf("failed to recover public key from signature %x: %w", request.Signature, err) } signingAddress := crypto.PubkeyToAddress(*signingPublicKey) From 4419be1e8360a7edb69030e1478bd065c6826932 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Tue, 7 Jan 2025 10:47:49 -0600 Subject: [PATCH 88/94] Enable store chunks request signing. Signed-off-by: Cody Littley --- inabox/deploy/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inabox/deploy/config.go b/inabox/deploy/config.go index 3ddc995cd..f9acc7480 100644 --- a/inabox/deploy/config.go +++ b/inabox/deploy/config.go @@ -347,7 +347,7 @@ func (env *Config) generateControllerVars( CONTROLLER_AWS_ENDPOINT_URL: "", CONTROLLER_ENCODER_ADDRESS: "0.0.0.0:34001", CONTROLLER_FINALIZATION_BLOCK_DELAY: "0", - CONTROLLER_DISPERSER_STORE_CHUNKS_SIGNING_DISABLED: "true", + CONTROLLER_DISPERSER_STORE_CHUNKS_SIGNING_DISABLED: "false", CONTROLLER_DISPERSER_KMS_KEY_ID: disperserKeyID, } env.applyDefaults(&v, "CONTROLLER", "controller", ind) From 09b14ceec1de98a93b4882a13dca544a137d0189 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Tue, 7 Jan 2025 11:14:50 -0600 Subject: [PATCH 89/94] Generate key prior to writing env files. Signed-off-by: Cody Littley --- inabox/deploy/config.go | 7 +++-- inabox/deploy/deploy.go | 37 ++++++++++---------------- inabox/tests/integration_suite_test.go | 6 ++--- 3 files changed, 20 insertions(+), 30 deletions(-) diff --git a/inabox/deploy/config.go b/inabox/deploy/config.go index f9acc7480..896ac34cd 100644 --- a/inabox/deploy/config.go +++ b/inabox/deploy/config.go @@ -323,8 +323,7 @@ func (env *Config) generateEncoderV2Vars(ind int, grpcPort string) EncoderVars { func (env *Config) generateControllerVars( ind int, - graphUrl string, - disperserKeyID string) ControllerVars { + graphUrl string) ControllerVars { v := ControllerVars{ CONTROLLER_DYNAMODB_TABLE_NAME: "test-BlobMetadata-v2", @@ -348,7 +347,7 @@ func (env *Config) generateControllerVars( CONTROLLER_ENCODER_ADDRESS: "0.0.0.0:34001", CONTROLLER_FINALIZATION_BLOCK_DELAY: "0", CONTROLLER_DISPERSER_STORE_CHUNKS_SIGNING_DISABLED: "false", - CONTROLLER_DISPERSER_KMS_KEY_ID: disperserKeyID, + CONTROLLER_DISPERSER_KMS_KEY_ID: env.DisperserKMSKeyID, } env.applyDefaults(&v, "CONTROLLER", "controller", ind) @@ -728,7 +727,7 @@ func (env *Config) GenerateAllVariables() { // Controller name = "controller0" _, _, _, envFile = env.getPaths(name) - controllerConfig := env.generateControllerVars(0, graphUrl, env.DisperserKMSKeyID) + controllerConfig := env.generateControllerVars(0, graphUrl) writeEnv(controllerConfig.getEnvMap(), envFile) env.Controller = controllerConfig diff --git a/inabox/deploy/deploy.go b/inabox/deploy/deploy.go index e638090dd..8fc9d9421 100644 --- a/inabox/deploy/deploy.go +++ b/inabox/deploy/deploy.go @@ -160,21 +160,20 @@ func (env *Config) DeployExperiment() { env.deploySubgraphs(startBlock) } + fmt.Println("Generating disperser keypair") + err = env.GenerateDisperserKeypair() + if err != nil { + log.Panicf("could not generate disperser keypair: %v", err) + } + fmt.Println("Generating variables") env.GenerateAllVariables() - // TODO this is causing problems - //log.Print("Generating disperser keypair") - //err = env.generateDisperserKeypair() - //if err != nil { - // log.Panicf("could not generate disperser keypair: %v", err) - //} - fmt.Println("Test environment has successfully deployed!") } -// GenerateDisperserKeypair generates a disperser keypair using AWS KMS. Returns the key ID and the public address. -func (env *Config) GenerateDisperserKeypair(ethClient common.EthClient) error { +// GenerateDisperserKeypair generates a disperser keypair using AWS KMS. +func (env *Config) GenerateDisperserKeypair() error { // Generate a keypair in AWS KMS @@ -208,11 +207,13 @@ func (env *Config) GenerateDisperserKeypair(ethClient common.EthClient) error { log.Printf("Generated disperser keypair: key ID: %s, address: %s", env.DisperserKMSKeyID, env.DisperserAddress.Hex()) - // Write the disperser's public key to on-chain storage + return nil +} - //pk := env.Pks.EcdsaMap["default"].PrivateKey - //pk = strings.TrimPrefix(pk, "0x") - //pk = strings.TrimPrefix(pk, "0X") +// RegisterDisperserKeypair registers the disperser's public key on-chain. +func (env *Config) RegisterDisperserKeypair(ethClient common.EthClient) error { + + // Write the disperser's public key to on-chain storage loggerConfig := common.DefaultLoggerConfig() logger, err := common.NewLogger(loggerConfig) @@ -220,16 +221,6 @@ func (env *Config) GenerateDisperserKeypair(ethClient common.EthClient) error { return fmt.Errorf("could not create logger: %v", err) } - //ethClient, err := geth.NewMultiHomingClient(geth.EthClientConfig{ - // RPCURLs: []string{env.Deployers[0].RPC}, - // PrivateKeyString: pk, - // NumConfirmations: 0, - // NumRetries: 0, - //}, gcommon.Address{}, logger) - //if err != nil { - // return fmt.Errorf("could not create eth client: %v", err) - //} - writer, err := eth.NewWriter( logger, ethClient, diff --git a/inabox/tests/integration_suite_test.go b/inabox/tests/integration_suite_test.go index adf3a95d8..2ac100ba3 100644 --- a/inabox/tests/integration_suite_test.go +++ b/inabox/tests/integration_suite_test.go @@ -114,7 +114,7 @@ var _ = BeforeSuite(func() { Expect(err).To(BeNil()) fmt.Println("Deploying experiment") - testConfig.DeployExperiment() ////////////////////////////////////////// TODO + testConfig.DeployExperiment() ethClient = buildEthClient(numConfirmations) rpcClient, err = ethrpc.Dial(testConfig.Deployers[0].RPC) @@ -123,8 +123,8 @@ var _ = BeforeSuite(func() { fmt.Println("Registering blob versions and relays") relays = testConfig.RegisterBlobVersionAndRelays(ethClient) - fmt.Println("Generating disperser keypair") // TODO possibly follow exemple of RegisterBlobVersionAndRelays - err = testConfig.GenerateDisperserKeypair(ethClient) + fmt.Println("Registering disperser keypair") + err = testConfig.RegisterDisperserKeypair(ethClient) if err != nil { panic(err) } From 7355080785f75894f2b654ff2328966fba1045a7 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Wed, 8 Jan 2025 08:33:44 -0600 Subject: [PATCH 90/94] Fix merge conflict. --- inabox/deploy/deploy.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/inabox/deploy/deploy.go b/inabox/deploy/deploy.go index 8fc9d9421..273def0a8 100644 --- a/inabox/deploy/deploy.go +++ b/inabox/deploy/deploy.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "github.com/Layr-Labs/eigenda/common" + caws "github.com/Layr-Labs/eigenda/common/aws" relayreg "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDARelayRegistry" eigendasrvmg "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDAServiceManager" thresholdreg "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDAThresholdRegistry" @@ -198,7 +199,7 @@ func (env *Config) GenerateDisperserKeypair() error { // Load the public key and convert it to an Ethereum address - key, err := common.LoadPublicKeyKMS(context.Background(), keyManager, env.DisperserKMSKeyID) + key, err := caws.LoadPublicKeyKMS(context.Background(), keyManager, env.DisperserKMSKeyID) if err != nil { return fmt.Errorf("could not load public key: %v", err) } From 6003a8999a7974d8b07a03d80e4d8b4026d57960 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Wed, 8 Jan 2025 08:50:13 -0600 Subject: [PATCH 91/94] Cleanup Signed-off-by: Cody Littley --- core/eth/reader.go | 14 +----------- inabox/tests/integration_suite_test.go | 30 ++++++++++---------------- 2 files changed, 12 insertions(+), 32 deletions(-) diff --git a/core/eth/reader.go b/core/eth/reader.go index b9f4716be..c52312412 100644 --- a/core/eth/reader.go +++ b/core/eth/reader.go @@ -5,7 +5,6 @@ import ( "crypto/ecdsa" "fmt" "math/big" - "runtime" "strings" "github.com/Layr-Labs/eigenda/common" @@ -435,19 +434,8 @@ func (t *Reader) GetOperatorStakesForQuorums(ctx context.Context, quorums []core Context: ctx, }, t.bindings.RegCoordinatorAddr, quorumBytes, blockNumber) if err != nil { - - // TODO don't merge this - b := make([]byte, 1024*64) // adjust buffer size to be larger than expected stack - n := runtime.Stack(b, false) - s := string(b[:n]) - - elements := strings.Split(s, "\n") - for _, element := range elements { - t.logger.Error(element) - } - t.logger.Errorf("Failed to fetch operator state: %s", err) - return nil, err + return nil, fmt.Errorf("failed to fetch operator state: %w", err) } state := make(core.OperatorStakes, len(state_)) diff --git a/inabox/tests/integration_suite_test.go b/inabox/tests/integration_suite_test.go index 2ac100ba3..f95d8ebcc 100644 --- a/inabox/tests/integration_suite_test.go +++ b/inabox/tests/integration_suite_test.go @@ -116,7 +116,17 @@ var _ = BeforeSuite(func() { fmt.Println("Deploying experiment") testConfig.DeployExperiment() - ethClient = buildEthClient(numConfirmations) + pk := testConfig.Pks.EcdsaMap["default"].PrivateKey + pk = strings.TrimPrefix(pk, "0x") + pk = strings.TrimPrefix(pk, "0X") + ethClient, err = geth.NewMultiHomingClient(geth.EthClientConfig{ + RPCURLs: []string{testConfig.Deployers[0].RPC}, + PrivateKeyString: pk, + NumConfirmations: numConfirmations, + NumRetries: numRetries, + }, gcommon.Address{}, logger) + Expect(err).To(BeNil()) + rpcClient, err = ethrpc.Dial(testConfig.Deployers[0].RPC) Expect(err).To(BeNil()) @@ -139,24 +149,6 @@ var _ = BeforeSuite(func() { } }) -// TODO revert this change -// buildEthClient builds an Ethereum client with the given number of required confirmations -func buildEthClient(confirmations int) common.EthClient { - pk := testConfig.Pks.EcdsaMap["default"].PrivateKey - pk = strings.TrimPrefix(pk, "0x") - pk = strings.TrimPrefix(pk, "0X") - - ec, err := geth.NewMultiHomingClient(geth.EthClientConfig{ - RPCURLs: []string{testConfig.Deployers[0].RPC}, - PrivateKeyString: pk, - NumConfirmations: confirmations, - NumRetries: numRetries, - }, gcommon.Address{}, logger) - - Expect(err).To(BeNil()) - return ec -} - func setupRetrievalClient(testConfig *deploy.Config) error { ethClientConfig := geth.EthClientConfig{ RPCURLs: []string{testConfig.Deployers[0].RPC}, From 8cbdd608736e8743a593888565f9027e4c030e22 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Thu, 9 Jan 2025 08:18:48 -0600 Subject: [PATCH 92/94] Made suggested change Signed-off-by: Cody Littley --- inabox/deploy/config.go | 3 +++ inabox/deploy/config_types.go | 3 +++ inabox/deploy/deploy.go | 4 ++-- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/inabox/deploy/config.go b/inabox/deploy/config.go index 20080e75e..926dfae23 100644 --- a/inabox/deploy/config.go +++ b/inabox/deploy/config.go @@ -577,6 +577,9 @@ func (env *Config) GenerateAllVariables() { // hardcode graphurl for now graphUrl := "http://localhost:8000/subgraphs/name/Layr-Labs/eigenda-operator-state" + env.localstackEndpoint = "http://localhost:4570" + env.localstackRegion = "us-east-1" + // Create envs directory createDirectory(env.Path + "/envs") changeDirectory(env.rootPath + "/inabox") diff --git a/inabox/deploy/config_types.go b/inabox/deploy/config_types.go index 39591c8d4..9ae47227b 100644 --- a/inabox/deploy/config_types.go +++ b/inabox/deploy/config_types.go @@ -183,6 +183,9 @@ type Config struct { Controller ControllerVars Relays []RelayVars + localstackEndpoint string + localstackRegion string + // DisperserAddress is the address of disperser 0 (aka the only disperser at the current time) DisperserAddress common.Address diff --git a/inabox/deploy/deploy.go b/inabox/deploy/deploy.go index 273def0a8..7ebe56420 100644 --- a/inabox/deploy/deploy.go +++ b/inabox/deploy/deploy.go @@ -179,8 +179,8 @@ func (env *Config) GenerateDisperserKeypair() error { // Generate a keypair in AWS KMS keyManager := kms.New(kms.Options{ - Region: "us-east-1", - BaseEndpoint: aws.String("http://localhost:4570"), // TODO don't hard code this + Region: env.localstackRegion, + BaseEndpoint: aws.String(env.localstackEndpoint), }) createKeyOutput, err := keyManager.CreateKey(context.Background(), &kms.CreateKeyInput{ From cba0358b12290b7786a8a7d1b92352b2de63ed39 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Thu, 9 Jan 2025 14:09:50 -0600 Subject: [PATCH 93/94] Made suggested changes. Signed-off-by: Cody Littley --- core/eth/writer.go | 4 ++-- inabox/deploy/deploy.go | 27 +++++++++++++++++++-------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/core/eth/writer.go b/core/eth/writer.go index 28e6fb210..5e8770f5d 100644 --- a/core/eth/writer.go +++ b/core/eth/writer.go @@ -7,7 +7,7 @@ import ( "encoding/json" "fmt" "github.com/Layr-Labs/eigenda/api" - contractEigenDADisperserRegistry "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDADisperserRegistry" + dreg "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDADisperserRegistry" "log" "math/big" @@ -354,7 +354,7 @@ func (t *Writer) SetDisperserAddress(ctx context.Context, address gethcommon.Add transaction, err := registry.SetDisperserInfo( options, api.EigenLabsDisperserID, - contractEigenDADisperserRegistry.DisperserInfo{ + dreg.DisperserInfo{ DisperserAddress: address, }) if err != nil { diff --git a/inabox/deploy/deploy.go b/inabox/deploy/deploy.go index 7ebe56420..daa4d2811 100644 --- a/inabox/deploy/deploy.go +++ b/inabox/deploy/deploy.go @@ -21,6 +21,7 @@ import ( "path/filepath" "strconv" "strings" + "time" "github.com/Layr-Labs/eigenda/core" "github.com/ethereum/go-ethereum/accounts/abi/bind" @@ -188,7 +189,9 @@ func (env *Config) GenerateDisperserKeypair() error { KeyUsage: types.KeyUsageTypeSignVerify, }) if err != nil { - if strings.Contains(err.Error(), "connect: connection refused") { + if strings.Contains(err.Error(), "connect: connection refused") || + strings.Contains(err.Error(), "request send failed") { + log.Printf("Unable to reach local stack, skipping disperser keypair generation. Error: %v", err) err = nil } @@ -238,16 +241,24 @@ func (env *Config) RegisterDisperserKeypair(ethClient common.EthClient) error { // Read the disperser's public key from on-chain storage to verify it was written correctly - address, err := writer.GetDisperserAddress(context.Background(), 0) - if err != nil { - return fmt.Errorf("could not get disperser address: %v", err) - } + retryTimeout := time.Now().Add(1 * time.Minute) + ticker := time.NewTicker(1 * time.Second) - if address != env.DisperserAddress { - return fmt.Errorf("expected disperser address %s, got %s", env.DisperserAddress, address) + for time.Now().Before(retryTimeout) { + address, err := writer.GetDisperserAddress(context.Background(), 0) + if err != nil { + logger.Warnf("could not get disperser address: %v", err) + } else { + if address != env.DisperserAddress { + return fmt.Errorf("expected disperser address %s, got %s", env.DisperserAddress, address) + } + return nil + } + + <-ticker.C } - return nil + return fmt.Errorf("timed out waiting for disperser address to be set") } func (env *Config) RegisterBlobVersionAndRelays(ethClient common.EthClient) map[uint32]string { From 57abb29b0e668607dccd458d18ec1717ff43a3df Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Fri, 10 Jan 2025 08:30:05 -0600 Subject: [PATCH 94/94] Fix test issue Signed-off-by: Cody Littley --- inabox/deploy/deploy.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/inabox/deploy/deploy.go b/inabox/deploy/deploy.go index daa4d2811..3e29860ed 100644 --- a/inabox/deploy/deploy.go +++ b/inabox/deploy/deploy.go @@ -162,6 +162,11 @@ func (env *Config) DeployExperiment() { env.deploySubgraphs(startBlock) } + // Ideally these should be set in GenerateAllVariables, but they need to be used in GenerateDisperserKeypair + // which is called before GenerateAllVariables + env.localstackEndpoint = "http://localhost:4570" + env.localstackRegion = "us-east-1" + fmt.Println("Generating disperser keypair") err = env.GenerateDisperserKeypair() if err != nil { @@ -189,9 +194,7 @@ func (env *Config) GenerateDisperserKeypair() error { KeyUsage: types.KeyUsageTypeSignVerify, }) if err != nil { - if strings.Contains(err.Error(), "connect: connection refused") || - strings.Contains(err.Error(), "request send failed") { - + if strings.Contains(err.Error(), "connect: connection refused") { log.Printf("Unable to reach local stack, skipping disperser keypair generation. Error: %v", err) err = nil }