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

gRPC implementation for Mino - Handler.Process #1

Merged
merged 18 commits into from
Mar 6, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,41 @@
# DEDIS Ledger Fabric

This framework aims to deliver a modular approach to a public ledger implementation.

[![Build Status](https://travis-ci.org/dedis/fabric.svg?branch=master)](https://travis-ci.org/dedis/fabric) [![Coverage Status](https://coveralls.io/repos/github/dedis/fabric/badge.svg?branch=master)](https://coveralls.io/github/dedis/fabric?branch=master)

## Terminologies

- **blockchain** - A blockchain is...

- **cosi** - Cosi stands for *Collective Signature**, ...

- **fabric** - Fabric is a set of modules definition and their implementations.
We choose this name for this project because it captures well the idea of
providing a modular framework that allows one to either use a module's
implementation or define its own. Fabric is not about a particular blockchain
implementation, it's about the definitions of modular pieces that build
a blockchain.

- **ledger** - A ledger is...

- **message** - A message...

- **mino** - Mino stands for *Minimalist Network Overlay*, it is the abstraction
that defines how to register and use RPCs over a distributed set of nodes.

- **node** - A node is...

- **proof** - A proof is...

- **protobuf** - Protobuf is...

- **roster** - A roster is...

- **RPC** - RPC stands for *Remote Procedure Call*...

- **seal** - A seal is...

- **skipchain** - A skipchain is...

- **state** - A state is...
10 changes: 9 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,17 @@ module go.dedis.ch/fabric
go 1.13

require (
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect
github.com/golang/protobuf v1.3.3
github.com/gorilla/websocket v1.4.1 // indirect
github.com/improbable-eng/grpc-web v0.12.0
github.com/rs/cors v1.7.0 // indirect
github.com/rs/zerolog v1.18.0
github.com/stretchr/testify v1.5.1
go.dedis.ch/kyber/v3 v3.0.12
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 // indirect
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
google.golang.org/grpc v1.27.1
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc // indirect
)
223 changes: 223 additions & 0 deletions go.sum

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions mino/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Mino

Minimalist Network Overlay
7 changes: 4 additions & 3 deletions mino/minoch/manager.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package minoch

import (
"errors"
"sync"

"golang.org/x/xerrors"
)

// Manager is an orchestrator to manage the communication between the local
Expand All @@ -29,14 +30,14 @@ func (m *Manager) get(id string) *Minoch {
func (m *Manager) insert(inst *Minoch) error {
id := inst.Address().GetId()
if id == "" {
return errors.New("identifier must not be empty")
return xerrors.New("identifier must not be empty")
}

m.Lock()
defer m.Unlock()

if _, ok := m.instances[id]; ok {
return errors.New("identifier already exists")
return xerrors.New("identifier already exists")
}

m.instances[id] = inst
Expand Down
4 changes: 2 additions & 2 deletions mino/minoch/mod.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ func (m *Minoch) Address() *mino.Address {
return &mino.Address{Id: m.identifier}
}

// MakePath returns an instance restricted to the path.
func (m *Minoch) MakePath(path string) (mino.Mino, error) {
// MakeNamespace returns an instance restricted to the namespace.
func (m *Minoch) MakeNamespace(path string) (mino.Mino, error) {
newMinoch := &Minoch{
identifier: m.identifier,
path: fmt.Sprintf("%s/%s", m.path, path),
Expand Down
4 changes: 2 additions & 2 deletions mino/minoch/mod_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@ func TestMinoch_Address(t *testing.T) {
require.Equal(t, "A", addr.GetId())
}

func TestMinoch_MakePath(t *testing.T) {
func TestMinoch_MakeNamespace(t *testing.T) {
manager := NewManager()

m, err := NewMinoch(manager, "A")
require.NoError(t, err)

m2, err := m.MakePath("abc")
m2, err := m.MakeNamespace("abc")
require.NoError(t, err)
require.Equal(t, m.identifier, m2.(*Minoch).identifier)
require.Equal(t, "/abc", m2.(*Minoch).path)
Expand Down
4 changes: 2 additions & 2 deletions mino/minoch/rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ package minoch

import (
"context"
"errors"

"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes"
"go.dedis.ch/fabric/mino"
"golang.org/x/xerrors"
)

// RPC is an implementation of the mino.RPC interface.
Expand Down Expand Up @@ -139,6 +139,6 @@ func (r receiver) Recv(ctx context.Context) (*mino.Address, proto.Message, error
case err := <-r.errs:
return nil, nil, err
case <-ctx.Done():
return nil, nil, errors.New("timeout")
return nil, nil, xerrors.New("timeout")
}
}
96 changes: 96 additions & 0 deletions mino/minogrpc/mod.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,99 @@
// Package minogrpc is an implementation of MINO using gRPC to communicate
// over the network.
// This package implements the interfaces defined by Mino
package minogrpc

import (
fmt "fmt"
"regexp"

"go.dedis.ch/fabric/mino"
"golang.org/x/xerrors"
)

//go:generate protoc -I ./ --go_out=plugins=grpc:./ ./overlay.proto

var namespaceMatch = regexp.MustCompile("^[a-zA-Z0-9]+$")

// Minogrpc represents a grpc service restricted to a namespace
type Minogrpc struct {
server Server
namespace string
}

// NewMinogrpc sets up the grpc and http servers. It does not start the
// server. Identifier must be an address with a port, something like
// 127.0.0.1:3333
//
// TODO: use a different type of argument for identifier, maybe net/url ?
func NewMinogrpc(identifier string) (Minogrpc, error) {
minoGrpc := Minogrpc{}

addr := &mino.Address{
Id: identifier,
}

server, err := CreateServer(addr)
if err != nil {
return minoGrpc, xerrors.Errorf("failed to create server: %v", err)
}

err = server.StartServer()
if err != nil {
return minoGrpc, xerrors.Errorf("failed to start the server: %v", err)
}

peer := Peer{
Address: server.listener.Addr().String(),
Certificate: server.cert.Leaf,
}
server.neighbours[identifier] = peer

minoGrpc.server = *server
minoGrpc.namespace = ""

return minoGrpc, err
}

// Address returns the address of the server
func (m Minogrpc) Address() *mino.Address {
return m.server.addr
}

// MakeNamespace creates a new Minogrpc struct that has the specified namespace.
// This namespace is further used to scope newly created RPCs. There can be
// multiple namespaces. If there is already a namespace, then the new one will
// be concatenated leading to namespace1/namespace2. A namespace can not be
// empty an should match [a-zA-Z0-9]+
func (m Minogrpc) MakeNamespace(namespace string) (mino.Mino, error) {
if namespace == "" {
return nil, xerrors.Errorf("a namespace can not be empty")
}

ok := namespaceMatch.MatchString(namespace)
if !ok {
return nil, xerrors.Errorf("a namespace should match [a-zA-Z0-9]+, "+
"but found '%s'", namespace)
}

newM := Minogrpc{
server: m.server,
namespace: namespace,
nkcr marked this conversation as resolved.
Show resolved Hide resolved
}
return newM, nil
}

// MakeRPC registers the handler using a uniq URI of form "namespace/name". It
// returns a struct that allows client to call the RPC.
func (m Minogrpc) MakeRPC(name string, h mino.Handler) (mino.RPC, error) {
URI := fmt.Sprintf("%s/%s", m.namespace, name)
rpc := RPC{
handler: h,
srv: m.server,
uri: URI,
}

m.server.handlers[URI] = h

return rpc, nil
}
95 changes: 95 additions & 0 deletions mino/minogrpc/mod_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package minogrpc

import (
fmt "fmt"
"testing"

"github.com/stretchr/testify/require"
"go.dedis.ch/fabric/mino"
)

func Test_NewMinogrpc(t *testing.T) {
id := "127.0.0.1:3333"

minoRPC, err := NewMinogrpc(id)
require.NoError(t, err)

require.Equal(t, id, minoRPC.Address().GetId())
require.Equal(t, "", minoRPC.namespace)

peer := Peer{
Address: id,
Certificate: minoRPC.server.cert.Leaf,
}

require.Equal(t, peer, minoRPC.server.neighbours[id])
}

func Test_MakeNamespace(t *testing.T) {
minoGrpc := Minogrpc{}
ns := "Test"
newMino, err := minoGrpc.MakeNamespace(ns)
require.NoError(t, err)

newMinoGrpc, ok := newMino.(Minogrpc)
require.True(t, ok)

require.Equal(t, ns, newMinoGrpc.namespace)

// A namespace can not be empty
ns = ""
newMino, err = minoGrpc.MakeNamespace(ns)
require.EqualError(t, err, "a namespace can not be empty")

// A namespace should match [a-zA-Z0-9]+
ns = "/namespace"
newMino, err = minoGrpc.MakeNamespace(ns)
require.EqualError(t, err, "a namespace should match [a-zA-Z0-9]+, but found '/namespace'")

ns = " test"
newMino, err = minoGrpc.MakeNamespace(ns)
require.EqualError(t, err, "a namespace should match [a-zA-Z0-9]+, but found ' test'")

ns = "test$"
newMino, err = minoGrpc.MakeNamespace(ns)
require.EqualError(t, err, "a namespace should match [a-zA-Z0-9]+, but found 'test$'")
}

func Test_Address(t *testing.T) {
addr := &mino.Address{
Id: "test",
}
minoGrpc := Minogrpc{
server: Server{
addr: addr,
},
}

require.Equal(t, addr, minoGrpc.Address())
}

func Test_MakeRPC(t *testing.T) {
minoGrpc := Minogrpc{}
minoGrpc.namespace = "namespace"
minoGrpc.server = Server{
handlers: make(map[string]mino.Handler),
}

handler := testSameHandler{}

rpc, err := minoGrpc.MakeRPC("name", handler)
require.NoError(t, err)

expectedRPC := RPC{
handler: handler,
srv: minoGrpc.server,
uri: fmt.Sprintf("namespace/name"),
}

h, ok := minoGrpc.server.handlers[expectedRPC.uri]
require.True(t, ok)
require.Equal(t, handler, h)

require.Equal(t, expectedRPC, rpc)

}
Loading