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

#5 add cli #12

Merged
merged 30 commits into from
May 24, 2024
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
84c67d2
#5 Added tests
XioZ May 3, 2024
c576329
Merge branch 'feature/#4-test-integration' into feature/#5-add-cli
XioZ May 5, 2024
a77b522
#5 Fixing tests
XioZ May 5, 2024
de71697
#5 Fixing tests
XioZ May 5, 2024
b3f3533
#5 Fixing tests
XioZ May 5, 2024
acffab5
#5 Fixing tests
XioZ May 5, 2024
55e202b
#5 Fixing tests
XioZ May 5, 2024
e4e37e9
#5 Fixing tests
XioZ May 5, 2024
d0512b9
#5 Fixed tests
XioZ May 5, 2024
be9038c
#6 Uses minows
XioZ May 5, 2024
229954e
#6 Allows port 0 for local testing
XioZ May 5, 2024
83d62d8
#5 Adds disk and memory storage
XioZ May 5, 2024
4e12b90
Merge branch 'feature/#6-automate-tests' into feature/#5-add-cli
XioZ May 5, 2024
9f1865f
#5 Adds disk and memory storage
XioZ May 5, 2024
6ec560a
#6 Uses minows
XioZ May 5, 2024
424b745
#5 Only 1 key entry per db/mino
XioZ May 5, 2024
bf0d366
#5 Uses disk store for key
XioZ May 5, 2024
d95bbc5
#5 Uses disk store for key
XioZ May 5, 2024
93a49a5
#5 Uses disk store for key
XioZ May 5, 2024
9fb2e37
#5 Uses disk store for key
XioZ May 5, 2024
908bc40
#5 Fixed lint
XioZ May 5, 2024
1facf91
#5 Makes public optional
XioZ May 5, 2024
21661da
#5 Makes public optional
XioZ May 5, 2024
bb4a2dc
#5 Send message to self
XioZ May 6, 2024
ae72a6b
Merge branch 'main' into feature/#5-add-cli
XioZ May 8, 2024
d45904a
#5 Refactored tests
XioZ May 8, 2024
62a1a76
Update mino/minows/controller_test.go
XioZ May 13, 2024
336bfa4
#5 Uses persistent storage only
XioZ May 13, 2024
75681ac
#5 Uses fake in-memory db for tests
XioZ May 13, 2024
2f7ab19
#5 Refactored tests
XioZ May 21, 2024
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
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ require (
github.com/russross/blackfriday/v2 v2.0.1 // indirect
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/stretchr/objx v0.5.0 // indirect
github.com/uber/jaeger-lib v2.4.0+incompatible // indirect
go.dedis.ch/fixbuf v1.0.3 // indirect
go.dedis.ch/protobuf v1.0.11 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -438,12 +438,16 @@ github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0b
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
Expand Down
3 changes: 3 additions & 0 deletions mino/minows/address_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,9 @@ func mustCreateAddress(t *testing.T, location, identity string) address {
}

func mustCreateMultiaddress(t *testing.T, address string) ma.Multiaddr {
if address == "" {
return nil
}
multiaddr, err := ma.NewMultiaddr(address)
require.NoError(t, err)
return multiaddr
Expand Down
88 changes: 88 additions & 0 deletions mino/minows/controller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package minows

import (
ma "github.com/multiformats/go-multiaddr"
"go.dedis.ch/dela/cli"
"go.dedis.ch/dela/cli/node"
"go.dedis.ch/dela/core/store/kv"
"go.dedis.ch/dela/mino/minows/key"
"golang.org/x/xerrors"
)

// controller
// - implements node.Initializer
type controller struct{}

// NewController creates a CLI app to start a Minows instance.
func NewController() node.Initializer {
return controller{}
}

const flagListen = "listen"
const flagPublic = "public"

func (c controller) SetCommands(builder node.Builder) {
builder.SetStartFlags(
cli.StringFlag{
Name: flagListen,
Usage: "set the address to listen on (default all interfaces, " +
"random port)",
Required: false,
Value: "/ip4/0.0.0.0/tcp/0",
},
cli.StringFlag{
Name: flagPublic,
Usage: "set the publicly reachable address (" +
"default listen address)",
Required: false,
Value: "",
},
)
}

func (c controller) OnStart(flags cli.Flags, inj node.Injector) error {
listen, err := ma.NewMultiaddr(flags.String(flagListen))
if err != nil {
return xerrors.Errorf("could not parse listen addr: %v", err)
}

var db kv.DB
err = inj.Resolve(&db)
if err != nil {
return xerrors.Errorf("could not resolve db: %v", err)
}
storage := key.NewStorage(db)
key, err := storage.LoadOrCreate()
if err != nil {
return xerrors.Errorf("could not load key: %v", err)
}

var public ma.Multiaddr
p := flags.String(flagPublic)
if p != "" {
public, err = ma.NewMultiaddr(p)
if err != nil {
return xerrors.Errorf("could not parse public addr: %v", err)
}
}

m, err := NewMinows(listen, public, key)
if err != nil {
return xerrors.Errorf("could not start mino: %v", err)
}
inj.Inject(m)
return nil
}

func (c controller) OnStop(inj node.Injector) error {
var m *minows
err := inj.Resolve(&m)
if err != nil {
return xerrors.Errorf("could not resolve mino: %v", err)
}
err = m.stop()
if err != nil {
return xerrors.Errorf("could not stop mino: %v", err)
}
return nil
}
156 changes: 156 additions & 0 deletions mino/minows/controller_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package minows

import (
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"go.dedis.ch/dela/cli/node"
"go.dedis.ch/dela/core/store/kv"
"os"
"path/filepath"
"testing"
"time"
)

func TestController_OnStart(t *testing.T) {
flags := new(mockFlags)
flags.On("String", "listen").Return("/ip4/0.0.0.0/tcp/8000/ws")
flags.On("String", "public").Return("/dns4/p2p-1.c4dt.dela.org/tcp/443/wss")
inj, clean := mustCreateInjector(t)
defer clean()
ctrl, stop := mustCreateController(t, inj)
defer stop()

err := ctrl.OnStart(flags, inj)
require.NoError(t, err)
var m *minows
err = inj.Resolve(&m)
require.NoError(t, err)
}

func TestController_OptionalPublic(t *testing.T) {
flags := new(mockFlags)
flags.On("String", "listen").Return("/ip4/0.0.0.0/tcp/8000/ws")
flags.On("String", "public").Return("")
inj, clean := mustCreateInjector(t)
defer clean()
ctrl, stop := mustCreateController(t, inj)
defer stop()

err := ctrl.OnStart(flags, inj)
require.NoError(t, err)
var m *minows
err = inj.Resolve(&m)
require.NoError(t, err)
}

func TestController_InvalidListen(t *testing.T) {
flags := new(mockFlags)
flags.On("String", "listen").Return("invalid")
flags.On("String", "public").Return("/dns4/p2p-1.c4dt.dela.org/tcp/443/wss")
inj, clean := mustCreateInjector(t)
defer clean()
ctrl, _ := mustCreateController(t, inj)

err := ctrl.OnStart(flags, inj)
require.Error(t, err)
}

func TestController_InvalidPublic(t *testing.T) {
flags := new(mockFlags)
flags.On("String", "listen").Return("/ip4/0.0.0.0/tcp/8000/ws")
flags.On("String", "public").Return("invalid")
inj, clean := mustCreateInjector(t)
defer clean()
ctrl, _ := mustCreateController(t, inj)

err := ctrl.OnStart(flags, inj)
require.Error(t, err)
}

func TestController_OnStop(t *testing.T) {
flags := new(mockFlags)
flags.On("String", "listen").Return("/ip4/0.0.0.0/tcp/8000/ws")
flags.On("String", "public").Return("/dns4/p2p-1.c4dt.dela.org/tcp/443/wss")
inj, clean := mustCreateInjector(t)
defer clean()
ctrl, _ := mustCreateController(t, inj)
err := ctrl.OnStart(flags, inj)
require.NoError(t, err)

err = ctrl.OnStop(inj)
require.NoError(t, err)
}

func TestController_MissingDependency(t *testing.T) {
flags := new(mockFlags)
flags.On("String", "listen").Return("/ip4/0.0.0.0/tcp/8000/ws")
flags.On("String", "public").Return("/dns4/p2p-1.c4dt.dela.org/tcp/443/wss")
inj, clean := mustCreateInjector(t)
defer clean()
ctrl, _ := mustCreateController(t, inj)
err := ctrl.OnStart(flags, inj)
require.NoError(t, err)

err = ctrl.OnStop(node.NewInjector())
require.Error(t, err)
}

func mustCreateInjector(t *testing.T) (node.Injector, func()) {
dir, err := os.MkdirTemp(os.TempDir(), "test")
require.NoError(t, err)
db, err := kv.New(filepath.Join(dir, "test.db"))
require.NoError(t, err)
inj := node.NewInjector()
inj.Inject(db)

clean := func() {
os.RemoveAll(dir)
}

return inj, clean
}

func mustCreateController(t *testing.T, inj node.Injector) (node.Initializer, func()) {

XioZ marked this conversation as resolved.
Show resolved Hide resolved
ctrl := NewController()
stop := func() {
require.NoError(t, ctrl.OnStop(inj))
}
return ctrl, stop
}

// mockFlags
// - implements cli.Flags
type mockFlags struct {
mock.Mock
}

func (m *mockFlags) String(name string) string {
args := m.Called(name)
return args.String(0)
}

func (m *mockFlags) StringSlice(name string) []string {
args := m.Called(name)
return args.Get(0).([]string)
}

func (m *mockFlags) Duration(name string) time.Duration {
args := m.Called(name)
return args.Get(0).(time.Duration)
}

func (m *mockFlags) Path(name string) string {
args := m.Called(name)
return args.String(0)
}

func (m *mockFlags) Int(name string) int {
args := m.Called(name)
return args.Int(0)
}

func (m *mockFlags) Bool(name string) bool {
args := m.Called(name)
return args.Bool(0)
}
64 changes: 64 additions & 0 deletions mino/minows/key/disk.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package key

import (
"crypto/rand"
"github.com/libp2p/go-libp2p/core/crypto"
"go.dedis.ch/dela/core/store/kv"
"golang.org/x/xerrors"
)

// diskStorage provides persistent storage for private keys on disk.
type diskStorage struct {
ineiti marked this conversation as resolved.
Show resolved Hide resolved
bucket []byte
db kv.DB
}

func newDiskStore(db kv.DB) *diskStorage {
ineiti marked this conversation as resolved.
Show resolved Hide resolved
return &diskStorage{
bucket: []byte("keys"),
db: db,
}
}

func (s *diskStorage) LoadOrCreate() (crypto.PrivKey, error) {
key := []byte("private_key")
var buffer []byte
err := s.db.Update(func(tx kv.WritableTx) error {
XioZ marked this conversation as resolved.
Show resolved Hide resolved
bucket, err := tx.GetBucketOrCreate(s.bucket)
if err != nil {
return xerrors.Errorf("could not get bucket: %v", err)
}
stored := bucket.Get(key)
if stored != nil {
buffer = make([]byte, len(stored))
copy(buffer, stored)
return nil
}

private, _, err := crypto.GenerateEd25519Key(rand.Reader)
if err != nil {
return xerrors.Errorf("could not generate key: %v", err)
}
generated, err := crypto.MarshalPrivateKey(private)
if err != nil {
return xerrors.Errorf("could not marshal key: %v", err)
}

err = bucket.Set(key, generated)
if err != nil {
return xerrors.Errorf("could not store key: %v", err)
}
buffer = make([]byte, len(generated))
copy(buffer, generated)
return nil
ineiti marked this conversation as resolved.
Show resolved Hide resolved
})
if err != nil {
return nil, xerrors.Errorf("could not update db: %v", err)
}

private, err := crypto.UnmarshalPrivateKey(buffer)
if err != nil {
return nil, xerrors.Errorf("could not unmarshal key: %v", err)
}
return private, nil
}
39 changes: 39 additions & 0 deletions mino/minows/key/memory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package key

import (
"github.com/libp2p/go-libp2p/core/crypto"
"go.dedis.ch/dela/core/store/kv"
"golang.org/x/xerrors"
"sync"
)

// cachedStorage offers a layer of in-memory caching on top of a diskStorage.
type cachedStorage struct {
ineiti marked this conversation as resolved.
Show resolved Hide resolved
sync.Mutex
*diskStorage

key crypto.PrivKey
}

func newMemoryStore(db kv.DB) *cachedStorage {
return &cachedStorage{
diskStorage: newDiskStore(db),
}
}

func (s *cachedStorage) LoadOrCreate() (crypto.PrivKey, error) {
s.Lock()
defer s.Unlock()

if s.key != nil {
return s.key, nil
}

key, err := s.diskStorage.LoadOrCreate()
if err != nil {
return nil, xerrors.Errorf("could not load from disk: %v", err)
}

s.key = key
return key, nil
}
Loading
Loading