Skip to content

Commit

Permalink
Merge f20f942 into 34033b7
Browse files Browse the repository at this point in the history
  • Loading branch information
nkcr committed Mar 6, 2020
2 parents 34033b7 + f20f942 commit 7a53950
Show file tree
Hide file tree
Showing 15 changed files with 1,390 additions and 13 deletions.
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.

[![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/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,
}
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)

}
61 changes: 61 additions & 0 deletions mino/minogrpc/overlay.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package minogrpc

import (
context "context"

"github.com/golang/protobuf/ptypes"
"go.dedis.ch/fabric/encoding"
"go.dedis.ch/fabric/mino"
"golang.org/x/xerrors"
"google.golang.org/grpc/metadata"
)

// gRPC service for the overlay. The handler map points to the one in
// Server.Handlers, which is updated each time the makeRPC function is called.
type overlayService struct {
handlers map[string]mino.Handler
}

// Call is the implementation of the overlay.Call proto definition
func (o overlayService) Call(ctx context.Context, msg *CallMsg) (*CallResp, error) {
// We fetch the uri that identifies the handler in the handlers map with the
// grpc metadata api. Using context.Value won't work.
headers, ok := metadata.FromIncomingContext(ctx)
if !ok {
return nil, xerrors.Errorf("header not found in provided context")
}

apiURI, ok := headers["apiuri"]
if !ok {
return nil, xerrors.Errorf("apiuri not found in context header: ", apiURI)
}
if len(apiURI) != 1 {
return nil, xerrors.Errorf("unexpected number of elements in apiuri "+
"header. Expected 1, found %d", len(apiURI))
}

handler, ok := o.handlers[apiURI[0]]
if !ok {
return nil, xerrors.Errorf("didn't find the '%s' handler in the map "+
"of handlers, did you register it?", apiURI)
}

var dynamicAny ptypes.DynamicAny
err := ptypes.UnmarshalAny(msg.Message, &dynamicAny)
if err != nil {
return nil, encoding.NewAnyDecodingError(msg.Message, err)
}

result, err := handler.Process(dynamicAny.Message)
if err != nil {
return nil, xerrors.Errorf("failed to call the Process function from "+
"the handler using the provided message: %v", err)
}

anyResult, err := ptypes.MarshalAny(result)
if err != nil {
return nil, encoding.NewAnyEncodingError(result, err)
}

return &CallResp{Message: anyResult}, nil
}
Loading

0 comments on commit 7a53950

Please sign in to comment.