From ae2fb13f53ce8d22ebdcfa61d1e62471a09c48ce Mon Sep 17 00:00:00 2001 From: Dean Eigenmann Date: Wed, 7 Aug 2019 14:31:32 +0200 Subject: [PATCH] feature/payloads (#7) * started working on payload concept * added crypto * moved into new package, added feed * added nicer feed stuff * minor readme for now * subscribe implemented badly * doc * doc block * cleaned up a little * making a mutex * mock, empty test * test wrapper * started playing around * updated mock * formatted * updated * updated interface * updated * updated * updated mock, rewrote test stuff * todos * added tests * reuse * dont need var --- Makefile | 10 + README.md | 17 + client/client.go | 73 +-- client/client_test.go | 107 +++++ client/internal/node.go | 2 +- client/internal/node_mock.go | 78 ++++ client/internal/store_mock.go | 79 ++++ crypto/crypto.go | 28 ++ event/feed.go | 20 + event/payload.go | 11 + go.mod | 7 +- go.sum | 20 +- vendor/github.com/golang/mock/AUTHORS | 12 + vendor/github.com/golang/mock/CONTRIBUTORS | 37 ++ vendor/github.com/golang/mock/LICENSE | 202 +++++++++ vendor/github.com/golang/mock/gomock/call.go | 420 ++++++++++++++++++ .../github.com/golang/mock/gomock/callset.go | 108 +++++ .../golang/mock/gomock/controller.go | 264 +++++++++++ .../github.com/golang/mock/gomock/matchers.go | 141 ++++++ vendor/github.com/vacp2p/mvds/node/node.go | 371 ---------------- .../github.com/vacp2p/mvds/node/payloads.go | 94 ---- .../vacp2p/mvds/protobuf/payload.go | 7 + .../vacp2p/mvds/protobuf/sync.pb.go | 44 +- .../vacp2p/mvds/protobuf/sync.proto | 14 +- vendor/github.com/vacp2p/mvds/state/state.go | 12 +- .../vacp2p/mvds/state/state_memory.go | 71 ++- .../vacp2p/mvds/store/messagestore.go | 6 +- .../vacp2p/mvds/store/messagestore_dummy.go | 15 +- .../mvds/transport/channel_transport.go | 54 --- .../vacp2p/mvds/transport/transport.go | 19 - vendor/modules.txt | 6 +- 31 files changed, 1693 insertions(+), 656 deletions(-) create mode 100644 client/client_test.go create mode 100644 client/internal/node_mock.go create mode 100644 client/internal/store_mock.go create mode 100644 crypto/crypto.go create mode 100644 event/feed.go create mode 100644 event/payload.go create mode 100644 vendor/github.com/golang/mock/AUTHORS create mode 100644 vendor/github.com/golang/mock/CONTRIBUTORS create mode 100644 vendor/github.com/golang/mock/LICENSE create mode 100644 vendor/github.com/golang/mock/gomock/call.go create mode 100644 vendor/github.com/golang/mock/gomock/callset.go create mode 100644 vendor/github.com/golang/mock/gomock/controller.go create mode 100644 vendor/github.com/golang/mock/gomock/matchers.go delete mode 100644 vendor/github.com/vacp2p/mvds/node/node.go delete mode 100644 vendor/github.com/vacp2p/mvds/node/payloads.go create mode 100644 vendor/github.com/vacp2p/mvds/protobuf/payload.go delete mode 100644 vendor/github.com/vacp2p/mvds/transport/channel_transport.go delete mode 100644 vendor/github.com/vacp2p/mvds/transport/transport.go diff --git a/Makefile b/Makefile index b889e4e..cac08a9 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,16 @@ protobuf: protoc --go_out=. ./protobuf/*.proto .PHONY: protobuf +mock-install: + go get -u github.com/golang/mock/mockgen + go get -u github.com/golang/mock +.PHONY: mock-install + +mock: + mockgen -package=internal -destination=client/internal/node_mock.go -source=client/internal/node.go + mockgen -package=internal -destination=client/internal/store_mock.go -source=vendor/github.com/vacp2p/mvds/store/messagestore.go +.PHONY: mock + lint: golangci-lint run -v .PHONY: lint diff --git a/README.md b/README.md index 4faa962..2cecf15 100644 --- a/README.md +++ b/README.md @@ -13,3 +13,20 @@ https://camo.githubusercontent.com/915b7be44ada53c290eb157634330494ebe3e30a/6874 > **Da**ta **Sy**nc Client This repository implements a basic [data sync client](#TODO) that operates on top of [mvds](https://github.com/vacp2p/mvds). + +## Usage + +Listening to messages + +```go + +postchan := make(chan event.Payload) + +client.Feed(protobuf.Message_POST).Subscribe(postchan) + +for { + post := <-postchan + fmt.Printf("%+v\n", post) +} + +``` diff --git a/client/client.go b/client/client.go index c1b91ae..19d4235 100644 --- a/client/client.go +++ b/client/client.go @@ -4,11 +4,14 @@ package client import ( "crypto/ecdsa" "log" + "sync" - "github.com/ethereum/go-ethereum/crypto" + ethcrypto "github.com/ethereum/go-ethereum/crypto" "github.com/golang/protobuf/proto" "github.com/pkg/errors" "github.com/vacp2p/dasy/client/internal" + "github.com/vacp2p/dasy/crypto" + "github.com/vacp2p/dasy/event" "github.com/vacp2p/dasy/protobuf" mvdsproto "github.com/vacp2p/mvds/protobuf" "github.com/vacp2p/mvds/state" @@ -23,11 +26,16 @@ type Peer state.PeerID // Client is the actual daisy client. type Client struct { + sync.Mutex + + id Peer // @todo think of turning dataSyncNode ID into a func + node internal.DataSyncNode - store store.MessageStore // @todo we probably need a different message store, not sure tho + store store.MessageStore identity *ecdsa.PrivateKey + feeds map[protobuf.Message_MessageType]*event.Feed lastMessages map[Chat]state.MessageID // @todo maybe make type } @@ -38,12 +46,12 @@ func (c *Client) Invite(chat Chat, peer Peer) { // Join joins a chat. func (c *Client) Join(chat Chat) (state.MessageID, error) { - return c.send(chat, protobuf.Message_JOIN, c.node.ID[:]) + return c.send(chat, protobuf.Message_JOIN, c.id[:]) } // Leave leaves a chat. func (c *Client) Leave(chat Chat) (state.MessageID, error) { - return c.send(chat, protobuf.Message_LEAVE, c.node.ID[:]) + return c.send(chat, protobuf.Message_LEAVE, c.id[:]) } // Kick kicks peer from a chat. @@ -62,17 +70,31 @@ func (c *Client) Post(chat Chat, body []byte) (state.MessageID, error) { return c.send(chat, protobuf.Message_POST, body) } +// Feed is a subscription feed for the specified message type. +func (c *Client) Feed(msg protobuf.Message_MessageType) *event.Feed { + c.Lock() + defer c.Unlock() + + if c.feeds[msg] == nil { + c.feeds[msg] = new(event.Feed) + } + + return c.feeds[msg] +} + // Listen listens for newly received messages and handles them appropriately. func (c *Client) Listen() { - sub := make(chan mvdsproto.Message) - c.node.Subscribe(sub) + sub := c.node.Subscribe() for { - go c.onReceive(<- sub) + go c.onReceive(<-sub) } } func (c *Client) send(chat Chat, t protobuf.Message_MessageType, body []byte) (state.MessageID, error) { + c.Lock() + defer c.Unlock() + lastMessage := c.lastMessages[chat] msg := &protobuf.Message{ MessageType: protobuf.Message_MessageType(t), @@ -80,9 +102,9 @@ func (c *Client) send(chat Chat, t protobuf.Message_MessageType, body []byte) (s PreviousMessage: lastMessage[:], } - err := c.sign(msg) + err := crypto.Sign(c.identity, msg) if err != nil { - return errors.Wrap(err, "failed to sign message") + return state.MessageID{}, errors.Wrap(err, "failed to sign message") } buf, err := proto.Marshal(msg) @@ -110,16 +132,21 @@ func (c *Client) onReceive(message mvdsproto.Message) { return } - pubkey, err := crypto.SigToPub(msg.ID(), msg.Signature) + pubkey, err := ethcrypto.SigToPub(msg.ID(), msg.Signature) if err != nil { log.Printf("error while recovering pubkey: %s", err.Error()) // @todo return } - // @todo probably store the sender somewhere? + payload := event.Payload{ + Body: msg.Body, // @todo this might need to be unmarshalled depending on the message type like invite? + Signature: msg.Signature, // @todo recover from signature + Sender: crypto.PublicKeyToPeerID(*pubkey), + Timestamp: message.Timestamp, + } - // @todo pump messages to subscriber channels + go c.Feed(msg.MessageType).Send(payload) if len(msg.PreviousMessage) == 0 { return @@ -132,25 +159,17 @@ func (c *Client) onReceive(message mvdsproto.Message) { } func (c *Client) handlePreviousMessage(group state.GroupID, previousMessage state.MessageID) { - if c.store.Has(previousMessage) { + ok, err := c.store.Has(previousMessage) + if ok { return } - err := c.node.RequestMessage(group, previousMessage) + if err != nil { + log.Printf("error while checking if message exists: %s", err.Error()) + } + + err = c.node.RequestMessage(group, previousMessage) if err != nil { log.Printf("error while requesting message: %s", err.Error()) } } - -// sign signs generates a signature of the message and adds it to the message. -func (c *Client) sign(m *protobuf.Message) error { - hash := m.ID() - - sig, err := crypto.Sign(hash[:], c.identity) - if err != nil { - return err - } - - m.Signature = sig - return nil -} diff --git a/client/client_test.go b/client/client_test.go new file mode 100644 index 0000000..18185a4 --- /dev/null +++ b/client/client_test.go @@ -0,0 +1,107 @@ +package client + +import ( + "crypto/ecdsa" + "crypto/rand" + "io/ioutil" + "log" + "reflect" + + "testing" + + "github.com/ethereum/go-ethereum/crypto/secp256k1" + "github.com/golang/mock/gomock" + "github.com/golang/protobuf/proto" + "github.com/vacp2p/dasy/client/internal" + "github.com/vacp2p/dasy/crypto" + "github.com/vacp2p/dasy/event" + "github.com/vacp2p/dasy/protobuf" + mvdsproto "github.com/vacp2p/mvds/protobuf" +) + +func TestMain(m *testing.M) { + log.SetOutput(ioutil.Discard) + m.Run() +} + +// @todo think about turning feed into an interface so we can mock it and ensure its never called when sigs fail + +func TestClient_Listen_MessageSentToFeed(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + node := internal.NewMockDataSyncNode(ctrl) + + client := Client{ + node: node, + feeds: make(map[protobuf.Message_MessageType]*event.Feed), + } + + sub := make(chan mvdsproto.Message) + node.EXPECT().Subscribe().Return(sub) + + go client.Listen() + + msg := createMessage() + + ok := make(chan event.Payload) + client.Feed(msg.MessageType).Subscribe(ok) + + val, _ := proto.Marshal(msg) + + sub<-mvdsproto.Message{ + Body: val, + } + + received := <-ok + if !reflect.DeepEqual(received.Body, msg.Body) { + t.Error("expected message did not equal received") + } +} + +func TestClient_Listen_RequestsMissingParent(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + node := internal.NewMockDataSyncNode(ctrl) + store := internal.NewMockMessageStore(ctrl) + + client := Client{ + node: node, + store: store, + feeds: make(map[protobuf.Message_MessageType]*event.Feed), + } + + sub := make(chan mvdsproto.Message) + node.EXPECT().Subscribe().Return(sub) + + store.EXPECT().Has(gomock.Any()).Return(false, nil) + node.EXPECT().RequestMessage(gomock.Any(), gomock.Any()).Return(nil) + + go client.Listen() + + msg := createMessage() + msg.PreviousMessage = []byte("parent") + + ok := make(chan event.Payload) + client.Feed(msg.MessageType).Subscribe(ok) + + val, _ := proto.Marshal(msg) + + sub<-mvdsproto.Message{ + Body: val, + } + + <-ok +} + +func createMessage() *protobuf.Message { + msg := &protobuf.Message{ + MessageType: protobuf.Message_POST, + Body: []byte("hi"), + } + + identity, _ := ecdsa.GenerateKey(secp256k1.S256(), rand.Reader) + _ = crypto.Sign(identity, msg) + return msg +} diff --git a/client/internal/node.go b/client/internal/node.go index 2f75c96..d65dc08 100644 --- a/client/internal/node.go +++ b/client/internal/node.go @@ -7,6 +7,6 @@ import ( type DataSyncNode interface { AppendMessage(groupID state.GroupID, data []byte) (state.MessageID, error) - Subscribe(sub chan <-protobuf.Message) + Subscribe() chan protobuf.Message RequestMessage(group state.GroupID, id state.MessageID) error } diff --git a/client/internal/node_mock.go b/client/internal/node_mock.go new file mode 100644 index 0000000..c8f6200 --- /dev/null +++ b/client/internal/node_mock.go @@ -0,0 +1,78 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: client/internal/node.go + +// Package internal is a generated GoMock package. +package internal + +import ( + gomock "github.com/golang/mock/gomock" + protobuf "github.com/vacp2p/mvds/protobuf" + state "github.com/vacp2p/mvds/state" + reflect "reflect" +) + +// MockDataSyncNode is a mock of DataSyncNode interface +type MockDataSyncNode struct { + ctrl *gomock.Controller + recorder *MockDataSyncNodeMockRecorder +} + +// MockDataSyncNodeMockRecorder is the mock recorder for MockDataSyncNode +type MockDataSyncNodeMockRecorder struct { + mock *MockDataSyncNode +} + +// NewMockDataSyncNode creates a new mock instance +func NewMockDataSyncNode(ctrl *gomock.Controller) *MockDataSyncNode { + mock := &MockDataSyncNode{ctrl: ctrl} + mock.recorder = &MockDataSyncNodeMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockDataSyncNode) EXPECT() *MockDataSyncNodeMockRecorder { + return m.recorder +} + +// AppendMessage mocks base method +func (m *MockDataSyncNode) AppendMessage(groupID state.GroupID, data []byte) (state.MessageID, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AppendMessage", groupID, data) + ret0, _ := ret[0].(state.MessageID) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AppendMessage indicates an expected call of AppendMessage +func (mr *MockDataSyncNodeMockRecorder) AppendMessage(groupID, data interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppendMessage", reflect.TypeOf((*MockDataSyncNode)(nil).AppendMessage), groupID, data) +} + +// Subscribe mocks base method +func (m *MockDataSyncNode) Subscribe() chan protobuf.Message { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Subscribe") + ret0, _ := ret[0].(chan protobuf.Message) + return ret0 +} + +// Subscribe indicates an expected call of Subscribe +func (mr *MockDataSyncNodeMockRecorder) Subscribe() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Subscribe", reflect.TypeOf((*MockDataSyncNode)(nil).Subscribe)) +} + +// RequestMessage mocks base method +func (m *MockDataSyncNode) RequestMessage(group state.GroupID, id state.MessageID) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RequestMessage", group, id) + ret0, _ := ret[0].(error) + return ret0 +} + +// RequestMessage indicates an expected call of RequestMessage +func (mr *MockDataSyncNodeMockRecorder) RequestMessage(group, id interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RequestMessage", reflect.TypeOf((*MockDataSyncNode)(nil).RequestMessage), group, id) +} diff --git a/client/internal/store_mock.go b/client/internal/store_mock.go new file mode 100644 index 0000000..e413474 --- /dev/null +++ b/client/internal/store_mock.go @@ -0,0 +1,79 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: vendor/github.com/vacp2p/mvds/store/messagestore.go + +// Package internal is a generated GoMock package. +package internal + +import ( + gomock "github.com/golang/mock/gomock" + protobuf "github.com/vacp2p/mvds/protobuf" + state "github.com/vacp2p/mvds/state" + reflect "reflect" +) + +// MockMessageStore is a mock of MessageStore interface +type MockMessageStore struct { + ctrl *gomock.Controller + recorder *MockMessageStoreMockRecorder +} + +// MockMessageStoreMockRecorder is the mock recorder for MockMessageStore +type MockMessageStoreMockRecorder struct { + mock *MockMessageStore +} + +// NewMockMessageStore creates a new mock instance +func NewMockMessageStore(ctrl *gomock.Controller) *MockMessageStore { + mock := &MockMessageStore{ctrl: ctrl} + mock.recorder = &MockMessageStoreMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockMessageStore) EXPECT() *MockMessageStoreMockRecorder { + return m.recorder +} + +// Has mocks base method +func (m *MockMessageStore) Has(id state.MessageID) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Has", id) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Has indicates an expected call of Has +func (mr *MockMessageStoreMockRecorder) Has(id interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Has", reflect.TypeOf((*MockMessageStore)(nil).Has), id) +} + +// Get mocks base method +func (m *MockMessageStore) Get(id state.MessageID) (*protobuf.Message, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", id) + ret0, _ := ret[0].(*protobuf.Message) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get +func (mr *MockMessageStoreMockRecorder) Get(id interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockMessageStore)(nil).Get), id) +} + +// Add mocks base method +func (m *MockMessageStore) Add(message *protobuf.Message) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Add", message) + ret0, _ := ret[0].(error) + return ret0 +} + +// Add indicates an expected call of Add +func (mr *MockMessageStoreMockRecorder) Add(message interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Add", reflect.TypeOf((*MockMessageStore)(nil).Add), message) +} diff --git a/crypto/crypto.go b/crypto/crypto.go new file mode 100644 index 0000000..df9707a --- /dev/null +++ b/crypto/crypto.go @@ -0,0 +1,28 @@ +package crypto + +import ( + "crypto/ecdsa" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/vacp2p/dasy/protobuf" + "github.com/vacp2p/mvds/state" +) + +func PublicKeyToPeerID(k ecdsa.PublicKey) state.PeerID { + var p state.PeerID + copy(p[:], crypto.FromECDSAPub(&k)) + return p +} + +// Sign signs generates a signature of the message and adds it to the message. +func Sign(identity *ecdsa.PrivateKey, m *protobuf.Message) error { + hash := m.ID() + + sig, err := crypto.Sign(hash[:], identity) + if err != nil { + return err + } + + m.Signature = sig + return nil +} diff --git a/event/feed.go b/event/feed.go new file mode 100644 index 0000000..b5ebd6b --- /dev/null +++ b/event/feed.go @@ -0,0 +1,20 @@ +package event + +type Subscription chan <-Payload + +type Feed struct { + subscribers []Subscription +} + +// Subscribe adds a channel to the feed. +func (f *Feed) Subscribe(channel Subscription) { // @todo think about returning a subscription like prysm + f.subscribers = append(f.subscribers, channel) +} + +// Send sends a payload to all the subscribers for the specific feed. +func (f *Feed) Send(value Payload) { + // @todo is this good enough for now? + for _, sub := range f.subscribers { + sub <- value + } +} diff --git a/event/payload.go b/event/payload.go new file mode 100644 index 0000000..8a2388d --- /dev/null +++ b/event/payload.go @@ -0,0 +1,11 @@ +package event + +import "github.com/vacp2p/mvds/state" + +// Payload represents a `dasy` packet. +type Payload struct { + Body interface{} + Signature []byte + Sender state.PeerID + Timestamp int64 +} diff --git a/go.mod b/go.mod index a90ac87..0d42a47 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,13 @@ module github.com/vacp2p/dasy go 1.12 require ( + github.com/btcsuite/btcd v0.0.0-20190427004231-96897255fd17 // indirect github.com/ethereum/go-ethereum v1.8.27 + github.com/golang/mock v1.3.1 github.com/golang/protobuf v1.3.2 + github.com/kr/pretty v0.1.0 // indirect github.com/pkg/errors v0.8.1 - github.com/vacp2p/mvds v0.0.0-20190718214327-b8e366e4e634 + github.com/vacp2p/mvds v0.0.20 + golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f // indirect + gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect ) diff --git a/go.sum b/go.sum index 12ac30a..6e5912b 100644 --- a/go.sum +++ b/go.sum @@ -8,28 +8,38 @@ github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVa github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495 h1:6IyqGr3fnd0tM3YxipK27TUskaOVUjU2nG45yzwcQKY= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ethereum/go-ethereum v1.8.27 h1:d+gkiLaBDk5fn3Pe/xNVaMrB/ozI+AUB2IlVBp29IrY= github.com/ethereum/go-ethereum v1.8.27/go.mod h1:PwpWDrCLZrV+tfrhqqF6kPknbISMHaJv9Ln3kPCZLwY= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/vacp2p/mvds v0.0.0-20190718214327-b8e366e4e634 h1:89YL0cZNx2wUJoG5fwqq9bAlkKilzkOUUdxv0I5ZBDM= -github.com/vacp2p/mvds v0.0.0-20190718214327-b8e366e4e634/go.mod h1:ZGHqIL3NyMyrtF6Gf+QvsMBPfa80E3NVgEavoea1u/k= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v0.0.0-20190716104307-221dbe5ed46703ee255b1da0dec05086f5035f62/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/vacp2p/mvds v0.0.20 h1:qU+i4xlFkCBMYgCIt28IteZwikqh99+m57JR5dWsOVQ= +github.com/vacp2p/mvds v0.0.20/go.mod h1:MxvN/VFp5zkjhUfJdBft+NrsVDwMsvueDIP2x0h6Tx8= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f h1:R423Cnkcp5JABoeemiGEPlt9tHXFfw5kvc0yqlxRPWo= @@ -44,9 +54,11 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/tools v0.0.0-20190525145741-7be61e1b0e51/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/golang/mock/AUTHORS b/vendor/github.com/golang/mock/AUTHORS new file mode 100644 index 0000000..660b8cc --- /dev/null +++ b/vendor/github.com/golang/mock/AUTHORS @@ -0,0 +1,12 @@ +# This is the official list of GoMock authors for copyright purposes. +# This file is distinct from the CONTRIBUTORS files. +# See the latter for an explanation. + +# Names should be added to this file as +# Name or Organization +# The email address is not required for organizations. + +# Please keep the list sorted. + +Alex Reece +Google Inc. diff --git a/vendor/github.com/golang/mock/CONTRIBUTORS b/vendor/github.com/golang/mock/CONTRIBUTORS new file mode 100644 index 0000000..def849c --- /dev/null +++ b/vendor/github.com/golang/mock/CONTRIBUTORS @@ -0,0 +1,37 @@ +# This is the official list of people who can contribute (and typically +# have contributed) code to the gomock repository. +# The AUTHORS file lists the copyright holders; this file +# lists people. For example, Google employees are listed here +# but not in AUTHORS, because Google holds the copyright. +# +# The submission process automatically checks to make sure +# that people submitting code are listed in this file (by email address). +# +# Names should be added to this file only after verifying that +# the individual or the individual's organization has agreed to +# the appropriate Contributor License Agreement, found here: +# +# http://code.google.com/legal/individual-cla-v1.0.html +# http://code.google.com/legal/corporate-cla-v1.0.html +# +# The agreement for individuals can be filled out on the web. +# +# When adding J Random Contributor's name to this file, +# either J's name or J's organization's name should be +# added to the AUTHORS file, depending on whether the +# individual or corporate CLA was used. + +# Names should be added to this file like so: +# Name +# +# An entry with two email addresses specifies that the +# first address should be used in the submit logs and +# that the second address should be recognized as the +# same person when interacting with Rietveld. + +# Please keep the list sorted. + +Aaron Jacobs +Alex Reece +David Symonds +Ryan Barrett diff --git a/vendor/github.com/golang/mock/LICENSE b/vendor/github.com/golang/mock/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/vendor/github.com/golang/mock/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/golang/mock/gomock/call.go b/vendor/github.com/golang/mock/gomock/call.go new file mode 100644 index 0000000..3d54d9f --- /dev/null +++ b/vendor/github.com/golang/mock/gomock/call.go @@ -0,0 +1,420 @@ +// Copyright 2010 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package gomock + +import ( + "fmt" + "reflect" + "strconv" + "strings" +) + +// Call represents an expected call to a mock. +type Call struct { + t TestHelper // for triggering test failures on invalid call setup + + receiver interface{} // the receiver of the method call + method string // the name of the method + methodType reflect.Type // the type of the method + args []Matcher // the args + origin string // file and line number of call setup + + preReqs []*Call // prerequisite calls + + // Expectations + minCalls, maxCalls int + + numCalls int // actual number made + + // actions are called when this Call is called. Each action gets the args and + // can set the return values by returning a non-nil slice. Actions run in the + // order they are created. + actions []func([]interface{}) []interface{} +} + +// newCall creates a *Call. It requires the method type in order to support +// unexported methods. +func newCall(t TestHelper, receiver interface{}, method string, methodType reflect.Type, args ...interface{}) *Call { + t.Helper() + + // TODO: check arity, types. + margs := make([]Matcher, len(args)) + for i, arg := range args { + if m, ok := arg.(Matcher); ok { + margs[i] = m + } else if arg == nil { + // Handle nil specially so that passing a nil interface value + // will match the typed nils of concrete args. + margs[i] = Nil() + } else { + margs[i] = Eq(arg) + } + } + + origin := callerInfo(3) + actions := []func([]interface{}) []interface{}{func([]interface{}) []interface{} { + // Synthesize the zero value for each of the return args' types. + rets := make([]interface{}, methodType.NumOut()) + for i := 0; i < methodType.NumOut(); i++ { + rets[i] = reflect.Zero(methodType.Out(i)).Interface() + } + return rets + }} + return &Call{t: t, receiver: receiver, method: method, methodType: methodType, + args: margs, origin: origin, minCalls: 1, maxCalls: 1, actions: actions} +} + +// AnyTimes allows the expectation to be called 0 or more times +func (c *Call) AnyTimes() *Call { + c.minCalls, c.maxCalls = 0, 1e8 // close enough to infinity + return c +} + +// MinTimes requires the call to occur at least n times. If AnyTimes or MaxTimes have not been called, MinTimes also +// sets the maximum number of calls to infinity. +func (c *Call) MinTimes(n int) *Call { + c.minCalls = n + if c.maxCalls == 1 { + c.maxCalls = 1e8 + } + return c +} + +// MaxTimes limits the number of calls to n times. If AnyTimes or MinTimes have not been called, MaxTimes also +// sets the minimum number of calls to 0. +func (c *Call) MaxTimes(n int) *Call { + c.maxCalls = n + if c.minCalls == 1 { + c.minCalls = 0 + } + return c +} + +// DoAndReturn declares the action to run when the call is matched. +// The return values from this function are returned by the mocked function. +// It takes an interface{} argument to support n-arity functions. +func (c *Call) DoAndReturn(f interface{}) *Call { + // TODO: Check arity and types here, rather than dying badly elsewhere. + v := reflect.ValueOf(f) + + c.addAction(func(args []interface{}) []interface{} { + vargs := make([]reflect.Value, len(args)) + ft := v.Type() + for i := 0; i < len(args); i++ { + if args[i] != nil { + vargs[i] = reflect.ValueOf(args[i]) + } else { + // Use the zero value for the arg. + vargs[i] = reflect.Zero(ft.In(i)) + } + } + vrets := v.Call(vargs) + rets := make([]interface{}, len(vrets)) + for i, ret := range vrets { + rets[i] = ret.Interface() + } + return rets + }) + return c +} + +// Do declares the action to run when the call is matched. The function's +// return values are ignored to retain backward compatibility. To use the +// return values call DoAndReturn. +// It takes an interface{} argument to support n-arity functions. +func (c *Call) Do(f interface{}) *Call { + // TODO: Check arity and types here, rather than dying badly elsewhere. + v := reflect.ValueOf(f) + + c.addAction(func(args []interface{}) []interface{} { + vargs := make([]reflect.Value, len(args)) + ft := v.Type() + for i := 0; i < len(args); i++ { + if args[i] != nil { + vargs[i] = reflect.ValueOf(args[i]) + } else { + // Use the zero value for the arg. + vargs[i] = reflect.Zero(ft.In(i)) + } + } + v.Call(vargs) + return nil + }) + return c +} + +// Return declares the values to be returned by the mocked function call. +func (c *Call) Return(rets ...interface{}) *Call { + c.t.Helper() + + mt := c.methodType + if len(rets) != mt.NumOut() { + c.t.Fatalf("wrong number of arguments to Return for %T.%v: got %d, want %d [%s]", + c.receiver, c.method, len(rets), mt.NumOut(), c.origin) + } + for i, ret := range rets { + if got, want := reflect.TypeOf(ret), mt.Out(i); got == want { + // Identical types; nothing to do. + } else if got == nil { + // Nil needs special handling. + switch want.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: + // ok + default: + c.t.Fatalf("argument %d to Return for %T.%v is nil, but %v is not nillable [%s]", + i, c.receiver, c.method, want, c.origin) + } + } else if got.AssignableTo(want) { + // Assignable type relation. Make the assignment now so that the generated code + // can return the values with a type assertion. + v := reflect.New(want).Elem() + v.Set(reflect.ValueOf(ret)) + rets[i] = v.Interface() + } else { + c.t.Fatalf("wrong type of argument %d to Return for %T.%v: %v is not assignable to %v [%s]", + i, c.receiver, c.method, got, want, c.origin) + } + } + + c.addAction(func([]interface{}) []interface{} { + return rets + }) + + return c +} + +// Times declares the exact number of times a function call is expected to be executed. +func (c *Call) Times(n int) *Call { + c.minCalls, c.maxCalls = n, n + return c +} + +// SetArg declares an action that will set the nth argument's value, +// indirected through a pointer. Or, in the case of a slice, SetArg +// will copy value's elements into the nth argument. +func (c *Call) SetArg(n int, value interface{}) *Call { + c.t.Helper() + + mt := c.methodType + // TODO: This will break on variadic methods. + // We will need to check those at invocation time. + if n < 0 || n >= mt.NumIn() { + c.t.Fatalf("SetArg(%d, ...) called for a method with %d args [%s]", + n, mt.NumIn(), c.origin) + } + // Permit setting argument through an interface. + // In the interface case, we don't (nay, can't) check the type here. + at := mt.In(n) + switch at.Kind() { + case reflect.Ptr: + dt := at.Elem() + if vt := reflect.TypeOf(value); !vt.AssignableTo(dt) { + c.t.Fatalf("SetArg(%d, ...) argument is a %v, not assignable to %v [%s]", + n, vt, dt, c.origin) + } + case reflect.Interface: + // nothing to do + case reflect.Slice: + // nothing to do + default: + c.t.Fatalf("SetArg(%d, ...) referring to argument of non-pointer non-interface non-slice type %v [%s]", + n, at, c.origin) + } + + c.addAction(func(args []interface{}) []interface{} { + v := reflect.ValueOf(value) + switch reflect.TypeOf(args[n]).Kind() { + case reflect.Slice: + setSlice(args[n], v) + default: + reflect.ValueOf(args[n]).Elem().Set(v) + } + return nil + }) + return c +} + +// isPreReq returns true if other is a direct or indirect prerequisite to c. +func (c *Call) isPreReq(other *Call) bool { + for _, preReq := range c.preReqs { + if other == preReq || preReq.isPreReq(other) { + return true + } + } + return false +} + +// After declares that the call may only match after preReq has been exhausted. +func (c *Call) After(preReq *Call) *Call { + c.t.Helper() + + if c == preReq { + c.t.Fatalf("A call isn't allowed to be its own prerequisite") + } + if preReq.isPreReq(c) { + c.t.Fatalf("Loop in call order: %v is a prerequisite to %v (possibly indirectly).", c, preReq) + } + + c.preReqs = append(c.preReqs, preReq) + return c +} + +// Returns true if the minimum number of calls have been made. +func (c *Call) satisfied() bool { + return c.numCalls >= c.minCalls +} + +// Returns true iff the maximum number of calls have been made. +func (c *Call) exhausted() bool { + return c.numCalls >= c.maxCalls +} + +func (c *Call) String() string { + args := make([]string, len(c.args)) + for i, arg := range c.args { + args[i] = arg.String() + } + arguments := strings.Join(args, ", ") + return fmt.Sprintf("%T.%v(%s) %s", c.receiver, c.method, arguments, c.origin) +} + +// Tests if the given call matches the expected call. +// If yes, returns nil. If no, returns error with message explaining why it does not match. +func (c *Call) matches(args []interface{}) error { + if !c.methodType.IsVariadic() { + if len(args) != len(c.args) { + return fmt.Errorf("Expected call at %s has the wrong number of arguments. Got: %d, want: %d", + c.origin, len(args), len(c.args)) + } + + for i, m := range c.args { + if !m.Matches(args[i]) { + return fmt.Errorf("Expected call at %s doesn't match the argument at index %s.\nGot: %v\nWant: %v", + c.origin, strconv.Itoa(i), args[i], m) + } + } + } else { + if len(c.args) < c.methodType.NumIn()-1 { + return fmt.Errorf("Expected call at %s has the wrong number of matchers. Got: %d, want: %d", + c.origin, len(c.args), c.methodType.NumIn()-1) + } + if len(c.args) != c.methodType.NumIn() && len(args) != len(c.args) { + return fmt.Errorf("Expected call at %s has the wrong number of arguments. Got: %d, want: %d", + c.origin, len(args), len(c.args)) + } + if len(args) < len(c.args)-1 { + return fmt.Errorf("Expected call at %s has the wrong number of arguments. Got: %d, want: greater than or equal to %d", + c.origin, len(args), len(c.args)-1) + } + + for i, m := range c.args { + if i < c.methodType.NumIn()-1 { + // Non-variadic args + if !m.Matches(args[i]) { + return fmt.Errorf("Expected call at %s doesn't match the argument at index %s.\nGot: %v\nWant: %v", + c.origin, strconv.Itoa(i), args[i], m) + } + continue + } + // The last arg has a possibility of a variadic argument, so let it branch + + // sample: Foo(a int, b int, c ...int) + if i < len(c.args) && i < len(args) { + if m.Matches(args[i]) { + // Got Foo(a, b, c) want Foo(matcherA, matcherB, gomock.Any()) + // Got Foo(a, b, c) want Foo(matcherA, matcherB, someSliceMatcher) + // Got Foo(a, b, c) want Foo(matcherA, matcherB, matcherC) + // Got Foo(a, b) want Foo(matcherA, matcherB) + // Got Foo(a, b, c, d) want Foo(matcherA, matcherB, matcherC, matcherD) + continue + } + } + + // The number of actual args don't match the number of matchers, + // or the last matcher is a slice and the last arg is not. + // If this function still matches it is because the last matcher + // matches all the remaining arguments or the lack of any. + // Convert the remaining arguments, if any, into a slice of the + // expected type. + vargsType := c.methodType.In(c.methodType.NumIn() - 1) + vargs := reflect.MakeSlice(vargsType, 0, len(args)-i) + for _, arg := range args[i:] { + vargs = reflect.Append(vargs, reflect.ValueOf(arg)) + } + if m.Matches(vargs.Interface()) { + // Got Foo(a, b, c, d, e) want Foo(matcherA, matcherB, gomock.Any()) + // Got Foo(a, b, c, d, e) want Foo(matcherA, matcherB, someSliceMatcher) + // Got Foo(a, b) want Foo(matcherA, matcherB, gomock.Any()) + // Got Foo(a, b) want Foo(matcherA, matcherB, someEmptySliceMatcher) + break + } + // Wrong number of matchers or not match. Fail. + // Got Foo(a, b) want Foo(matcherA, matcherB, matcherC, matcherD) + // Got Foo(a, b, c) want Foo(matcherA, matcherB, matcherC, matcherD) + // Got Foo(a, b, c, d) want Foo(matcherA, matcherB, matcherC, matcherD, matcherE) + // Got Foo(a, b, c, d, e) want Foo(matcherA, matcherB, matcherC, matcherD) + // Got Foo(a, b, c) want Foo(matcherA, matcherB) + return fmt.Errorf("Expected call at %s doesn't match the argument at index %s.\nGot: %v\nWant: %v", + c.origin, strconv.Itoa(i), args[i:], c.args[i]) + + } + } + + // Check that all prerequisite calls have been satisfied. + for _, preReqCall := range c.preReqs { + if !preReqCall.satisfied() { + return fmt.Errorf("Expected call at %s doesn't have a prerequisite call satisfied:\n%v\nshould be called before:\n%v", + c.origin, preReqCall, c) + } + } + + // Check that the call is not exhausted. + if c.exhausted() { + return fmt.Errorf("Expected call at %s has already been called the max number of times.", c.origin) + } + + return nil +} + +// dropPrereqs tells the expected Call to not re-check prerequisite calls any +// longer, and to return its current set. +func (c *Call) dropPrereqs() (preReqs []*Call) { + preReqs = c.preReqs + c.preReqs = nil + return +} + +func (c *Call) call(args []interface{}) []func([]interface{}) []interface{} { + c.numCalls++ + return c.actions +} + +// InOrder declares that the given calls should occur in order. +func InOrder(calls ...*Call) { + for i := 1; i < len(calls); i++ { + calls[i].After(calls[i-1]) + } +} + +func setSlice(arg interface{}, v reflect.Value) { + va := reflect.ValueOf(arg) + for i := 0; i < v.Len(); i++ { + va.Index(i).Set(v.Index(i)) + } +} + +func (c *Call) addAction(action func([]interface{}) []interface{}) { + c.actions = append(c.actions, action) +} diff --git a/vendor/github.com/golang/mock/gomock/callset.go b/vendor/github.com/golang/mock/gomock/callset.go new file mode 100644 index 0000000..c44a8a5 --- /dev/null +++ b/vendor/github.com/golang/mock/gomock/callset.go @@ -0,0 +1,108 @@ +// Copyright 2011 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package gomock + +import ( + "bytes" + "fmt" +) + +// callSet represents a set of expected calls, indexed by receiver and method +// name. +type callSet struct { + // Calls that are still expected. + expected map[callSetKey][]*Call + // Calls that have been exhausted. + exhausted map[callSetKey][]*Call +} + +// callSetKey is the key in the maps in callSet +type callSetKey struct { + receiver interface{} + fname string +} + +func newCallSet() *callSet { + return &callSet{make(map[callSetKey][]*Call), make(map[callSetKey][]*Call)} +} + +// Add adds a new expected call. +func (cs callSet) Add(call *Call) { + key := callSetKey{call.receiver, call.method} + m := cs.expected + if call.exhausted() { + m = cs.exhausted + } + m[key] = append(m[key], call) +} + +// Remove removes an expected call. +func (cs callSet) Remove(call *Call) { + key := callSetKey{call.receiver, call.method} + calls := cs.expected[key] + for i, c := range calls { + if c == call { + // maintain order for remaining calls + cs.expected[key] = append(calls[:i], calls[i+1:]...) + cs.exhausted[key] = append(cs.exhausted[key], call) + break + } + } +} + +// FindMatch searches for a matching call. Returns error with explanation message if no call matched. +func (cs callSet) FindMatch(receiver interface{}, method string, args []interface{}) (*Call, error) { + key := callSetKey{receiver, method} + + // Search through the expected calls. + expected := cs.expected[key] + var callsErrors bytes.Buffer + for _, call := range expected { + err := call.matches(args) + if err != nil { + fmt.Fprintf(&callsErrors, "\n%v", err) + } else { + return call, nil + } + } + + // If we haven't found a match then search through the exhausted calls so we + // get useful error messages. + exhausted := cs.exhausted[key] + for _, call := range exhausted { + if err := call.matches(args); err != nil { + fmt.Fprintf(&callsErrors, "\n%v", err) + } + } + + if len(expected)+len(exhausted) == 0 { + fmt.Fprintf(&callsErrors, "there are no expected calls of the method %q for that receiver", method) + } + + return nil, fmt.Errorf(callsErrors.String()) +} + +// Failures returns the calls that are not satisfied. +func (cs callSet) Failures() []*Call { + failures := make([]*Call, 0, len(cs.expected)) + for _, calls := range cs.expected { + for _, call := range calls { + if !call.satisfied() { + failures = append(failures, call) + } + } + } + return failures +} diff --git a/vendor/github.com/golang/mock/gomock/controller.go b/vendor/github.com/golang/mock/gomock/controller.go new file mode 100644 index 0000000..0651c91 --- /dev/null +++ b/vendor/github.com/golang/mock/gomock/controller.go @@ -0,0 +1,264 @@ +// Copyright 2010 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package gomock is a mock framework for Go. +// +// Standard usage: +// (1) Define an interface that you wish to mock. +// type MyInterface interface { +// SomeMethod(x int64, y string) +// } +// (2) Use mockgen to generate a mock from the interface. +// (3) Use the mock in a test: +// func TestMyThing(t *testing.T) { +// mockCtrl := gomock.NewController(t) +// defer mockCtrl.Finish() +// +// mockObj := something.NewMockMyInterface(mockCtrl) +// mockObj.EXPECT().SomeMethod(4, "blah") +// // pass mockObj to a real object and play with it. +// } +// +// By default, expected calls are not enforced to run in any particular order. +// Call order dependency can be enforced by use of InOrder and/or Call.After. +// Call.After can create more varied call order dependencies, but InOrder is +// often more convenient. +// +// The following examples create equivalent call order dependencies. +// +// Example of using Call.After to chain expected call order: +// +// firstCall := mockObj.EXPECT().SomeMethod(1, "first") +// secondCall := mockObj.EXPECT().SomeMethod(2, "second").After(firstCall) +// mockObj.EXPECT().SomeMethod(3, "third").After(secondCall) +// +// Example of using InOrder to declare expected call order: +// +// gomock.InOrder( +// mockObj.EXPECT().SomeMethod(1, "first"), +// mockObj.EXPECT().SomeMethod(2, "second"), +// mockObj.EXPECT().SomeMethod(3, "third"), +// ) +// +// TODO: +// - Handle different argument/return types (e.g. ..., chan, map, interface). +package gomock + +import ( + "context" + "fmt" + "reflect" + "runtime" + "sync" +) + +// A TestReporter is something that can be used to report test failures. It +// is satisfied by the standard library's *testing.T. +type TestReporter interface { + Errorf(format string, args ...interface{}) + Fatalf(format string, args ...interface{}) +} + +// TestHelper is a TestReporter that has the Helper method. It is satisfied +// by the standard library's *testing.T. +type TestHelper interface { + TestReporter + Helper() +} + +// A Controller represents the top-level control of a mock ecosystem. It +// defines the scope and lifetime of mock objects, as well as their +// expectations. It is safe to call Controller's methods from multiple +// goroutines. Each test should create a new Controller and invoke Finish via +// defer. +// +// func TestFoo(t *testing.T) { +// ctrl := gomock.NewController(st) +// defer ctrl.Finish() +// // .. +// } +// +// func TestBar(t *testing.T) { +// t.Run("Sub-Test-1", st) { +// ctrl := gomock.NewController(st) +// defer ctrl.Finish() +// // .. +// }) +// t.Run("Sub-Test-2", st) { +// ctrl := gomock.NewController(st) +// defer ctrl.Finish() +// // .. +// }) +// }) +type Controller struct { + // T should only be called within a generated mock. It is not intended to + // be used in user code and may be changed in future versions. T is the + // TestReporter passed in when creating the Controller via NewController. + // If the TestReporter does not implement a TestHelper it will be wrapped + // with a nopTestHelper. + T TestHelper + mu sync.Mutex + expectedCalls *callSet + finished bool +} + +// NewController returns a new Controller. It is the preferred way to create a +// Controller. +func NewController(t TestReporter) *Controller { + h, ok := t.(TestHelper) + if !ok { + h = nopTestHelper{t} + } + + return &Controller{ + T: h, + expectedCalls: newCallSet(), + } +} + +type cancelReporter struct { + TestHelper + cancel func() +} + +func (r *cancelReporter) Errorf(format string, args ...interface{}) { + r.TestHelper.Errorf(format, args...) +} +func (r *cancelReporter) Fatalf(format string, args ...interface{}) { + defer r.cancel() + r.TestHelper.Fatalf(format, args...) +} + +// WithContext returns a new Controller and a Context, which is cancelled on any +// fatal failure. +func WithContext(ctx context.Context, t TestReporter) (*Controller, context.Context) { + h, ok := t.(TestHelper) + if !ok { + h = nopTestHelper{t} + } + + ctx, cancel := context.WithCancel(ctx) + return NewController(&cancelReporter{h, cancel}), ctx +} + +type nopTestHelper struct { + TestReporter +} + +func (h nopTestHelper) Helper() {} + +// RecordCall is called by a mock. It should not be called by user code. +func (ctrl *Controller) RecordCall(receiver interface{}, method string, args ...interface{}) *Call { + ctrl.T.Helper() + + recv := reflect.ValueOf(receiver) + for i := 0; i < recv.Type().NumMethod(); i++ { + if recv.Type().Method(i).Name == method { + return ctrl.RecordCallWithMethodType(receiver, method, recv.Method(i).Type(), args...) + } + } + ctrl.T.Fatalf("gomock: failed finding method %s on %T", method, receiver) + panic("unreachable") +} + +// RecordCallWithMethodType is called by a mock. It should not be called by user code. +func (ctrl *Controller) RecordCallWithMethodType(receiver interface{}, method string, methodType reflect.Type, args ...interface{}) *Call { + ctrl.T.Helper() + + call := newCall(ctrl.T, receiver, method, methodType, args...) + + ctrl.mu.Lock() + defer ctrl.mu.Unlock() + ctrl.expectedCalls.Add(call) + + return call +} + +// Call is called by a mock. It should not be called by user code. +func (ctrl *Controller) Call(receiver interface{}, method string, args ...interface{}) []interface{} { + ctrl.T.Helper() + + // Nest this code so we can use defer to make sure the lock is released. + actions := func() []func([]interface{}) []interface{} { + ctrl.T.Helper() + ctrl.mu.Lock() + defer ctrl.mu.Unlock() + + expected, err := ctrl.expectedCalls.FindMatch(receiver, method, args) + if err != nil { + origin := callerInfo(2) + ctrl.T.Fatalf("Unexpected call to %T.%v(%v) at %s because: %s", receiver, method, args, origin, err) + } + + // Two things happen here: + // * the matching call no longer needs to check prerequite calls, + // * and the prerequite calls are no longer expected, so remove them. + preReqCalls := expected.dropPrereqs() + for _, preReqCall := range preReqCalls { + ctrl.expectedCalls.Remove(preReqCall) + } + + actions := expected.call(args) + if expected.exhausted() { + ctrl.expectedCalls.Remove(expected) + } + return actions + }() + + var rets []interface{} + for _, action := range actions { + if r := action(args); r != nil { + rets = r + } + } + + return rets +} + +// Finish checks to see if all the methods that were expected to be called +// were called. It should be invoked for each Controller. It is not idempotent +// and therefore can only be invoked once. +func (ctrl *Controller) Finish() { + ctrl.T.Helper() + + ctrl.mu.Lock() + defer ctrl.mu.Unlock() + + if ctrl.finished { + ctrl.T.Fatalf("Controller.Finish was called more than once. It has to be called exactly once.") + } + ctrl.finished = true + + // If we're currently panicking, probably because this is a deferred call, + // pass through the panic. + if err := recover(); err != nil { + panic(err) + } + + // Check that all remaining expected calls are satisfied. + failures := ctrl.expectedCalls.Failures() + for _, call := range failures { + ctrl.T.Errorf("missing call(s) to %v", call) + } + if len(failures) != 0 { + ctrl.T.Fatalf("aborting test due to missing call(s)") + } +} + +func callerInfo(skip int) string { + if _, file, line, ok := runtime.Caller(skip + 1); ok { + return fmt.Sprintf("%s:%d", file, line) + } + return "unknown file" +} diff --git a/vendor/github.com/golang/mock/gomock/matchers.go b/vendor/github.com/golang/mock/gomock/matchers.go new file mode 100644 index 0000000..fbff060 --- /dev/null +++ b/vendor/github.com/golang/mock/gomock/matchers.go @@ -0,0 +1,141 @@ +// Copyright 2010 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package gomock + +import ( + "fmt" + "reflect" +) + +// A Matcher is a representation of a class of values. +// It is used to represent the valid or expected arguments to a mocked method. +type Matcher interface { + // Matches returns whether x is a match. + Matches(x interface{}) bool + + // String describes what the matcher matches. + String() string +} + +type anyMatcher struct{} + +func (anyMatcher) Matches(x interface{}) bool { + return true +} + +func (anyMatcher) String() string { + return "is anything" +} + +type eqMatcher struct { + x interface{} +} + +func (e eqMatcher) Matches(x interface{}) bool { + return reflect.DeepEqual(e.x, x) +} + +func (e eqMatcher) String() string { + return fmt.Sprintf("is equal to %v", e.x) +} + +type nilMatcher struct{} + +func (nilMatcher) Matches(x interface{}) bool { + if x == nil { + return true + } + + v := reflect.ValueOf(x) + switch v.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, + reflect.Ptr, reflect.Slice: + return v.IsNil() + } + + return false +} + +func (nilMatcher) String() string { + return "is nil" +} + +type notMatcher struct { + m Matcher +} + +func (n notMatcher) Matches(x interface{}) bool { + return !n.m.Matches(x) +} + +func (n notMatcher) String() string { + // TODO: Improve this if we add a NotString method to the Matcher interface. + return "not(" + n.m.String() + ")" +} + +type assignableToTypeOfMatcher struct { + targetType reflect.Type +} + +func (m assignableToTypeOfMatcher) Matches(x interface{}) bool { + return reflect.TypeOf(x).AssignableTo(m.targetType) +} + +func (m assignableToTypeOfMatcher) String() string { + return "is assignable to " + m.targetType.Name() +} + +// Constructors +// Any returns a matcher that always matches. +func Any() Matcher { return anyMatcher{} } + +// Eq returns a matcher that matches on equality. +// +// Example usage: +// Eq(5).Matches(5) // returns true +// Eq(5).Matches(4) // returns false +func Eq(x interface{}) Matcher { return eqMatcher{x} } + +// Nil returns a matcher that matches if the received value is nil. +// +// Example usage: +// var x *bytes.Buffer +// Nil().Matches(x) // returns true +// x = &bytes.Buffer{} +// Nil().Matches(x) // returns false +func Nil() Matcher { return nilMatcher{} } + +// Not reverses the results of its given child matcher. +// +// Example usage: +// Not(Eq(5)).Matches(4) // returns true +// Not(Eq(5)).Matches(5) // returns false +func Not(x interface{}) Matcher { + if m, ok := x.(Matcher); ok { + return notMatcher{m} + } + return notMatcher{Eq(x)} +} + +// AssignableToTypeOf is a Matcher that matches if the parameter to the mock +// function is assignable to the type of the parameter to this function. +// +// Example usage: +// var s fmt.Stringer = &bytes.Buffer{} +// AssignableToTypeOf(s).Matches(time.Second) // returns true +// AssignableToTypeOf(s).Matches(99) // returns false +func AssignableToTypeOf(x interface{}) Matcher { + return assignableToTypeOfMatcher{reflect.TypeOf(x)} +} diff --git a/vendor/github.com/vacp2p/mvds/node/node.go b/vendor/github.com/vacp2p/mvds/node/node.go deleted file mode 100644 index c989aaa..0000000 --- a/vendor/github.com/vacp2p/mvds/node/node.go +++ /dev/null @@ -1,371 +0,0 @@ -// Package Node contains node logic. -package node - -// @todo this is a very rough implementation that needs cleanup - -import ( - "bytes" - "context" - "fmt" - "log" - "sync/atomic" - "time" - - "github.com/vacp2p/mvds/protobuf" - "github.com/vacp2p/mvds/state" - "github.com/vacp2p/mvds/store" - "github.com/vacp2p/mvds/transport" -) - -// Mode represents the synchronization mode. -type Mode int - -const ( - INTERACTIVE Mode = iota - BATCH -) - -// CalculateNextEpoch is a function used to calculate the next `SendEpoch` for a given message. -type CalculateNextEpoch func(count uint64, epoch int64) int64 - -// Node represents an MVDS node, it runs all the logic like sending and receiving protocol messages. -type Node struct { - ctx context.Context - cancel context.CancelFunc - - store store.MessageStore - transport transport.Transport - - syncState state.SyncState - - peers map[state.GroupID][]state.PeerID - - payloads payloads - - nextEpoch CalculateNextEpoch - - ID state.PeerID - - epoch int64 - mode Mode - - subscription chan<- protobuf.Message -} - -// NewNode returns a new node. -func NewNode( - ms store.MessageStore, - st transport.Transport, - ss state.SyncState, - nextEpoch CalculateNextEpoch, - currentEpoch int64, - id state.PeerID, - mode Mode, -) *Node { - ctx, cancel := context.WithCancel(context.Background()) - - return &Node{ - ctx: ctx, - cancel: cancel, - store: ms, - transport: st, - syncState: ss, - peers: make(map[state.GroupID][]state.PeerID), - payloads: newPayloads(), - nextEpoch: nextEpoch, - ID: id, - epoch: currentEpoch, - mode: mode, - } -} - -// Start listens for new messages received by the node and sends out those required every epoch. -func (n *Node) Start() { - go func() { - for { - select { - case <-n.ctx.Done(): - log.Print("Watch stopped") - return - default: - p := n.transport.Watch() - go n.onPayload(p.Group, p.Sender, p.Payload) - } - } - }() - - go func() { - for { - select { - case <-n.ctx.Done(): - log.Print("Epoch processing stopped") - return - default: - log.Printf("Node: %x Epoch: %d", n.ID[:4], n.epoch) - time.Sleep(1 * time.Second) - - n.sendMessages() - atomic.AddInt64(&n.epoch, 1) - } - } - }() -} - -// Stop message reading and epoch processing -func (n *Node) Stop() { - n.cancel() -} - -// Subscribe subscribes to incoming messages. -func (n *Node) Subscribe(sub chan <-protobuf.Message) { - n.subscription = sub -} - -// AppendMessage sends a message to a given group. -func (n *Node) AppendMessage(group state.GroupID, data []byte) (state.MessageID, error) { - m := protobuf.Message{ - GroupId: group[:], - Timestamp: time.Now().Unix(), - Body: data, - } - - id := m.ID() - - peers, ok := n.peers[group] - if !ok { - return state.MessageID{}, fmt.Errorf("trying to send to unknown group %x", group[:4]) - } - - err := n.store.Add(m) - if err != nil { - return state.MessageID{}, err - } - - go func() { - for _, p := range peers { - if !n.IsPeerInGroup(group, p) { - continue - } - - t := state.OFFER - if n.mode == BATCH { - t = state.MESSAGE - } - - n.insertSyncState(group, id, p, t) - } - }() - - log.Printf("[%x] node %x sending %x\n", group[:4], n.ID[:4], id[:4]) - // @todo think about a way to insta trigger send messages when send was selected, we don't wanna wait for ticks here - - return id, nil -} - -// RequestMessage adds a REQUEST record to the next payload for a given message ID. -func (n *Node) RequestMessage(group state.GroupID, id state.MessageID) error { - peers, ok := n.peers[group] - if !ok { - return fmt.Errorf("trying to request from an unknown group %x", group[:4]) - } - - go func() { - for _, p := range peers { - if !n.IsPeerInGroup(group, p) { - continue - } - - n.insertSyncState(group, id, p, state.REQUEST) - } - }() - - return nil -} - -// AddPeer adds a peer to a specific group making it a recipient of messages. -func (n *Node) AddPeer(group state.GroupID, id state.PeerID) { - if _, ok := n.peers[group]; !ok { - n.peers[group] = make([]state.PeerID, 0) - } - - n.peers[group] = append(n.peers[group], id) -} - -// IsPeerInGroup checks whether a peer is in the specified group. -func (n Node) IsPeerInGroup(g state.GroupID, p state.PeerID) bool { - for _, peer := range n.peers[g] { - if bytes.Equal(peer[:], p[:]) { - return true - } - } - - return false -} - -func (n *Node) sendMessages() { - err := n.syncState.Map(n.epoch, func(g state.GroupID, m state.MessageID, p state.PeerID, s state.State) state.State { - if !n.IsPeerInGroup(g, p) { - return s - } - - switch s.Type { - case state.OFFER: - n.payloads.AddOffers(g, p, m[:]) - case state.REQUEST: - n.payloads.AddRequests(g, p, m[:]) - log.Printf("[%x] sending REQUEST (%x -> %x): %x\n", g[:4], n.ID[:4], p[:4], m[:4]) - case state.MESSAGE: - msg, err := n.store.Get(m) - if err != nil { - log.Printf("failed to retreive message %x %s", m[:4], err.Error()) - return s - } - - n.payloads.AddMessages(g, p, &msg) - log.Printf("[%x] sending MESSAGE (%x -> %x): %x\n", g[:4], n.ID[:4], p[:4], m[:4]) - } - - return n.updateSendEpoch(s) - }) - - if err != nil { - log.Printf("error while mapping sync state: %s", err.Error()) - } - - n.payloads.MapAndClear(func(id state.GroupID, peer state.PeerID, payload protobuf.Payload) { - err := n.transport.Send(id, n.ID, peer, payload) - if err != nil { - log.Printf("error sending message: %s", err.Error()) - // @todo - } - }) -} - -func (n *Node) onPayload(group state.GroupID, sender state.PeerID, payload protobuf.Payload) { - // Acks, Requests and Offers are all arrays of bytes as protobuf doesn't allow type aliases otherwise arrays of messageIDs would be nicer. - n.onAck(group, sender, payload.Acks) - n.onRequest(group, sender, payload.Requests) - n.onOffer(group, sender, payload.Offers) - n.payloads.AddAcks(group, sender, n.onMessages(group, sender, payload.Messages)...) -} - -func (n *Node) onOffer(group state.GroupID, sender state.PeerID, offers [][]byte) { - for _, raw := range offers { - id := toMessageID(raw) - log.Printf("[%x] OFFER (%x -> %x): %x received.\n", group[:4], sender[:4], n.ID[:4], id[:4]) - - // @todo maybe ack? - if n.store.Has(id) { - continue - } - - n.insertSyncState(group, id, sender, state.REQUEST) - } -} - -func (n *Node) onRequest(group state.GroupID, sender state.PeerID, requests [][]byte) { - for _, raw := range requests { - id := toMessageID(raw) - log.Printf("[%x] REQUEST (%x -> %x): %x received.\n", group[:4], sender[:4], n.ID[:4], id[:4]) - - if !n.IsPeerInGroup(group, sender) { - log.Printf("[%x] peer %x is not in group", group[:4], sender[:4]) - continue - } - - if !n.store.Has(id) { - log.Printf("message %x does not exist", id[:4]) - continue - } - - n.insertSyncState(group, id, sender, state.MESSAGE) - } -} - -func (n *Node) onAck(group state.GroupID, sender state.PeerID, acks [][]byte) { - for _, raw := range acks { - id := toMessageID(raw) - - err := n.syncState.Remove(group, id, sender) - if err != nil { - log.Printf("error while removing sync state %s", err.Error()) - continue - } - - log.Printf("[%x] ACK (%x -> %x): %x received.\n", group[:4], sender[:4], n.ID[:4], id[:4]) - } -} - -func (n *Node) onMessages(group state.GroupID, sender state.PeerID, messages []*protobuf.Message) [][]byte { - a := make([][]byte, 0) - - for _, m := range messages { - err := n.onMessage(group, sender, *m) - if err != nil { - // @todo - continue - } - - id := m.ID() - log.Printf("[%x] sending ACK (%x -> %x): %x\n", group[:4], n.ID[:4], sender[:4], id[:4]) - a = append(a, id[:]) - } - - return a -} - -func (n *Node) onMessage(group state.GroupID, sender state.PeerID, msg protobuf.Message) error { - id := msg.ID() - log.Printf("[%x] MESSAGE (%x -> %x): %x received.\n", group[:4], sender[:4], n.ID[:4], id[:4]) - - err := n.syncState.Remove(group, id, sender) - if err != nil { - return err - } - - go func() { - for _, peer := range n.peers[group] { - if peer == sender { - continue - } - - n.insertSyncState(group, id, peer, state.OFFER) - } - }() - - if n.subscription != nil { - n.subscription <- msg - } - - err = n.store.Add(msg) - if err != nil { - return err - // @todo process, should this function ever even have an error? - } - - return nil -} - -func (n *Node) insertSyncState(group state.GroupID, id state.MessageID, p state.PeerID, t state.RecordType) { - s := state.State{ - Type: t, - SendEpoch: n.epoch + 1, - } - - err := n.syncState.Set(group, id, p, s) - if err != nil { - log.Printf("error (%s) setting sync state group: %x id: %x peer: %x", err.Error(), group[:4], id[:4], p[:4]) - } -} - -func (n Node) updateSendEpoch(s state.State) state.State { - s.SendCount += 1 - s.SendEpoch += n.nextEpoch(s.SendCount, n.epoch) - return s -} - -func toMessageID(b []byte) state.MessageID { - var id state.MessageID - copy(id[:], b) - return id -} diff --git a/vendor/github.com/vacp2p/mvds/node/payloads.go b/vendor/github.com/vacp2p/mvds/node/payloads.go deleted file mode 100644 index b2c278e..0000000 --- a/vendor/github.com/vacp2p/mvds/node/payloads.go +++ /dev/null @@ -1,94 +0,0 @@ -package node - -import ( - "sync" - - "github.com/vacp2p/mvds/protobuf" - "github.com/vacp2p/mvds/state" -) - -type payloads struct { - sync.Mutex - - payloads map[state.GroupID]map[state.PeerID]protobuf.Payload -} -// @todo check in all the functions below that we aren't duplicating stuff - -func newPayloads() payloads { - return payloads{ - payloads: make(map[state.GroupID]map[state.PeerID]protobuf.Payload), - } -} - -func (p *payloads) AddOffers(group state.GroupID, peer state.PeerID, offers ...[]byte) { - p.Lock() - defer p.Unlock() - - payload := p.get(group, peer) - - payload.Offers = append(payload.Offers, offers...) - - p.set(group, peer, payload) -} - -func (p *payloads) AddAcks(group state.GroupID, peer state.PeerID, acks ...[]byte) { - p.Lock() - defer p.Unlock() - - payload := p.get(group, peer) - - payload.Requests = append(payload.Requests, acks...) - - p.set(group, peer, payload) -} - -func (p *payloads) AddRequests(group state.GroupID, peer state.PeerID, request ...[]byte) { - p.Lock() - defer p.Unlock() - - payload := p.get(group, peer) - - payload.Requests = append(payload.Requests, request...) - - p.set(group, peer, payload) -} - -func (p *payloads) AddMessages(group state.GroupID, peer state.PeerID, messages ...*protobuf.Message) { - p.Lock() - defer p.Unlock() - - payload := p.get(group, peer) - if payload.Messages == nil { - payload.Messages = make([]*protobuf.Message, 0) - } - - payload.Messages = append(payload.Messages, messages...) - p.set(group, peer, payload) -} - -func (p *payloads) MapAndClear(f func(state.GroupID, state.PeerID, protobuf.Payload)) { - p.Lock() - defer p.Unlock() - - for g, payloads := range p.payloads { - for peer, payload := range payloads { - f(g, peer, payload) - } - } - - p.payloads = make(map[state.GroupID]map[state.PeerID]protobuf.Payload) -} - -func (p *payloads) get(id state.GroupID, peer state.PeerID) protobuf.Payload { - payload, _ := p.payloads[id][peer] - return payload -} - -func (p *payloads) set(id state.GroupID, peer state.PeerID, payload protobuf.Payload) { - _, ok := p.payloads[id] - if !ok { - p.payloads[id] = make(map[state.PeerID]protobuf.Payload) - } - - p.payloads[id][peer] = payload -} diff --git a/vendor/github.com/vacp2p/mvds/protobuf/payload.go b/vendor/github.com/vacp2p/mvds/protobuf/payload.go new file mode 100644 index 0000000..ac94f89 --- /dev/null +++ b/vendor/github.com/vacp2p/mvds/protobuf/payload.go @@ -0,0 +1,7 @@ +package protobuf + +// IsValid checks whether there are any known field in the protobuf +// message +func (m *Payload) IsValid() bool { + return len(m.Messages)+len(m.Acks)+len(m.Offers)+len(m.Requests) != 0 +} diff --git a/vendor/github.com/vacp2p/mvds/protobuf/sync.pb.go b/vendor/github.com/vacp2p/mvds/protobuf/sync.pb.go index 5ce3a07..35f8c86 100644 --- a/vendor/github.com/vacp2p/mvds/protobuf/sync.pb.go +++ b/vendor/github.com/vacp2p/mvds/protobuf/sync.pb.go @@ -21,10 +21,10 @@ var _ = math.Inf const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package type Payload struct { - Acks [][]byte `protobuf:"bytes,1,rep,name=acks,proto3" json:"acks,omitempty"` - Offers [][]byte `protobuf:"bytes,2,rep,name=offers,proto3" json:"offers,omitempty"` - Requests [][]byte `protobuf:"bytes,3,rep,name=requests,proto3" json:"requests,omitempty"` - Messages []*Message `protobuf:"bytes,4,rep,name=messages,proto3" json:"messages,omitempty"` + Acks [][]byte `protobuf:"bytes,5001,rep,name=acks,proto3" json:"acks,omitempty"` + Offers [][]byte `protobuf:"bytes,5002,rep,name=offers,proto3" json:"offers,omitempty"` + Requests [][]byte `protobuf:"bytes,5003,rep,name=requests,proto3" json:"requests,omitempty"` + Messages []*Message `protobuf:"bytes,5004,rep,name=messages,proto3" json:"messages,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -84,9 +84,9 @@ func (m *Payload) GetMessages() []*Message { } type Message struct { - GroupId []byte `protobuf:"bytes,1,opt,name=group_id,json=groupId,proto3" json:"group_id,omitempty"` - Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` - Body []byte `protobuf:"bytes,3,opt,name=body,proto3" json:"body,omitempty"` + GroupId []byte `protobuf:"bytes,6001,opt,name=group_id,json=groupId,proto3" json:"group_id,omitempty"` + Timestamp int64 `protobuf:"varint,6002,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Body []byte `protobuf:"bytes,6003,opt,name=body,proto3" json:"body,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -146,19 +146,19 @@ func init() { func init() { proto.RegisterFile("protobuf/sync.proto", fileDescriptor_2dca527c092c79d7) } var fileDescriptor_2dca527c092c79d7 = []byte{ - // 212 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x44, 0x8f, 0xb1, 0x4a, 0x04, 0x31, - 0x10, 0x86, 0xd9, 0xcb, 0x72, 0x1b, 0xc7, 0xb3, 0x19, 0x41, 0xa2, 0x58, 0x84, 0xab, 0x62, 0xb3, - 0x82, 0xbe, 0x81, 0x9d, 0x85, 0x20, 0x29, 0x2c, 0x6c, 0x24, 0x7b, 0xc9, 0x1e, 0x87, 0xc6, 0xac, - 0x99, 0xac, 0xb0, 0xe0, 0xc3, 0xcb, 0x8d, 0xe7, 0x5e, 0xf7, 0x7f, 0xdf, 0xcf, 0x30, 0x33, 0x70, - 0x3e, 0xe4, 0x54, 0x52, 0x37, 0xf6, 0xb7, 0x34, 0x7d, 0x6e, 0x5a, 0x26, 0xac, 0xe3, 0xb7, 0xa7, - 0xf5, 0x0f, 0x34, 0xcf, 0x6e, 0xfa, 0x48, 0xce, 0x23, 0x42, 0xed, 0x36, 0xef, 0xa4, 0x2a, 0x2d, - 0xcc, 0xca, 0x72, 0xc6, 0x0b, 0x58, 0xa6, 0xbe, 0x0f, 0x99, 0xd4, 0x82, 0xed, 0x81, 0xf0, 0x0a, - 0x64, 0x0e, 0x5f, 0x63, 0xa0, 0x42, 0x4a, 0x70, 0x33, 0x33, 0xde, 0x80, 0x8c, 0x81, 0xc8, 0x6d, - 0x03, 0xa9, 0x5a, 0x0b, 0x73, 0x7a, 0x77, 0xd6, 0xee, 0x77, 0xb5, 0x4f, 0x7f, 0xd6, 0xce, 0xf5, - 0xfa, 0x05, 0x9a, 0x83, 0xc4, 0x4b, 0x90, 0xdb, 0x9c, 0xc6, 0xe1, 0x6d, 0xe7, 0x55, 0xa5, 0x2b, - 0xb3, 0xb2, 0x0d, 0xf3, 0xa3, 0xc7, 0x6b, 0x38, 0x29, 0xbb, 0x18, 0xa8, 0xb8, 0x38, 0xa8, 0x85, - 0xae, 0x8c, 0xb0, 0x47, 0xb1, 0x3f, 0xbb, 0x4b, 0x7e, 0x52, 0x82, 0x87, 0x38, 0x3f, 0xc0, 0xab, - 0xfc, 0x7f, 0xb9, 0x5b, 0x72, 0xba, 0xff, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x4c, 0x0a, 0x11, 0xee, - 0x05, 0x01, 0x00, 0x00, + // 215 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x2e, 0x28, 0xca, 0x2f, + 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x2f, 0xae, 0xcc, 0x4b, 0xd6, 0x03, 0xf3, 0x84, 0x58, 0x72, 0xcb, + 0x52, 0x8a, 0x95, 0x1a, 0x18, 0xb9, 0xd8, 0x03, 0x12, 0x2b, 0x73, 0xf2, 0x13, 0x53, 0x84, 0x84, + 0xb9, 0x58, 0x12, 0x93, 0xb3, 0x8b, 0x25, 0x3a, 0xd5, 0x15, 0x98, 0x35, 0x78, 0x82, 0xc0, 0x1c, + 0x21, 0x71, 0x2e, 0xb6, 0xfc, 0xb4, 0xb4, 0xd4, 0xa2, 0x62, 0x89, 0x2e, 0x88, 0x30, 0x94, 0x2b, + 0x24, 0xcd, 0xc5, 0x51, 0x94, 0x5a, 0x58, 0x9a, 0x5a, 0x5c, 0x52, 0x2c, 0xd1, 0x0d, 0x91, 0x82, + 0x0b, 0x08, 0x69, 0x71, 0x71, 0xe4, 0xa6, 0x16, 0x17, 0x27, 0xa6, 0xa7, 0x16, 0x4b, 0xf4, 0x80, + 0x24, 0xb9, 0x8d, 0x78, 0xf5, 0x40, 0x16, 0xea, 0xf9, 0x42, 0x84, 0x83, 0xe0, 0xf2, 0x4a, 0x91, + 0x5c, 0xec, 0x50, 0x41, 0x21, 0x29, 0x2e, 0x8e, 0xf4, 0xa2, 0xfc, 0xd2, 0x82, 0xf8, 0xcc, 0x14, + 0x89, 0x8f, 0x7a, 0x0a, 0x8c, 0x1a, 0x3c, 0x41, 0xec, 0x60, 0x01, 0xcf, 0x14, 0x21, 0x59, 0x2e, + 0xce, 0x92, 0xcc, 0xdc, 0xd4, 0xe2, 0x92, 0xc4, 0xdc, 0x02, 0x89, 0x4f, 0x20, 0x49, 0xe6, 0x20, + 0x84, 0x08, 0xc8, 0xf1, 0x49, 0xf9, 0x29, 0x95, 0x12, 0x9f, 0x21, 0xda, 0xc0, 0x1c, 0x27, 0xae, + 0x28, 0x0e, 0x98, 0xd7, 0x93, 0xd8, 0xc0, 0x2c, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x18, + 0xdd, 0xc7, 0x8d, 0x0d, 0x01, 0x00, 0x00, } diff --git a/vendor/github.com/vacp2p/mvds/protobuf/sync.proto b/vendor/github.com/vacp2p/mvds/protobuf/sync.proto index ebca19e..b068493 100644 --- a/vendor/github.com/vacp2p/mvds/protobuf/sync.proto +++ b/vendor/github.com/vacp2p/mvds/protobuf/sync.proto @@ -4,14 +4,14 @@ package mvds; option go_package = "protobuf"; message Payload { - repeated bytes acks = 1; - repeated bytes offers = 2; - repeated bytes requests = 3; - repeated Message messages = 4; + repeated bytes acks = 5001; + repeated bytes offers = 5002; + repeated bytes requests = 5003; + repeated Message messages = 5004; } message Message { - bytes group_id = 1; - int64 timestamp = 2; - bytes body = 3; + bytes group_id = 6001; + int64 timestamp = 6002; + bytes body = 6003; } diff --git a/vendor/github.com/vacp2p/mvds/state/state.go b/vendor/github.com/vacp2p/mvds/state/state.go index df5ed9d..5f18684 100644 --- a/vendor/github.com/vacp2p/mvds/state/state.go +++ b/vendor/github.com/vacp2p/mvds/state/state.go @@ -15,11 +15,15 @@ type State struct { Type RecordType SendCount uint64 SendEpoch int64 + // GroupID is optional, thus nullable + GroupID *GroupID + PeerID PeerID + MessageID MessageID } type SyncState interface { - Get(group GroupID, id MessageID, peer PeerID) (State, error) - Set(group GroupID, id MessageID, peer PeerID, newState State) error - Remove(group GroupID, id MessageID, peer PeerID) error - Map(epoch int64, process func(GroupID, MessageID, PeerID, State) State) error + Add(newState State) error + Remove(id MessageID, peer PeerID) error + All() ([]State, error) + Map(epoch int64, process func(State) State) error } diff --git a/vendor/github.com/vacp2p/mvds/state/state_memory.go b/vendor/github.com/vacp2p/mvds/state/state_memory.go index 5045560..ef1d75b 100644 --- a/vendor/github.com/vacp2p/mvds/state/state_memory.go +++ b/vendor/github.com/vacp2p/mvds/state/state_memory.go @@ -7,62 +7,55 @@ import ( type memorySyncState struct { sync.Mutex - state map[GroupID]map[MessageID]map[PeerID]State + state []State } func NewSyncState() *memorySyncState { - return &memorySyncState{ - state: make(map[GroupID]map[MessageID]map[PeerID]State), - } + return &memorySyncState{} } -func (s *memorySyncState) Get(group GroupID, id MessageID, peer PeerID) (State, error) { +func (s *memorySyncState) Add(newState State) error { s.Lock() defer s.Unlock() - state, _ := s.state[group][id][peer] - return state, nil -} + s.state = append(s.state, newState) -func (s *memorySyncState) Set(group GroupID, id MessageID, peer PeerID, newState State) error { - s.Lock() - defer s.Unlock() - - if _, ok := s.state[group]; !ok { - s.state[group] = make(map[MessageID]map[PeerID]State) - } - - if _, ok := s.state[group][id]; !ok { - s.state[group][id] = make(map[PeerID]State) - } - - s.state[group][id][peer] = newState return nil } -func (s *memorySyncState) Remove(group GroupID, id MessageID, peer PeerID) error { +func (s *memorySyncState) Remove(id MessageID, peer PeerID) error { s.Lock() defer s.Unlock() + var newState []State - delete(s.state[group][id], peer) - return nil -} - -func (s *memorySyncState) Map(epoch int64, process func(GroupID, MessageID, PeerID, State) State) error { - s.Lock() - defer s.Unlock() - - for group, syncstate := range s.state { - for id, peers := range syncstate { - for peer, state := range peers { - if state.SendEpoch < epoch { - continue - } - - s.state[group][id][peer] = process(group, id, peer, state) - } + for _, state := range s.state { + if state.MessageID != id || state.PeerID != peer { + newState = append(newState, state) } } + s.state = newState + + return nil +} + +func (s *memorySyncState) All() ([]State, error) { + s.Lock() + defer s.Unlock() + return s.state, nil +} + +func (s *memorySyncState) Map(epoch int64, process func(State) State) error { + s.Lock() + defer s.Unlock() + + for i, state := range s.state { + if state.SendEpoch > epoch { + continue + } + + s.state[i] = process(state) + } + return nil } diff --git a/vendor/github.com/vacp2p/mvds/store/messagestore.go b/vendor/github.com/vacp2p/mvds/store/messagestore.go index ad8aebd..cd2bd9f 100644 --- a/vendor/github.com/vacp2p/mvds/store/messagestore.go +++ b/vendor/github.com/vacp2p/mvds/store/messagestore.go @@ -7,7 +7,7 @@ import ( ) type MessageStore interface { - Has(id state.MessageID) bool - Get(id state.MessageID) (protobuf.Message, error) - Add(message protobuf.Message) error + Has(id state.MessageID) (bool, error) + Get(id state.MessageID) (*protobuf.Message, error) + Add(message *protobuf.Message) error } diff --git a/vendor/github.com/vacp2p/mvds/store/messagestore_dummy.go b/vendor/github.com/vacp2p/mvds/store/messagestore_dummy.go index 08de3f5..5a857fb 100644 --- a/vendor/github.com/vacp2p/mvds/store/messagestore_dummy.go +++ b/vendor/github.com/vacp2p/mvds/store/messagestore_dummy.go @@ -10,33 +10,34 @@ import ( type DummyStore struct { sync.Mutex - ms map[state.MessageID]protobuf.Message + ms map[state.MessageID]*protobuf.Message } func NewDummyStore() DummyStore { - return DummyStore{ms: make(map[state.MessageID]protobuf.Message)} + return DummyStore{ms: make(map[state.MessageID]*protobuf.Message)} } -func (ds *DummyStore) Has(id state.MessageID) bool { +func (ds *DummyStore) Has(id state.MessageID) (bool, error) { ds.Lock() defer ds.Unlock() - _, ok := ds.ms[id]; return ok + _, ok := ds.ms[id] + return ok, nil } -func (ds *DummyStore) Get(id state.MessageID) (protobuf.Message, error) { +func (ds *DummyStore) Get(id state.MessageID) (*protobuf.Message, error) { ds.Lock() defer ds.Unlock() m, ok := ds.ms[id] if !ok { - return protobuf.Message{}, errors.New("message does not exist") + return nil, errors.New("message does not exist") } return m, nil } -func (ds *DummyStore) Add(message protobuf.Message) error { +func (ds *DummyStore) Add(message *protobuf.Message) error { ds.Lock() defer ds.Unlock() ds.ms[message.ID()] = message diff --git a/vendor/github.com/vacp2p/mvds/transport/channel_transport.go b/vendor/github.com/vacp2p/mvds/transport/channel_transport.go deleted file mode 100644 index d443844..0000000 --- a/vendor/github.com/vacp2p/mvds/transport/channel_transport.go +++ /dev/null @@ -1,54 +0,0 @@ -package transport - -import ( - "errors" - math "math/rand" - "sync" - "time" - - "github.com/vacp2p/mvds/protobuf" - "github.com/vacp2p/mvds/state" -) - -// ChannelTransport implements a basic MVDS transport using channels for basic testing purposes. -type ChannelTransport struct { - sync.Mutex - - offline int - - in <-chan Packet - out map[state.PeerID]chan<- Packet -} - -func NewChannelTransport(offline int, in <-chan Packet) *ChannelTransport { - return &ChannelTransport{ - offline: offline, - in: in, - out: make(map[state.PeerID]chan<- Packet), - } -} - -func (t *ChannelTransport) AddOutput(id state.PeerID, c chan<-Packet) { - t.out[id] = c -} - -func (t *ChannelTransport) Watch() Packet { - return <-t.in -} - -func (t *ChannelTransport) Send(group state.GroupID, sender state.PeerID, peer state.PeerID, payload protobuf.Payload) error { - // @todo we can do this better, we put node onlineness into a goroutine where we just stop the nodes for x seconds - // outside of this class - math.Seed(time.Now().UnixNano()) - if math.Intn(100) < t.offline { - return nil - } - - c, ok := t.out[peer] - if !ok { - return errors.New("peer unknown") - } - - c <- Packet{Group: group, Sender: sender, Payload: payload} - return nil -} \ No newline at end of file diff --git a/vendor/github.com/vacp2p/mvds/transport/transport.go b/vendor/github.com/vacp2p/mvds/transport/transport.go deleted file mode 100644 index de26e3e..0000000 --- a/vendor/github.com/vacp2p/mvds/transport/transport.go +++ /dev/null @@ -1,19 +0,0 @@ -// Package transport contains transport related logic for MVDS. -package transport - -import ( - "github.com/vacp2p/mvds/protobuf" - "github.com/vacp2p/mvds/state" -) - -type Packet struct { - Group state.GroupID - Sender state.PeerID - Payload protobuf.Payload -} - -// Transport defines an interface allowing for agnostic transport implementations. -type Transport interface { - Watch() Packet - Send(group state.GroupID, sender state.PeerID, peer state.PeerID, payload protobuf.Payload) error -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 099a9d5..dc8da9e 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -7,16 +7,16 @@ github.com/ethereum/go-ethereum/common/math github.com/ethereum/go-ethereum/crypto/secp256k1 github.com/ethereum/go-ethereum/rlp github.com/ethereum/go-ethereum/common/hexutil +# github.com/golang/mock v1.3.1 +github.com/golang/mock/gomock # github.com/golang/protobuf v1.3.2 github.com/golang/protobuf/proto # github.com/pkg/errors v0.8.1 github.com/pkg/errors -# github.com/vacp2p/mvds v0.0.0-20190718214327-b8e366e4e634 -github.com/vacp2p/mvds/node +# github.com/vacp2p/mvds v0.0.20 github.com/vacp2p/mvds/protobuf github.com/vacp2p/mvds/state github.com/vacp2p/mvds/store -github.com/vacp2p/mvds/transport # golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f golang.org/x/crypto/sha3 # golang.org/x/sys v0.0.0-20190412213103-97732733099d