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 ccdd538
Show file tree
Hide file tree
Showing 4 changed files with 289 additions and 17 deletions.
96 changes: 96 additions & 0 deletions utils/crypto/bls/signers/json-rpc/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package jsonrpc

import (
"bytes"
"fmt"
"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)

// print the requestBody as a string
fmt.Println(string(requestBody))

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()}
}
79 changes: 62 additions & 17 deletions utils/crypto/bls/test/bls_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,40 @@
package bls_test

import (
"io"
"net/url"
"testing"

"github.com/stretchr/testify/require"

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

type Signer interface {
bls.Signer
io.Closer
}

type jsonRPCSigner struct {
*jsonrpc.Client
server *jsonrpc.Server
}

func (s jsonRPCSigner) Close() error {
return s.server.Close()
}

type localSigner struct {
*local.LocalSigner
}

func (s localSigner) Close() error {
return nil
}

func TestAggregation(t *testing.T) {
type test struct {
name string
Expand Down Expand Up @@ -418,17 +443,42 @@ func TestVerify(t *testing.T) {
func TestVerifyProofOfPossession(t *testing.T) {
type test struct {
name string
signers []func() (bls.Signer, error)
signers []func() (Signer, error)
setup func(*require.Assertions, bls.Signer) (pk *bls.PublicKey, sig *bls.Signature, msg []byte)
expectedValid bool
}

localSignerFn := func() (Signer, error) {
signer, err := local.NewSigner()
return &localSigner{LocalSigner: signer}, err
}
serverSignerFn := func() (Signer, error) {
// do I need to make sure the server gets closed properly?
service := jsonrpc.NewSignerService()
server, err := jsonrpc.Serve(service)
if err != nil {
return nil, err
}

url := url.URL{
Scheme: "http",
Host: server.Addr().String(),
}

client := jsonrpc.NewClient(url)

return jsonRPCSigner{server: server, Client: client}, nil
}

signerFns := []func() (Signer, error){
localSignerFn,
serverSignerFn,
}

tests := []test{
{
name: "valid",
signers: []func() (bls.Signer, error){
func() (bls.Signer, error) { return local.NewSigner() },
},
name: "valid",
signers: signerFns,
setup: func(require *require.Assertions, signer bls.Signer) (*bls.PublicKey, *bls.Signature, []byte) {
pk := signer.PublicKey()
msg := utils.RandomBytes(1234)
Expand All @@ -438,10 +488,8 @@ func TestVerifyProofOfPossession(t *testing.T) {
expectedValid: true,
},
{
name: "wrong message",
signers: []func() (bls.Signer, error){
func() (bls.Signer, error) { return local.NewSigner() },
},
name: "wrong message",
signers: signerFns,
setup: func(require *require.Assertions, signer bls.Signer) (*bls.PublicKey, *bls.Signature, []byte) {
pk := signer.PublicKey()
msg := utils.RandomBytes(1234)
Expand All @@ -452,10 +500,8 @@ func TestVerifyProofOfPossession(t *testing.T) {
expectedValid: false,
},
{
name: "wrong pub key",
signers: []func() (bls.Signer, error){
func() (bls.Signer, error) { return local.NewSigner() },
},
name: "wrong pub key",
signers: signerFns,
setup: func(require *require.Assertions, signer bls.Signer) (*bls.PublicKey, *bls.Signature, []byte) {
msg := utils.RandomBytes(1234)
sig := signer.SignProofOfPossession(msg)
Expand All @@ -468,10 +514,8 @@ func TestVerifyProofOfPossession(t *testing.T) {
expectedValid: false,
},
{
name: "wrong sig",
signers: []func() (bls.Signer, error){
func() (bls.Signer, error) { return local.NewSigner() },
},
name: "wrong sig",
signers: signerFns,
setup: func(_ *require.Assertions, signer bls.Signer) (*bls.PublicKey, *bls.Signature, []byte) {
pk := signer.PublicKey()
msg := utils.RandomBytes(1234)
Expand All @@ -496,6 +540,7 @@ func TestVerifyProofOfPossession(t *testing.T) {
require.Equal(tt.expectedValid, valid)
valid = bls.Verify(pk, sig, msg)
require.False(valid)
signer.Close()
}
})
}
Expand Down

0 comments on commit ccdd538

Please sign in to comment.