-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
1,390 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# Mino | ||
|
||
Minimalist Network Overlay |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
Oops, something went wrong.