Skip to content

Commit

Permalink
Add test for networked signer
Browse files Browse the repository at this point in the history
  • Loading branch information
richardpringle committed Dec 19, 2024
1 parent b731e7e commit 435d116
Show file tree
Hide file tree
Showing 4 changed files with 371 additions and 105 deletions.
92 changes: 92 additions & 0 deletions utils/crypto/bls/signers/json-rpc/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package jsonrpc

import (
"bytes"
"net/http"
"net/url"

"github.com/ava-labs/avalanchego/utils/crypto/bls"
"github.com/gorilla/rpc/v2/json2"
)

type Client struct {
// http client
http *http.Client
url url.URL
}

func NewClient(url url.URL) *Client {
return &Client{
http: &http.Client{},
url: url,
}
}

func (client *Client) call(method string, params []interface{}, result interface{}) error {
requestBody, err := json2.EncodeClientRequest(method, params)

if err != nil {
return err
}

resp, err := client.http.Post(client.url.String(), "application/json", bytes.NewBuffer(requestBody))

if err != nil {
return err
}

return json2.DecodeClientResponse(resp.Body, result)
}

func (c *Client) PublicKey() *bls.PublicKey {
reply := new(PublicKeyReply)

err := c.call("Signer.PublicKey", []interface{}{PublicKeyArgs{}}, reply)

if err != nil {
panic(err)
}

pk := new(bls.PublicKey)
pk = pk.Deserialize(reply.PublicKey)

return pk
}

// Sign [msg] to authorize this message
func (c *Client) Sign(msg []byte) *bls.Signature {
// request the public key from the json-rpc server
reply := new(SignReply)
err := c.call("Signer.Sign", []interface{}{SignArgs{msg}}, reply)

// TODO: handle this
if err != nil {
panic(err)
}

// deserialize the public key
sig := new(bls.Signature)
sig = sig.Deserialize(reply.Signature)

// can be nil if the public key is invalid
return sig
}

// Sign [msg] to prove the ownership
func (c *Client) SignProofOfPossession(msg []byte) *bls.Signature {
// request the public key from the json-rpc server
reply := new(SignReply)
err := c.call("Signer.SignProofOfPossession", []interface{}{SignArgs{msg}}, reply)

// TODO: handle this
if err != nil {
panic(err)
}

// deserialize the public key
sig := new(bls.Signature)
sig = sig.Deserialize(reply.Signature)

// can be nil if the public key is invalid
return sig
}
23 changes: 23 additions & 0 deletions utils/crypto/bls/signers/json-rpc/messages.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package jsonrpc

type PublicKeyArgs struct{}

type SignArgs struct {
Msg []byte
}

type SignProofOfPossessionArgs struct {
Msg []byte
}

type PublicKeyReply struct {
PublicKey []byte
}

type SignReply struct {
Signature []byte
}

type SignProofOfPossessionReply struct {
Signature []byte
}
108 changes: 108 additions & 0 deletions utils/crypto/bls/signers/json-rpc/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package jsonrpc

import (
"context"
"log"
"net"
"net/http"
"time"

"github.com/ava-labs/avalanchego/utils/crypto/bls"
"github.com/ava-labs/avalanchego/utils/crypto/bls/signers/local"
"github.com/gorilla/rpc/v2"
"github.com/gorilla/rpc/v2/json"
)

type signerService struct {
signer *local.LocalSigner
}

type Server struct {
httpServer *http.Server
listener net.Listener
}

func NewSignerService() *signerService {
signer, err := local.NewSigner()

if err != nil {
panic(err)
}

return &signerService{signer: signer}
}

func Serve(service *signerService) (*Server, error) {
server := rpc.NewServer()
server.RegisterCodec(json.NewCodec(), "application/json")

err := server.RegisterService(service, "Signer")
if err != nil {
return nil, err
}

httpServer := &http.Server{
Handler: server,
}

listener, err := net.Listen("tcp", "")
if err != nil {
return nil, err
}

go func() {
if err := httpServer.Serve(listener); err != nil && err != http.ErrServerClosed {
log.Fatal(err)
}
}()

return &Server{
httpServer: httpServer,
listener: listener,
}, nil
}

func (s *Server) Addr() net.Addr {
return s.listener.Addr()
}

func (s *Server) Close() error {
// Create a context with a timeout to allow for graceful shutdown
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()

// Shutdown the HTTP server
if err := s.httpServer.Shutdown(ctx); err != nil {
return err
}

// Close the listener
return s.listener.Close()
}

func (s *signerService) PublicKey(r *http.Request, args *PublicKeyArgs, reply *PublicKeyReply) error {
*reply = toPkReply(s.signer.PublicKey())
return nil
}

func (s *signerService) Sign(r *http.Request, args *struct{ Msg []byte }, reply *SignReply) error {
*reply = toSignReply(s.signer.Sign(args.Msg))
return nil
}

func (s *signerService) SignProofOfPossession(r *http.Request, args *struct{ Msg []byte }, reply *SignProofOfPossessionReply) error {
*reply = toSignProofOfPossessionReply(s.signer.SignProofOfPossession(args.Msg))
return nil
}

func toPkReply(pk *bls.PublicKey) PublicKeyReply {
return PublicKeyReply{PublicKey: pk.Serialize()}
}

func toSignReply(sig *bls.Signature) SignReply {
return SignReply{Signature: sig.Serialize()}
}

func toSignProofOfPossessionReply(sig *bls.Signature) SignProofOfPossessionReply {
return SignProofOfPossessionReply{Signature: sig.Serialize()}
}
Loading

0 comments on commit 435d116

Please sign in to comment.