diff --git a/commonspace/spaceutils_test.go b/commonspace/spaceutils_test.go index f19ce78f..56e1f2f0 100644 --- a/commonspace/spaceutils_test.go +++ b/commonspace/spaceutils_test.go @@ -3,6 +3,9 @@ package commonspace import ( "context" "fmt" + "testing" + "time" + accountService "github.com/anyproto/any-sync/accountservice" "github.com/anyproto/any-sync/app" "github.com/anyproto/any-sync/app/ocache" @@ -22,8 +25,6 @@ import ( "github.com/anyproto/any-sync/testutil/accounttest" "github.com/anyproto/go-chash" "github.com/stretchr/testify/require" - "testing" - "time" ) // @@ -103,6 +104,14 @@ func (m *mockConf) CoordinatorPeers() []string { return nil } +func (m *mockConf) NamingNodePeers() []string { + return nil +} + +func (m *mockConf) PaymentProcessingNodePeers() []string { + return nil +} + func (m *mockConf) PeerAddresses(peerId string) (addrs []string, ok bool) { if peerId == m.configuration.Nodes[0].PeerId { return m.configuration.Nodes[0].Addresses, true diff --git a/nodeconf/config.go b/nodeconf/config.go index 1d117d88..3caea78e 100644 --- a/nodeconf/config.go +++ b/nodeconf/config.go @@ -24,7 +24,9 @@ const ( NodeTypeConsensus NodeType = "consensus" NodeTypeFile NodeType = "file" - NodeTypeCoordinator NodeType = "coordinator" + NodeTypeCoordinator NodeType = "coordinator" + NodeTypeNamingNode NodeType = "namingNode" + NodeTypePaymentProcessingNode NodeType = "paymentProcessingNode" ) type Node struct { diff --git a/nodeconf/mock_nodeconf/mock_nodeconf.go b/nodeconf/mock_nodeconf/mock_nodeconf.go index d9c2fa0d..2f6b26a4 100644 --- a/nodeconf/mock_nodeconf/mock_nodeconf.go +++ b/nodeconf/mock_nodeconf/mock_nodeconf.go @@ -181,6 +181,20 @@ func (mr *MockServiceMockRecorder) Name() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Name", reflect.TypeOf((*MockService)(nil).Name)) } +// NamingNodePeers mocks base method. +func (m *MockService) NamingNodePeers() []string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NamingNodePeers") + ret0, _ := ret[0].([]string) + return ret0 +} + +// NamingNodePeers indicates an expected call of NamingNodePeers. +func (mr *MockServiceMockRecorder) NamingNodePeers() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NamingNodePeers", reflect.TypeOf((*MockService)(nil).NamingNodePeers)) +} + // NetworkCompatibilityStatus mocks base method. func (m *MockService) NetworkCompatibilityStatus() nodeconf.NetworkCompatibilityStatus { m.ctrl.T.Helper() @@ -237,6 +251,20 @@ func (mr *MockServiceMockRecorder) Partition(arg0 any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Partition", reflect.TypeOf((*MockService)(nil).Partition), arg0) } +// PaymentProcessingNodePeers mocks base method. +func (m *MockService) PaymentProcessingNodePeers() []string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "PaymentProcessingNodePeers") + ret0, _ := ret[0].([]string) + return ret0 +} + +// PaymentProcessingNodePeers indicates an expected call of PaymentProcessingNodePeers. +func (mr *MockServiceMockRecorder) PaymentProcessingNodePeers() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PaymentProcessingNodePeers", reflect.TypeOf((*MockService)(nil).PaymentProcessingNodePeers)) +} + // PeerAddresses mocks base method. func (m *MockService) PeerAddresses(arg0 string) ([]string, bool) { m.ctrl.T.Helper() diff --git a/nodeconf/nodeconf.go b/nodeconf/nodeconf.go index 444f9f67..06c5b55c 100644 --- a/nodeconf/nodeconf.go +++ b/nodeconf/nodeconf.go @@ -1,8 +1,9 @@ package nodeconf import ( - "github.com/anyproto/go-chash" "strings" + + "github.com/anyproto/go-chash" ) type NodeConf interface { @@ -20,6 +21,11 @@ type NodeConf interface { ConsensusPeers() []string // CoordinatorPeers returns list of coordinator nodes CoordinatorPeers() []string + // Please see any-ns-node repo for details + // Usually one network has only 1 naming node, but we support array of NNs + NamingNodePeers() []string + // Please see any-pp-node repo for details + PaymentProcessingNodePeers() []string // PeerAddresses returns peer addresses by peer id PeerAddresses(peerId string) (addrs []string, ok bool) // CHash returns nodes consistent table @@ -31,15 +37,17 @@ type NodeConf interface { } type nodeConf struct { - id string - accountId string - filePeers []string - consensusPeers []string - coordinatorPeers []string - chash chash.CHash - allMembers []Node - c Configuration - addrs map[string][]string + id string + accountId string + filePeers []string + consensusPeers []string + coordinatorPeers []string + namingNodePeers []string + paymentProcessingNodePeers []string + chash chash.CHash + allMembers []Node + c Configuration + addrs map[string][]string } func (c *nodeConf) Id() string { @@ -82,6 +90,14 @@ func (c *nodeConf) CoordinatorPeers() []string { return c.coordinatorPeers } +func (c *nodeConf) NamingNodePeers() []string { + return c.namingNodePeers +} + +func (c *nodeConf) PaymentProcessingNodePeers() []string { + return c.paymentProcessingNodePeers +} + func (c *nodeConf) PeerAddresses(peerId string) (addrs []string, ok bool) { addrs, ok = c.addrs[peerId] if ok && len(addrs) == 0 { @@ -113,3 +129,48 @@ func ReplKey(spaceId string) (replKey string) { } return spaceId } + +func ConfigurationToNodeConf(c Configuration) (nc *nodeConf, err error) { + nc = &nodeConf{ + id: c.Id, + c: c, + + // WARN: do not forget to set it later, Configuration does not feature it + //accountId: s.accountId, + + addrs: map[string][]string{}, + } + if nc.chash, err = chash.New(chash.Config{ + PartitionCount: PartitionCount, + ReplicationFactor: ReplicationFactor, + }); err != nil { + return + } + + members := make([]chash.Member, 0, len(c.Nodes)) + for _, n := range c.Nodes { + if n.HasType(NodeTypeTree) { + members = append(members, n) + } + if n.HasType(NodeTypeConsensus) { + nc.consensusPeers = append(nc.consensusPeers, n.PeerId) + } + if n.HasType(NodeTypeFile) { + nc.filePeers = append(nc.filePeers, n.PeerId) + } + if n.HasType(NodeTypeCoordinator) { + nc.coordinatorPeers = append(nc.coordinatorPeers, n.PeerId) + } + if n.HasType(NodeTypeNamingNode) { + nc.namingNodePeers = append(nc.namingNodePeers, n.PeerId) + } + if n.HasType(NodeTypePaymentProcessingNode) { + nc.paymentProcessingNodePeers = append(nc.paymentProcessingNodePeers, n.PeerId) + } + + nc.allMembers = append(nc.allMembers, n) + nc.addrs[n.PeerId] = n.Addresses + } + err = nc.chash.AddMembers(members...) + return +} diff --git a/nodeconf/nodeconf_test.go b/nodeconf/nodeconf_test.go index 6c614bbf..69efb9cb 100644 --- a/nodeconf/nodeconf_test.go +++ b/nodeconf/nodeconf_test.go @@ -2,11 +2,15 @@ package nodeconf import ( "fmt" + "math/rand" + "testing" + "github.com/anyproto/go-chash" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "math/rand" - "testing" + "go.uber.org/zap" + + "gopkg.in/yaml.v3" ) func TestReplKey(t *testing.T) { @@ -60,6 +64,107 @@ func TestConfiguration_NodeIds(t *testing.T) { } +func TestNodeConf_NamingNodePeers(t *testing.T) { + ch, err := chash.New(chash.Config{ + PartitionCount: PartitionCount, + ReplicationFactor: ReplicationFactor, + }) + require.NoError(t, err) + + conf := &nodeConf{ + id: "last", + accountId: "1", + chash: ch, + namingNodePeers: []string{ + "naming-node-1", + "naming-node-2", + "naming-node-3", + }, + } + + assert.Equal(t, []string{"naming-node-1", "naming-node-2", "naming-node-3"}, conf.NamingNodePeers()) +} + +func TestNodeConf_PaymentProcessingNodePeers(t *testing.T) { + ch, err := chash.New(chash.Config{ + PartitionCount: PartitionCount, + ReplicationFactor: ReplicationFactor, + }) + require.NoError(t, err) + + conf := &nodeConf{ + id: "last", + accountId: "1", + chash: ch, + paymentProcessingNodePeers: []string{ + "payment-processing-node-1", + "payment-processing-node-2", + "payment-processing-node-3", + }, + } + + assert.Equal(t, []string{"payment-processing-node-1", "payment-processing-node-2", "payment-processing-node-3"}, conf.PaymentProcessingNodePeers()) +} + +func TestNewNodeConfFromYaml(t *testing.T) { + yamlData := ` +id: 64ba63209976be4a733bbb91 +networkId: N4Gvo3v5wL31RrYgX3PrhAGMYvdWe5rAgtVB8cZySYWrkhb6 +nodes: + - peerId: 12D3KooWA8EXV3KjBxEU5EnsPfneLx84vMWAtTBQBeyooN82KSuS + addresses: + - 127.0.0.1:4830 + types: + - coordinator + - peerId: 12D3KooWA8EXV3KjBxEU5EnsPfneLx84vMWAtTBQBeyooN82KSuS + addresses: + - 127.0.0.1:4830 + types: + - namingNode + - peerId: 12D3KooXXXEXV3KjBxEU5EnsPfneLx84vMWAtTBQBeyooN82KSuS + addresses: + - 127.0.0.1:4830 + types: + - paymentProcessingNode + - peerId: 12D3KooYYYEXV3KjBxEU5EnsPfneLx84vMWAtTBQBeyooN82KSuS + addresses: + - 127.0.0.1:4830 + types: + - file + - peerId: consensus1 + addresses: + - 127.0.0.1:4830 + types: + - consensus + - peerId: consensus2 + addresses: + - 127.0.0.1:4830 + types: + - consensus +creationTime: 2023-07-21T11:51:12.970882+01:00 +` + + var conf Configuration + err := yaml.Unmarshal([]byte(yamlData), &conf) + require.NoError(t, err) + + log.Info("conf", zap.Any("conf", conf)) + + nodeConf, err := ConfigurationToNodeConf(conf) + require.NoError(t, err) + + assert.Equal(t, "64ba63209976be4a733bbb91", nodeConf.Id()) + assert.Equal(t, "N4Gvo3v5wL31RrYgX3PrhAGMYvdWe5rAgtVB8cZySYWrkhb6", nodeConf.c.NetworkId) + + // should not be set by ConfigurationToNodeConf + assert.Equal(t, "", nodeConf.accountId) + assert.Equal(t, []string{"12D3KooYYYEXV3KjBxEU5EnsPfneLx84vMWAtTBQBeyooN82KSuS"}, nodeConf.FilePeers()) + assert.Equal(t, []string{"consensus1", "consensus2"}, nodeConf.ConsensusPeers()) + assert.Equal(t, []string{"12D3KooWA8EXV3KjBxEU5EnsPfneLx84vMWAtTBQBeyooN82KSuS"}, nodeConf.CoordinatorPeers()) + assert.Equal(t, []string{"12D3KooWA8EXV3KjBxEU5EnsPfneLx84vMWAtTBQBeyooN82KSuS"}, nodeConf.NamingNodePeers()) + assert.Equal(t, []string{"12D3KooXXXEXV3KjBxEU5EnsPfneLx84vMWAtTBQBeyooN82KSuS"}, nodeConf.PaymentProcessingNodePeers()) +} + type testMember string func (t testMember) Id() string { diff --git a/nodeconf/service.go b/nodeconf/service.go index db6ba1ed..982a826d 100644 --- a/nodeconf/service.go +++ b/nodeconf/service.go @@ -3,6 +3,8 @@ package nodeconf import ( "context" + "sync" + commonaccount "github.com/anyproto/any-sync/accountservice" "github.com/anyproto/any-sync/app" "github.com/anyproto/any-sync/app/logger" @@ -11,7 +13,6 @@ import ( "github.com/anyproto/any-sync/util/periodicsync" "github.com/anyproto/go-chash" "go.uber.org/zap" - "sync" ) const CName = "common.nodeconf" @@ -117,39 +118,12 @@ func (s *service) setLastConfiguration(c Configuration) (err error) { return } - nc := &nodeConf{ - id: c.Id, - c: c, - accountId: s.accountId, - addrs: map[string][]string{}, - } - if nc.chash, err = chash.New(chash.Config{ - PartitionCount: PartitionCount, - ReplicationFactor: ReplicationFactor, - }); err != nil { + nc, err := ConfigurationToNodeConf(c) + if err != nil { return } + nc.accountId = s.accountId - members := make([]chash.Member, 0, len(c.Nodes)) - for _, n := range c.Nodes { - if n.HasType(NodeTypeTree) { - members = append(members, n) - } - if n.HasType(NodeTypeConsensus) { - nc.consensusPeers = append(nc.consensusPeers, n.PeerId) - } - if n.HasType(NodeTypeFile) { - nc.filePeers = append(nc.filePeers, n.PeerId) - } - if n.HasType(NodeTypeCoordinator) { - nc.coordinatorPeers = append(nc.coordinatorPeers, n.PeerId) - } - nc.allMembers = append(nc.allMembers, n) - nc.addrs[n.PeerId] = n.Addresses - } - if err = nc.chash.AddMembers(members...); err != nil { - return - } var beforeId = "" if s.last != nil { beforeId = s.last.Id() @@ -220,6 +194,18 @@ func (s *service) CoordinatorPeers() []string { return s.last.CoordinatorPeers() } +func (s *service) NamingNodePeers() []string { + s.mu.RLock() + defer s.mu.RUnlock() + return s.last.NamingNodePeers() +} + +func (s *service) PaymentProcessingNodePeers() []string { + s.mu.RLock() + defer s.mu.RUnlock() + return s.last.PaymentProcessingNodePeers() +} + func (s *service) PeerAddresses(peerId string) ([]string, bool) { s.mu.RLock() defer s.mu.RUnlock()