Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement g1 sign api #25

Merged
merged 6 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ toolchain go1.22.3
require (
cloud.google.com/go/secretmanager v1.14.2
github.com/Layr-Labs/bn254-keystore-go v0.0.0-20250107020618-26bd412fae87
github.com/Layr-Labs/cerberus-api v0.0.2-0.20250107174124-05df6050f723
github.com/Layr-Labs/cerberus-api v0.0.2-0.20250108174619-d5e1eb03fbd5
github.com/aws/aws-sdk-go-v2 v1.32.5
github.com/aws/aws-sdk-go-v2/config v1.28.5
github.com/aws/aws-sdk-go-v2/credentials v1.17.46
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg6
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Layr-Labs/bn254-keystore-go v0.0.0-20250107020618-26bd412fae87 h1:EkaBNT0o8RTgtFeYSKaoNHNbnCVxrcsAyRpUeN29hiQ=
github.com/Layr-Labs/bn254-keystore-go v0.0.0-20250107020618-26bd412fae87/go.mod h1:7J8hptSX8cFq7KmVb+rEO5aEifj7E44c3i0afIyr4WA=
github.com/Layr-Labs/cerberus-api v0.0.2-0.20250107174124-05df6050f723 h1:f6gJS/egys133nGcOGKduiPHq9hyK9KRiEB9fARB5t0=
github.com/Layr-Labs/cerberus-api v0.0.2-0.20250107174124-05df6050f723/go.mod h1:Lm4fhzy0S3P7GjerzuseGaBFVczsIKmEhIjcT52Hluo=
github.com/Layr-Labs/cerberus-api v0.0.2-0.20250108174619-d5e1eb03fbd5 h1:s24M6HYObEuV9OSY36jUM09kp5fOhuz/g1ev2qWDPzU=
github.com/Layr-Labs/cerberus-api v0.0.2-0.20250108174619-d5e1eb03fbd5/go.mod h1:Lm4fhzy0S3P7GjerzuseGaBFVczsIKmEhIjcT52Hluo=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/aws/aws-sdk-go-v2 v1.32.5 h1:U8vdWJuY7ruAkzaOdD7guwJjD06YSKmnKCJs7s3IkIo=
Expand Down
40 changes: 39 additions & 1 deletion internal/services/signing/signing.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,5 +71,43 @@ func (s *Service) SignGeneric(
// Sign the data with the private key
sig := blsKey.SignMessage(byteArray)
s.logger.Info(fmt.Sprintf("Signed a message successfully using %s", pubKeyHex))
return &v1.SignGenericResponse{Signature: sig.Serialize()}, nil
signatureBytes := sig.RawBytes()
return &v1.SignGenericResponse{Signature: signatureBytes[:]}, nil
}

func (s *Service) SignG1(
shrimalmadhur marked this conversation as resolved.
Show resolved Hide resolved
ctx context.Context,
req *v1.SignG1Request,
) (*v1.SignG1Response, error) {
// Take the public key and data from the request
pubKeyHex := common.Trim0x(req.GetPublicKeyG1())
password := req.GetPassword()

if pubKeyHex == "" {
return nil, status.Error(codes.InvalidArgument, "public key is required")
}

g1Bytes := req.GetData()
if len(g1Bytes) == 0 {
return nil, status.Error(codes.InvalidArgument, "data must be > 0 bytes")
}

if _, ok := s.keyCache[pubKeyHex]; !ok {
Copy link

Choose a reason for hiding this comment

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

this isn't thread safe the way this cache is updated and checked, I would wrap the get/set in a mutex

Choose a reason for hiding this comment

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

Agree with you George. Risk is really small here since the store is local and doesn't have eventual consistency but locking is needed for correctness, with this approach.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The reason this doesn't necessarily need to be thread safe is because the value of the key will never be different. So practically there's no race condition on the value of the key. Give the only downside it has is to load from disk again which is a decent trade off to avoid mutex overhead. Let me know if that makes sense.

Choose a reason for hiding this comment

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

I see. If the key isn't mutable then race condition concern is moot.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That is correct

Copy link

Choose a reason for hiding this comment

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

I am pretty sure this could end up panicing even with different keys, since hash map might need to grow when adding a new key. There is a sync.map which is thread safe.

Copy link

Choose a reason for hiding this comment

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

here is a sample in a go playground: https://go.dev/play/p/mRFKiEikPuc

fatal error: concurrent map writes

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

this makes sense - thanks for pointing that out. updated

s.logger.Info(fmt.Sprintf("In memory cache miss. Retrieving key for %s", pubKeyHex))
blsKey, err := s.store.RetrieveKey(ctx, pubKeyHex, password)
if err != nil {
s.logger.Error(fmt.Sprintf("Failed to retrieve key: %v", err))
return nil, status.Error(codes.Internal, err.Error())
}
s.keyCache[pubKeyHex] = blsKey
}
blsKey := s.keyCache[pubKeyHex]

g1Point := new(crypto.G1Point)
g1Point = g1Point.Deserialize(g1Bytes)

sig := blsKey.SignHashedToCurveMessage(g1Point.G1Affine)
s.logger.Info(fmt.Sprintf("Signed a G1 message successfully using %s", pubKeyHex))
signatureBytes := sig.RawBytes()
return &v1.SignG1Response{Signature: signatureBytes[:]}, nil
}
26 changes: 26 additions & 0 deletions internal/services/signing/signing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,29 @@ func TestSigning(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, expectedSig, hex.EncodeToString(resp.Signature))
}

func TestSigningG1(t *testing.T) {
// private key: 0x040ad69253b921aca71dd714cccc3095576fbe1a21f86c9b10cb5b119b1c6899
pubKeyHex := "a3111a2232584734d526d62cbb7c9a0d4ce1984a92b7ecb85bde8878fea5d1b0"
password := "p@$$w0rd"
expectedSig := "24a87f9eab63a40c62831d2e9598e698f8819b15093c268b89c1a521f7d986650000000000000000000000000000000000000000000000000000000000000000"
data := []byte("somedata")
var bytes [64]byte
copy(bytes[:], data)

config := &configuration.Configuration{
KeystoreDir: "testdata/keystore",
}
logger := testutils.GetTestLogger()
store := filesystem.NewStore(config.KeystoreDir, logger)
m := metrics.NewNoopRPCMetrics()
signingService := NewService(config, store, logger, m)

resp, err := signingService.SignG1(context.Background(), &v1.SignG1Request{
PublicKeyG1: pubKeyHex,
Data: bytes[:],
Password: password,
})
assert.NoError(t, err)
assert.Equal(t, expectedSig, hex.EncodeToString(resp.Signature))
}
Loading