diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b29e09218b488027b32ab2402fcb3c600fd633bb..6646077df5fe882f78f3467cc6448a32307fc649 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -9,7 +9,7 @@ cache: variables: REPO_DIR: gitlab.com/elixxir REPO_NAME: registration - DOCKER_IMAGE: elixxirlabs/cuda-go:latest + DOCKER_IMAGE: elixxirlabs/cuda-go:go1.13-cuda11.1-mc MIN_CODE_COVERAGE: "0.0" before_script: @@ -77,11 +77,19 @@ build: - tags script: - mkdir -p release + - rm -rf upload || true - GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags '-w -s' ./... - GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags '-w -s' -o release/registration.linux64 main.go + - GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -tags stateless -trimpath -ldflags '-w -s' -o release/registration.stateless.linux64 main.go - GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -ldflags '-w -s' -o release/registration.win64 main.go + - GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -tags stateless -trimpath -ldflags '-w -s' -o release/registration.stateless.win64 main.go - GOOS=windows GOARCH=386 CGO_ENABLED=0 go build -ldflags '-w -s' -o release/registration.win32 main.go + - GOOS=windows GOARCH=386 CGO_ENABLED=0 go build -tags stateless -trimpath -ldflags '-w -s' -o release/registration.stateless.win32 main.go - GOOS=darwin GOARCH=amd64 CGO_ENABLED=0 go build -ldflags '-w -s' -o release/registration.darwin64 main.go + - GOOS=darwin GOARCH=amd64 CGO_ENABLED=0 go build -tags stateless -trimpath -ldflags '-w -s' -o release/registration.stateless.darwin64 main.go + - mkdir -p upload + - mv release/registration.stateless.* upload/ + - /upload-artifacts.sh upload/ artifacts: paths: - release/ diff --git a/Makefile b/Makefile index 86491db6d4e21f04eabcc8c459b22012d683b24e..37f286586e0ae2aeeb980466bafe7dd933d0c27c 100644 --- a/Makefile +++ b/Makefile @@ -20,12 +20,14 @@ build: update_release: GOFLAGS="" go get -u gitlab.com/elixxir/primitives@release + GOFLAGS="" go get -u gitlab.com/xx_network/crypto@release GOFLAGS="" go get -u gitlab.com/elixxir/crypto@release GOFLAGS="" go get -u gitlab.com/xx_network/comms@release GOFLAGS="" go get -u gitlab.com/elixxir/comms@release update_master: GOFLAGS="" go get -u gitlab.com/elixxir/primitives@master + GOFLAGS="" go get -u gitlab.com/xx_network/crypto@master GOFLAGS="" go get -u gitlab.com/elixxir/crypto@master GOFLAGS="" go get -u gitlab.com/xx_network/comms@master GOFLAGS="" go get -u gitlab.com/elixxir/comms@master diff --git a/README.md b/README.md index dfa1a133efeab6f25f5b635c0eea57cab589cb3a..ee04257af4df339150df7c489796a959eb81731a 100644 --- a/README.md +++ b/README.md @@ -10,29 +10,33 @@ cMix # Permissioning Server Configuration # ================================== -# Log message level +# Log message level (0 = info, 1 = debug, >1 = trace) logLevel: 1 + # Path to log file logPath: "registration.log" + # Path to the node topology permissioning info ndfOutputPath: "ndf.json" -# Minimum number of nodes to begin running rounds. this differs from the number of members -# in a team because some scheduling algorithms may require multiple teams worth of nodes at minimum + +# Minimum number of nodes to begin running rounds. This differs from the number +# of members in a team because some scheduling algorithms may require multiple +# teams worth of nodes at minimum. minimumNodes: 3 -# Path to the file containing the round ID -roundIdPath: "roundId.txt" +# "Location of the user discovery contact file. +udContactPath: "udContact.bin" -# Path to the file containing the update ID -updateIdPath: "updateId.txt" +# Path to UDB cert file +udbCertPath: "udb.crt" -# UDB ID -udbID: 1 +# Address for UDB +udbAddress: "1.2.3.4:11420" # Public address, used in NDF it gives to client -publicAdress: "0.0.0.0:11420" +publicAddress: "0.0.0.0:11420" -# The listening port of this server +# The listening port of this server port: 11420 # The minimum version required of gateways to connect @@ -52,7 +56,7 @@ dbAddress: "" # Path to JSON file with list of Node registration codes (in order of network # placement) -RegCodesFilePath: "regCodes.json" +regCodesFilePath: "regCodes.json" # List of client codes to be added to the database (for testing) clientRegCodes: @@ -64,8 +68,7 @@ clientRegCodes: # Client version (will allow all versions with major version 0) clientVersion: "0.0.0" -# The duration between polling the disabled Node list for updates. Optional. -# Defaults to 1m. +# The duration between polling the disabled Node list for updates (Default 1m) disabledNodesPollDuration: 1m # Path to the text file with a list of IDs of disabled Nodes. If no path is, @@ -81,8 +84,7 @@ certPath: "" # Time interval (in seconds) between committing Node statistics to storage nodeMetricInterval: 180 -# Time interval (in minutes) in which the database is -# checked for banned nodes +# Time interval (in minutes) in which the database is checked for banned nodes BanTrackerInterval: "3" # E2E/CMIX Primes @@ -94,20 +96,27 @@ groups: prime: "${e2e_prime}" generator: "${e2e_generator}" -# Selection of scheduling algorithm to use. Options are: -# simple - Schedules multiple teams to maximize performance, does not randomly re-arrange teams, if only a single -# only scheduling a single team, will use numerical ordering data for AlphaNet -# secure - Schedules new teams randomly, has appropriate buffers to ensure -# unpredictability, designed for BetaNet -schedulingAlgorithm: "single" - # Path to file with config for scheduling algorithm within the user directory schedulingConfigPath: "Scheduling_Simple_NonRandom.json" -# Time that the registration server waits before timing out while killing the round scheduling thread +# Time that the registration server waits before timing out while killing the +# round scheduling thread schedulingKillTimeout: 10s # Time the registration waits for rounds to close out and stop (optional) closeTimeout: 60s + +# Address of the notification server +nsAddress: "" +# Path to certificate for the notification server +nsCertPath: "" + +# Maximum number of connections per period +userRegCapacity: 1000 +# How often the number of connections is reset +userRegLeakPeriod: "24h" + +# The size of the address space used for ephemeral IDs +addressSpace: 10 ``` ### SchedulingConfig template: diff --git a/cmd/bannedNodeTracker_test.go b/cmd/bannedNodeTracker_test.go index dc278f5e9c38c6ccb3f2cb444c959393b1ec753e..9ca3e37a2700738d99979bc52b2f6251f05960e1 100644 --- a/cmd/bannedNodeTracker_test.go +++ b/cmd/bannedNodeTracker_test.go @@ -8,11 +8,11 @@ package cmd import ( "crypto/rand" "fmt" - "gitlab.com/elixxir/crypto/signature/rsa" - "gitlab.com/elixxir/primitives/id" - "gitlab.com/elixxir/primitives/ndf" "gitlab.com/elixxir/registration/storage" "gitlab.com/elixxir/registration/storage/node" + "gitlab.com/xx_network/crypto/signature/rsa" + "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/ndf" "sync" "testing" ) @@ -28,7 +28,7 @@ func TestBannedNodeTracker(t *testing.T) { // Build network state privKey, _ := rsa.GenerateKey(rand.Reader, 2048) - testState, err := storage.NewState(privKey, "", "") + testState, err := storage.NewState(privKey, 8) impl := &RegistrationImpl{ State: testState, NDFLock: sync.Mutex{}, @@ -64,12 +64,10 @@ func TestBannedNodeTracker(t *testing.T) { } // Clean out banned nodes - fmt.Println("1") err = BannedNodeTracker(impl) if err != nil { t.Errorf("Error with node tracker: %v", err) } - fmt.Println("2") updatedDef := testState.GetFullNdf().Get() if len(updatedDef.Nodes) != 1 { diff --git a/cmd/impl.go b/cmd/impl.go index 94c060bd15d1354565f1a56b5e287ec79e3c3333..942063ece60a2da42c9ccd88c8bb25961ccce054 100644 --- a/cmd/impl.go +++ b/cmd/impl.go @@ -14,43 +14,40 @@ import ( jww "github.com/spf13/jwalterweatherman" pb "gitlab.com/elixxir/comms/mixmessages" "gitlab.com/elixxir/comms/registration" - "gitlab.com/elixxir/crypto/signature/rsa" - "gitlab.com/elixxir/crypto/tls" - "gitlab.com/elixxir/primitives/id" - "gitlab.com/elixxir/primitives/ndf" - "gitlab.com/elixxir/primitives/utils" - "gitlab.com/elixxir/primitives/version" "gitlab.com/elixxir/registration/storage" "gitlab.com/elixxir/registration/storage/node" "gitlab.com/xx_network/comms/connect" + "gitlab.com/xx_network/crypto/signature/rsa" + "gitlab.com/xx_network/crypto/tls" + "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/ndf" + "gitlab.com/xx_network/primitives/rateLimiting" + "gitlab.com/xx_network/primitives/utils" "sync" "time" ) -//generally large buffer, should be roughly as many nodes as are expected -const nodeCompletionChanLen = 1000 - // The main registration instance object type RegistrationImpl struct { - Comms *registration.Comms - params *Params - State *storage.NetworkState - Stopped *uint32 - permissioningCert *x509.Certificate - ndfOutputPath string - NdfReady *uint32 - certFromFile string - registrationsRemaining *uint64 - maxRegistrationAttempts uint64 - disableGatewayPing bool + Comms *registration.Comms + params *Params + State *storage.NetworkState + Stopped *uint32 + permissioningCert *x509.Certificate + ndfOutputPath string + NdfReady *uint32 + certFromFile string + registrationLimiting *rateLimiting.Bucket + disableGatewayPing bool //registration status trackers numRegistered int //FIXME: it is possible that polling lock and registration lock // do the same job and could conflict. reconsiderations of this logic // may be fruitful - registrationLock sync.Mutex - beginScheduling chan struct{} + registrationLock sync.Mutex + beginScheduling chan struct{} + registrationTimes map[id.ID]int64 NDFLock sync.Mutex } @@ -58,49 +55,10 @@ type RegistrationImpl struct { //function used to schedule nodes type SchedulingAlgorithm func(params []byte, state *storage.NetworkState) error -// Params object for reading in configuration data -type Params struct { - Address string - CertPath string - KeyPath string - NdfOutputPath string - NsCertPath string - NsAddress string - cmix ndf.Group - e2e ndf.Group - publicAddress string - maxRegistrationAttempts uint64 - registrationCountDuration time.Duration - schedulingKillTimeout time.Duration - closeTimeout time.Duration - minimumNodes uint32 - udbId []byte - minGatewayVersion version.Version - minServerVersion version.Version - roundIdPath string - updateIdPath string - disableGatewayPing bool -} - -// toGroup takes a group represented by a map of string to string, -// then uses the prime and generator to create an ndf group object. -func toGroup(grp map[string]string) (*ndf.Group, error) { - jww.DEBUG.Printf("Group is: %v", grp) - pStr, pOk := grp["prime"] - gStr, gOk := grp["generator"] - - if !gOk || !pOk { - return nil, errors.Errorf("Invalid Group Config "+ - "(prime: %v, generator: %v", pOk, gOk) - } - return &ndf.Group{Prime: pStr, Generator: gStr}, nil -} - // Configure and start the Permissioning Server -func StartRegistration(params Params, done chan bool) (*RegistrationImpl, error) { +func StartRegistration(params Params) (*RegistrationImpl, error) { // Initialize variables - regRemaining := uint64(0) ndfReady := uint32(0) roundCreationStopped := uint32(0) @@ -117,33 +75,27 @@ func StartRegistration(params Params, done chan bool) (*RegistrationImpl, error) "PermissioningKey is %+v", err, pk) } - //initilize the state tracking object - state, err := storage.NewState(pk, params.roundIdPath, params.updateIdPath) + // Initialize the state tracking object + state, err := storage.NewState(pk, params.addressSpace) if err != nil { return nil, err } // Build default parameters regImpl := &RegistrationImpl{ - State: state, - params: ¶ms, - maxRegistrationAttempts: params.maxRegistrationAttempts, - registrationsRemaining: ®Remaining, - ndfOutputPath: params.NdfOutputPath, - NdfReady: &ndfReady, - Stopped: &roundCreationStopped, - + State: state, + params: ¶ms, + ndfOutputPath: params.NdfOutputPath, + NdfReady: &ndfReady, + Stopped: &roundCreationStopped, numRegistered: 0, beginScheduling: make(chan struct{}, 1), disableGatewayPing: params.disableGatewayPing, + registrationTimes: make(map[id.ID]int64), } - // Create timer and channel to be used by routine that clears the number of - // registrations every time the ticker activates - go func() { - ticker := time.NewTicker(params.registrationCountDuration) - regImpl.registrationCapacityRestRunner(ticker, done) - }() + //regImpl.registrationLimiting = rateLimiting.Create(params.userRegCapacity, params.userRegLeakRate) + regImpl.registrationLimiting = rateLimiting.CreateBucket(params.userRegCapacity, params.userRegCapacity, params.userRegLeakPeriod, func(u uint32, i int64) {}) if !noTLS { // Read in TLS keys from files @@ -162,6 +114,12 @@ func StartRegistration(params Params, done chan bool) (*RegistrationImpl, error) } + // Load the UDB cert from file + udbCert, err := utils.ReadFile(params.udbCertPath) + if err != nil { + return nil, errors.Errorf("failed to read UDB cert: %+v", err) + } + // Construct the NDF networkDef := &ndf.NetworkDefinition{ Registration: ndf.Registration{ @@ -170,13 +128,20 @@ func StartRegistration(params Params, done chan bool) (*RegistrationImpl, error) }, Timestamp: time.Now(), - UDB: ndf.UDB{ID: RegParams.udbId}, - E2E: RegParams.e2e, - CMIX: RegParams.cmix, + UDB: ndf.UDB{ + ID: RegParams.udbId, + Cert: string(udbCert), + Address: RegParams.udbAddress, + DhPubKey: RegParams.udbDhPubKey, + }, + E2E: RegParams.e2e, + CMIX: RegParams.cmix, // fixme: consider removing. this allows clients to remain agnostic of teaming order // by forcing team order == ndf order for simple non-random - Nodes: make([]ndf.Node, 0), - Gateways: make([]ndf.Gateway, 0), + Nodes: make([]ndf.Node, 0), + Gateways: make([]ndf.Gateway, 0), + AddressSpaceSize: params.addressSpace, + ClientVersion: RegParams.minClientVersion.String(), } // Assemble notification server information if configured @@ -235,8 +200,8 @@ func BannedNodeTracker(impl *RegistrationImpl) error { var newNodes []ndf.Node // Loop through NDF nodes to remove any that are banned - for i, node := range def.Nodes { - ndfNodeID, err := id.Unmarshal(node.ID) + for i, n := range def.Nodes { + ndfNodeID, err := id.Unmarshal(n.ID) if err != nil { return errors.WithMessage(err, "Failed to unmarshal node id from NDF") } @@ -284,23 +249,12 @@ func BannedNodeTracker(impl *RegistrationImpl) error { // NewImplementation returns a registration server Handler func NewImplementation(instance *RegistrationImpl) *registration.Implementation { impl := registration.NewImplementation() - impl.Functions.RegisterUser = func( - registrationCode, pubKey string) (signature []byte, err error) { - - response, err := instance.RegisterUser(registrationCode, pubKey) + impl.Functions.RegisterUser = func(regCode string, pubKey, receptionPubKey string) ([]byte, []byte, error) { + transmissionSig, receptionSig, err := instance.RegisterUser(regCode, pubKey, receptionPubKey) if err != nil { jww.ERROR.Printf("RegisterUser error: %+v", err) } - return response, err - } - - impl.Functions.GetCurrentClientVersion = func() (version string, err error) { - response, err := instance.GetCurrentClientVersion() - if err != nil { - jww.ERROR.Printf("GetCurrentClientVersion error: %+v", err) - } - - return response, err + return transmissionSig, receptionSig, err } impl.Functions.RegisterNode = func(salt []byte, serverAddr, serverTlsCert, gatewayAddr, gatewayTlsCert, registrationCode string) error { @@ -313,9 +267,9 @@ func NewImplementation(instance *RegistrationImpl) *registration.Implementation return err } - impl.Functions.PollNdf = func(theirNdfHash []byte, auth *connect.Auth) ([]byte, error) { + impl.Functions.PollNdf = func(theirNdfHash []byte) ([]byte, error) { - response, err := instance.PollNdf(theirNdfHash, auth) + response, err := instance.PollNdf(theirNdfHash) if err != nil && err.Error() != ndf.NO_NDF { jww.ERROR.Printf("PollNdf error: %+v", err) } @@ -323,9 +277,9 @@ func NewImplementation(instance *RegistrationImpl) *registration.Implementation return response, err } - impl.Functions.Poll = func(msg *pb.PermissioningPoll, auth *connect.Auth, serverAddress string) (*pb.PermissionPollResponse, error) { + impl.Functions.Poll = func(msg *pb.PermissioningPoll, auth *connect.Auth) (*pb.PermissionPollResponse, error) { //ensure a bad poll can not take down the permisisoning server - response, err := instance.Poll(msg, auth, serverAddress) + response, err := instance.Poll(msg, auth) return response, err } diff --git a/cmd/params.go b/cmd/params.go new file mode 100644 index 0000000000000000000000000000000000000000..0c845bf730e106ae419da9da724162975dbf38c2 --- /dev/null +++ b/cmd/params.go @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file // +/////////////////////////////////////////////////////////////////////////////// + +// Contains Params-related functionality + +package cmd + +import ( + "github.com/pkg/errors" + jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/primitives/version" + "gitlab.com/xx_network/primitives/ndf" + "time" +) + +// Params object for reading in configuration data +type Params struct { + Address string + CertPath string + KeyPath string + NdfOutputPath string + NsCertPath string + NsAddress string + cmix ndf.Group + e2e ndf.Group + publicAddress string + schedulingKillTimeout time.Duration + closeTimeout time.Duration + minimumNodes uint32 + udbId []byte + udbDhPubKey []byte + udbCertPath string + udbAddress string + minGatewayVersion version.Version + minServerVersion version.Version + minClientVersion version.Version + addressSpace uint32 + disableGatewayPing bool + // User registration can take userRegCapacity registrations in userRegLeakPeriod period of time + userRegCapacity uint32 + userRegLeakPeriod time.Duration +} + +// toGroup takes a group represented by a map of string to string, +// then uses the prime and generator to create an ndf group object. +func toGroup(grp map[string]string) (*ndf.Group, error) { + jww.DEBUG.Printf("Group is: %v", grp) + pStr, pOk := grp["prime"] + gStr, gOk := grp["generator"] + + if !gOk || !pOk { + return nil, errors.Errorf("Invalid Group Config "+ + "(prime: %v, generator: %v", pOk, gOk) + } + return &ndf.Group{Prime: pStr, Generator: gStr}, nil +} diff --git a/cmd/permissioning.go b/cmd/permissioning.go index bb95004134783167c6bfd4eeeff78d0025ef870c..de0b26fb0d8d149f96fc8d6bf233e5a46a1e9297 100644 --- a/cmd/permissioning.go +++ b/cmd/permissioning.go @@ -14,16 +14,15 @@ import ( "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/comms/mixmessages" - "gitlab.com/elixxir/crypto/signature/rsa" - "gitlab.com/elixxir/crypto/tls" - "gitlab.com/elixxir/crypto/xx" - "gitlab.com/elixxir/primitives/id" - "gitlab.com/elixxir/primitives/ndf" - "gitlab.com/elixxir/primitives/utils" "gitlab.com/elixxir/registration/storage" "gitlab.com/elixxir/registration/storage/node" "gitlab.com/xx_network/comms/connect" - "strconv" + "gitlab.com/xx_network/crypto/signature/rsa" + "gitlab.com/xx_network/crypto/tls" + "gitlab.com/xx_network/crypto/xx" + "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/ndf" + "gitlab.com/xx_network/primitives/utils" "sync/atomic" ) @@ -60,10 +59,20 @@ func (m *RegistrationImpl) CheckNodeRegistration(msg *mixmessages.RegisteredNode } +// An atomic counter for which regcode we're using next, when disableRegCodes is enabled +var curNodeReg = uint32(0) +var curNodeRegPtr = &curNodeReg + // Handle registration attempt by a Node func (m *RegistrationImpl) RegisterNode(salt []byte, serverAddr, serverTlsCert, gatewayAddr, gatewayTlsCert, registrationCode string) error { + // If disableRegCodes is set, we atomically increase curNodeReg and use the previous code in the sequence + if disableRegCodes { + regNum := atomic.AddUint32(curNodeRegPtr, 1) + registrationCode = regCodeInfos[regNum-1].RegCode + } + // Check that the node hasn't already been registered nodeInfo, err := storage.PermissioningDb.GetNode(registrationCode) if err != nil { @@ -91,7 +100,7 @@ func (m *RegistrationImpl) RegisterNode(salt []byte, serverAddr, serverTlsCert, // Ensure that generated ID matches stored ID // Ensure that salt is not already stored if !bytes.Equal(nodeInfo.Id, nodeId.Marshal()) { - return errors.Errorf("Submitted salt %+v does not match stored salt: %+v", salt, nodeInfo.Salt) + return errors.Errorf("Generated ID %+v does not match stored ID: %+v", nodeId.Marshal(), nodeInfo.Id) } else if len(nodeInfo.Salt) != 0 { return errors.Errorf( @@ -205,8 +214,7 @@ func (m *RegistrationImpl) completeNodeRegistration(regCode string) error { // Add the new node to the topology m.NDFLock.Lock() networkDef := m.State.GetFullNdf().Get() - gateway, n, order, err := assembleNdf(regCode) - + gateway, n, regTime, err := assembleNdf(regCode) if err != nil { m.NDFLock.Unlock() err := errors.Errorf("unable to assemble topology: %+v", err) @@ -214,18 +222,17 @@ func (m *RegistrationImpl) completeNodeRegistration(regCode string) error { return errors.Errorf("Could not complete registration: %+v", err) } - if order != -1 { - // fixme: consider removing. this allows clients to remain agnostic of teaming order - // by forcing team order == ndf order for simple non-random - if order >= len(networkDef.Nodes) { - appendNdf(networkDef, order) - } + nodeID, err := id.Unmarshal(n.ID) + if err != nil { + m.NDFLock.Unlock() + return errors.WithMessage(err, "Error parsing node ID") + } - networkDef.Gateways[order] = gateway - networkDef.Nodes[order] = n - } else { - networkDef.Gateways = append(networkDef.Gateways, gateway) - networkDef.Nodes = append(networkDef.Nodes, n) + m.registrationTimes[*nodeID] = regTime + err = m.insertNdf(networkDef, gateway, n, regTime) + if err != nil { + m.NDFLock.Unlock() + return errors.WithMessage(err, "Failed to insert nodes in definition") } // update the internal state with the newly-updated ndf @@ -273,8 +280,33 @@ func appendNdf(definition *ndf.NetworkDefinition, order int) { } +// Insert a node into the NDF, preserving ordering +func (m *RegistrationImpl) insertNdf(definition *ndf.NetworkDefinition, g ndf.Gateway, + n ndf.Node, regTime int64) error { + var i int + for i = 0; i < len(definition.Nodes); i++ { + nid, err := id.Unmarshal(definition.Nodes[i].ID) + if err != nil { + return errors.Errorf("Could not unmarshal ID from definition: %+v", err) + } + cmpTime := m.registrationTimes[*nid] + if regTime < cmpTime { + break + } + } + + if i == len(definition.Nodes) { + definition.Nodes = append(definition.Nodes, n) + definition.Gateways = append(definition.Gateways, g) + } else { + definition.Nodes = append(definition.Nodes[0:i], append([]ndf.Node{n}, definition.Nodes[i:]...)...) + definition.Gateways = append(definition.Gateways[0:i], append([]ndf.Gateway{g}, definition.Gateways[i:]...)...) + } + return nil +} + // Assemble information for the given registration code -func assembleNdf(code string) (ndf.Gateway, ndf.Node, int, error) { +func assembleNdf(code string) (ndf.Gateway, ndf.Node, int64, error) { // Get node information for each registration code nodeInfo, err := storage.PermissioningDb.GetNode(code) @@ -305,12 +337,7 @@ func assembleNdf(code string) (ndf.Gateway, ndf.Node, int, error) { TlsCertificate: nodeInfo.GatewayCertificate, } - order, err := strconv.Atoi(nodeInfo.Sequence) - if err != nil { - return gateway, n, -1, nil - } - - return gateway, n, order, nil + return gateway, n, nodeInfo.DateRegistered.UnixNano(), nil } // outputNodeTopologyToJSON encodes the NodeTopology structure to JSON and diff --git a/cmd/permissioning_test.go b/cmd/permissioning_test.go index eda3286f79611d71d5ad475812f503a0d399e2c9..a118f8b0993ec91d64bddafdce89bc92e87cb8b7 100644 --- a/cmd/permissioning_test.go +++ b/cmd/permissioning_test.go @@ -1,11 +1,11 @@ package cmd import ( - "gitlab.com/elixxir/primitives/id" - "gitlab.com/elixxir/primitives/utils" "gitlab.com/elixxir/registration/storage" "gitlab.com/elixxir/registration/storage/node" "gitlab.com/elixxir/registration/testkeys" + "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/utils" "testing" "time" ) @@ -34,38 +34,47 @@ func TestLoadAllRegisteredNodes(t *testing.T) { // Create a new ID and store a new active node into the database activeNodeId := id.NewIdFromUInt(0, id.Node, t) - err = storage.PermissioningDb.RegisterNode(activeNodeId, []byte("test"), "AAAA", "0.0.0.0", string(crt), + err = storage.PermissioningDb.RegisterNode(activeNodeId, []byte("test1"), "AAAA", "0.0.0.0", string(crt), "0.0.0.0", string(crt)) if err != nil { t.Error(err) } + time.Sleep(1) // Create a new ID and store a new *banned* node into the database bannedNodeId := id.NewIdFromUInt(1, id.Node, t) - err = storage.PermissioningDb.RegisterNode(bannedNodeId, []byte("test"), "BBBB", "0.0.0.0", string(crt), + err = storage.PermissioningDb.RegisterNode(bannedNodeId, []byte("test2"), "BBBB", "0.0.0.0", string(crt), "0.0.0.0", string(crt)) if err != nil { t.Error(err) } - permissioningMap := storage.PermissioningDb.NodeRegistration.(*storage.MapImpl) + time.Sleep(1) + + // Create a new ID and store a new *banned* node into the database + altNodeID := id.NewIdFromString("alt", id.Node, t) + err = storage.PermissioningDb.RegisterNode(altNodeID, []byte("test3"), "CCCC", "0.0.0.0", string(crt), + "0.0.0.0", string(crt)) + if err != nil { + t.Error(err) + } + + permissioningMap := storage.PermissioningDb.GetMapImpl(t) err = permissioningMap.BannedNode(bannedNodeId, t) if err != nil { t.Error(err) } //endregion - //region Test code // Create params for test registration server testParams := Params{ - CertPath: testkeys.GetCACertPath(), - KeyPath: testkeys.GetCAKeyPath(), - NdfOutputPath: testkeys.GetNDFPath(), - maxRegistrationAttempts: 5, - registrationCountDuration: time.Hour, + CertPath: testkeys.GetCACertPath(), + KeyPath: testkeys.GetCAKeyPath(), + NdfOutputPath: testkeys.GetNDFPath(), + udbCertPath: testkeys.GetUdbCertPath(), } - bc := make(chan bool, 1) + // Start registration server - impl, err := StartRegistration(testParams, bc) + impl, err := StartRegistration(testParams) if err != nil { t.Error(err) } @@ -95,34 +104,51 @@ func TestLoadAllRegisteredNodes(t *testing.T) { if !hmBannedNode.GetId().Cmp(bannedNodeId) { t.Error("Unexpected node ID for node 0:\r\tGot: %i\r\tExpected: %i", hmBannedNode.GetId(), bannedNodeId) } - //endregion //region Node map checking // Check that the nodes were added to the node map + expected_nodes := 3 nodeMapNodes := impl.State.GetNodeMap().GetNodeStates() - if len(nodeMapNodes) != 2 { - t.Errorf("Unexpected number of nodes found in node map:\r\tGot: %d\r"+ - "\tExpected: %d", len(nodeMapNodes), 2) + if len(nodeMapNodes) != expected_nodes { + t.Errorf("Unexpected number of nodes found in node map:\n\tGot: %d\n"+ + "\tExpected: %d", len(nodeMapNodes), expected_nodes) } - - if !nodeMapNodes[0].GetID().Cmp(activeNodeId) { - t.Errorf("Unexpected node ID for node 0:\r\tGot: %d\r\tExpected: %d", + def := impl.State.GetFullNdf().Get() + id0, err := id.Unmarshal(def.Nodes[0].ID) + if err != nil { + t.Error("Failed to unmarshal ID") + } + if !id0.Cmp(activeNodeId) { + t.Errorf("Unexpected node ID for node 0:\n\tGot: %d\n\tExpected: %d", nodeMapNodes[0].GetID(), activeNodeId) } - if !nodeMapNodes[1].GetID().Cmp(bannedNodeId) { - t.Errorf("Unexpected node ID for node 1:\r\tGot: %d\r\tExpected: %d", + id1, err := id.Unmarshal(def.Nodes[1].ID) + if err != nil { + t.Error("Failed to unmarshal ID") + } + if !id1.Cmp(bannedNodeId) { + t.Errorf("Unexpected node ID for node 1:\n\tGot: %d\n\tExpected: %d", nodeMapNodes[1].GetID(), bannedNodeId) } - if nodeMapNodes[0].GetStatus() != node.Active { - t.Errorf("Unexpected status for node 0:\r\tGot: %s\r\tExpected: %s", - nodeMapNodes[0].GetStatus().String(), node.Banned.String()) + id2, err := id.Unmarshal(def.Nodes[2].ID) + if err != nil { + t.Error("Failed to unmarshal ID") + } + if !id2.Cmp(altNodeID) { + t.Errorf("Unexpected node ID for node 2:\n\tGot: %d\n\tExpected: %d", + nodeMapNodes[2].GetID(), altNodeID) } - if nodeMapNodes[1].GetStatus() != node.Banned { - t.Errorf("Unexpected status for node 1:\r\tGot: %s\r\tExpected: %s", - nodeMapNodes[1].GetStatus().String(), node.Banned.String()) + banned := 0 + for _, n := range nodeMapNodes { + if n.GetStatus() == node.Banned { + banned++ + } + } + if banned != 1 { + t.Error("Should only be one banned node") } //endregion diff --git a/cmd/poll.go b/cmd/poll.go index 8ceb647b81161b95315d5d404c308c5928da50a7..be65a0c3048a19f2cb8f0866dff8afd8df85992f 100644 --- a/cmd/poll.go +++ b/cmd/poll.go @@ -13,25 +13,19 @@ import ( "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" pb "gitlab.com/elixxir/comms/mixmessages" - "gitlab.com/elixxir/crypto/signature" "gitlab.com/elixxir/primitives/current" - "gitlab.com/elixxir/primitives/id" - "gitlab.com/elixxir/primitives/ndf" "gitlab.com/elixxir/primitives/version" "gitlab.com/elixxir/registration/storage" "gitlab.com/elixxir/registration/storage/node" "gitlab.com/xx_network/comms/connect" - "net" + "gitlab.com/xx_network/comms/signature" + "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/ndf" "sync/atomic" ) -// The placeholder for the host in the Gateway address that is used to indicate -// to permissioning to replace it with the Node's host. -const gatewayReplaceIpPlaceholder = "CHANGE_TO_PUBLIC_IP" - // Server->Permissioning unified poll function -func (m *RegistrationImpl) Poll(msg *pb.PermissioningPoll, auth *connect.Auth, - serverAddress string) (*pb.PermissionPollResponse, error) { +func (m *RegistrationImpl) Poll(msg *pb.PermissioningPoll, auth *connect.Auth) (*pb.PermissionPollResponse, error) { // Initialize the response response := &pb.PermissionPollResponse{} @@ -69,11 +63,15 @@ func (m *RegistrationImpl) Poll(msg *pb.PermissioningPoll, auth *connect.Auth, activity := current.Activity(msg.Activity) - // Increment the Node's poll count - n.IncrementNumPolls() + // check that the activity is not error and then poll, do not count error + // polls so erring nodes are not issues rounds + if activity != current.ERROR { + // Increment the Node's poll count + n.IncrementNumPolls() + } - //update ip addresses if nessessary - err := checkIPAddresses(m, n, msg, auth.Sender, serverAddress) + // update ip addresses if necessary + err := checkIPAddresses(m, n, msg, auth.Sender) if err != nil { err = errors.WithMessage(err, "Failed to update IP addresses") return response, err @@ -153,19 +151,14 @@ func (m *RegistrationImpl) Poll(msg *pb.PermissioningPoll, auth *connect.Auth, if updateNotification.ToActivity == current.ERROR { updateNotification.Error = msg.Error } - err = m.State.SendUpdateNotification(updateNotification) - if err!=nil{ - jww.WARN.Printf("Failed to send update notification, " + - "is the update thread running?") - n.GetPollingLock().Unlock() - } + updateNotification.ClientErrors = msg.ClientErrors // Update occurred, report it to the control thread - return response, err + return response, m.State.SendUpdateNotification(updateNotification) } // PollNdf handles the client polling for an updated NDF -func (m *RegistrationImpl) PollNdf(theirNdfHash []byte, auth *connect.Auth) ([]byte, error) { +func (m *RegistrationImpl) PollNdf(theirNdfHash []byte) ([]byte, error) { // Ensure the NDF is ready to be returned regComplete := atomic.LoadUint32(m.NdfReady) @@ -173,27 +166,14 @@ func (m *RegistrationImpl) PollNdf(theirNdfHash []byte, auth *connect.Auth) ([]b return nil, errors.New(ndf.NO_NDF) } - // Handle client request - if !auth.IsAuthenticated || auth.Sender.IsDynamicHost() { - // Do not return NDF if client hash matches - if isSame := m.State.GetPartialNdf().CompareHash(theirNdfHash); isSame { - return nil, nil - } - - // Send the json of the client - jww.TRACE.Printf("Returning a new NDF to client!") - jww.TRACE.Printf("Sending the following ndf: %v", m.State.GetPartialNdf().Get()) - return m.State.GetPartialNdf().Get().Marshal() - } - // Do not return NDF if backend hash matches - if isSame := m.State.GetFullNdf().CompareHash(theirNdfHash); isSame { + if isSame := m.State.GetPartialNdf().CompareHash(theirNdfHash); isSame { return nil, nil } //Send the json of the ndf jww.TRACE.Printf("Returning a new NDF to a back-end server!") - return m.State.GetFullNdf().Get().Marshal() + return m.State.GetPartialNdf().Get().Marshal() } // checkVersion checks if the PermissioningPoll message server and gateway @@ -324,37 +304,11 @@ func verifyError(msg *pb.PermissioningPoll, n *node.State, m *RegistrationImpl) return nil } -// updateGatewayAdvertisedAddress checks if the Gateway's address host is set to -// gatewayReplaceIpPlaceholder. If it is, then it is replaced with the Node's -// host while retaining the Gateway's port. -func updateGatewayAdvertisedAddress(gatewayAddress, nodeAddress string) (string, error) { - if gatewayAddress == "" { - return gatewayAddress, nil - } - - gwAddr, gwPort, err := net.SplitHostPort(gatewayAddress) - if err != nil { - return "", errors.Errorf("Error parsing Gateway address: %v", err) - } - - if gwAddr == gatewayReplaceIpPlaceholder { - nAddr, _, err := net.SplitHostPort(nodeAddress) - if err != nil { - return "", errors.Errorf("Error parsing Node address: %v", err) - } +func checkIPAddresses(m *RegistrationImpl, n *node.State, + msg *pb.PermissioningPoll, nodeHost *connect.Host) error { - gatewayAddress = net.JoinHostPort(nAddr, gwPort) - } - - return gatewayAddress, nil -} - -func checkIPAddresses(m *RegistrationImpl, n *node.State, msg *pb.PermissioningPoll, nodeHost *connect.Host, nodeAddress string) error { - // Check if the Gateway address needs to be updated - gatewayAddress, err := updateGatewayAdvertisedAddress(msg.GatewayAddress, nodeAddress) - if err != nil { - return err - } + // Pull the addresses out of the message + gatewayAddress, nodeAddress := msg.GatewayAddress, msg.ServerAddress // Update server and gateway addresses in state, if necessary nodeUpdate := n.UpdateNodeAddresses(nodeAddress) @@ -366,11 +320,11 @@ func checkIPAddresses(m *RegistrationImpl, n *node.State, msg *pb.PermissioningP // If state required changes, then check the NDF if nodeUpdate || gatewayUpdate { - jww.TRACE.Printf("UPDATING gateway and node update: %s, %s", nodeAddress, + jww.TRACE.Printf("UPDATING gateway and node update: %s, %s", msg.ServerAddress, gatewayAddress) // Update address information in Storage - err = storage.PermissioningDb.UpdateNodeAddresses(nodeHost.GetId(), nodeAddress, gatewayAddress) + err := storage.PermissioningDb.UpdateNodeAddresses(nodeHost.GetId(), nodeAddress, gatewayAddress) if err != nil { return err } @@ -381,21 +335,21 @@ func checkIPAddresses(m *RegistrationImpl, n *node.State, msg *pb.PermissioningP if nodeUpdate { nodeHost.UpdateAddress(nodeAddress) n.SetConnectivity(node.PortUnknown) - if err = updateNdfNodeAddr(n.GetID(), nodeAddress, currentNDF); err != nil { + if err := updateNdfNodeAddr(n.GetID(), nodeAddress, currentNDF); err != nil { m.NDFLock.Unlock() return err } } if gatewayUpdate { - if err = updateNdfGatewayAddr(n.GetID(), gatewayAddress, currentNDF); err != nil { + if err := updateNdfGatewayAddr(n.GetID(), gatewayAddress, currentNDF); err != nil { m.NDFLock.Unlock() return err } } // Update the internal state with the newly-updated ndf - if err = m.State.UpdateNdf(currentNDF); err != nil { + if err := m.State.UpdateNdf(currentNDF); err != nil { m.NDFLock.Unlock() return err } diff --git a/cmd/poll_test.go b/cmd/poll_test.go index cb4a8b46510dbc12790b3a45c2e6ec6b51ebc8a4..9719726d4a982e236a00591ad03db0e40e826a82 100644 --- a/cmd/poll_test.go +++ b/cmd/poll_test.go @@ -11,19 +11,19 @@ import ( "fmt" pb "gitlab.com/elixxir/comms/mixmessages" "gitlab.com/elixxir/comms/registration" - "gitlab.com/elixxir/crypto/signature" - "gitlab.com/elixxir/crypto/signature/rsa" "gitlab.com/elixxir/primitives/current" - "gitlab.com/elixxir/primitives/id" - "gitlab.com/elixxir/primitives/ndf" "gitlab.com/elixxir/primitives/states" - "gitlab.com/elixxir/primitives/utils" "gitlab.com/elixxir/primitives/version" "gitlab.com/elixxir/registration/storage" "gitlab.com/elixxir/registration/storage/node" "gitlab.com/elixxir/registration/storage/round" "gitlab.com/elixxir/registration/testkeys" "gitlab.com/xx_network/comms/connect" + "gitlab.com/xx_network/comms/signature" + "gitlab.com/xx_network/crypto/signature/rsa" + "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/ndf" + "gitlab.com/xx_network/primitives/utils" "sync" "sync/atomic" "testing" @@ -44,7 +44,7 @@ func TestRegistrationImpl_Poll(t *testing.T) { testString := "test" // Start registration server testParams.KeyPath = testkeys.GetCAKeyPath() - impl, err := StartRegistration(testParams, nil) + impl, err := StartRegistration(testParams) if err != nil { t.Errorf("Unable to start registration: %+v", err) } @@ -103,7 +103,7 @@ func TestRegistrationImpl_Poll(t *testing.T) { n := impl.State.GetNodeMap().GetNode(testID) n.SetConnectivity(node.PortSuccessful) - response, err := impl.Poll(testMsg, testAuth, "0.0.0.0:11420") + response, err := impl.Poll(testMsg, testAuth) if err != nil { t.Errorf("Unexpected error polling: %+v", err) } @@ -135,7 +135,7 @@ func TestRegistrationImpl_PollNoNdf(t *testing.T) { } // Start registration server ndfReady := uint32(0) - state, err := storage.NewState(pk, "", "") + state, err := storage.NewState(pk, 8) if err != nil { t.Errorf("Unable to create state: %+v", err) } @@ -151,7 +151,7 @@ func TestRegistrationImpl_PollNoNdf(t *testing.T) { dummyMessage := &pb.PermissioningPoll{} - _, err = impl.Poll(dummyMessage, nil, "") + _, err = impl.Poll(dummyMessage, nil) if err == nil || err.Error() != ndf.NO_NDF { t.Errorf("Unexpected error polling: %+v", err) } @@ -163,7 +163,7 @@ func TestRegistrationImpl_PollFailAuth(t *testing.T) { // Start registration server ndfReady := uint32(1) - state, err := storage.NewState(getTestKey(), "", "") + state, err := storage.NewState(getTestKey(), 8) if err != nil { t.Errorf("Unable to create state: %+v", err) } @@ -194,7 +194,7 @@ func TestRegistrationImpl_PollFailAuth(t *testing.T) { dummyMessage := &pb.PermissioningPoll{} - _, err = impl.Poll(dummyMessage, testAuth, "0.0.0.0:11420") + _, err = impl.Poll(dummyMessage, testAuth) if err == nil || err.Error() != connect.AuthError(testAuth.Sender.GetId()).Error() { t.Errorf("Unexpected error polling: %+v", err) } @@ -218,12 +218,11 @@ func TestRegistrationImpl_PollNdf(t *testing.T) { storage.PopulateNodeRegistrationCodes(infos) RegParams = testParams - udbId := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4} - RegParams.udbId = udbId + udbId := id.NewIdFromUInt(5, id.User, t) + RegParams.udbId = udbId.Marshal() RegParams.minimumNodes = 3 - fmt.Println("-A") // Start registration server - impl, err := StartRegistration(RegParams, nil) + impl, err := StartRegistration(RegParams) if err != nil { t.Errorf(err.Error()) return @@ -269,20 +268,20 @@ func TestRegistrationImpl_PollNdf(t *testing.T) { } l.Lock() - observedNDFBytes, err := impl.PollNdf(nil, &connect.Auth{}) + observedNDFBytes, err := impl.PollNdf(nil) l.Unlock() if err != nil { t.Errorf("failed to update ndf: %v", err) } - observedNDF, _, err := ndf.DecodeNDF(string(observedNDFBytes)) + observedNDF, err := ndf.Unmarshal(observedNDFBytes) if err != nil { t.Errorf("Could not decode ndf: %v\nNdf output: %s", err, string(observedNDFBytes)) } fmt.Printf("\n\n\nndf: %v\n\n\n", observedNDF.Nodes) - if bytes.Compare(observedNDF.UDB.ID, udbId) != 0 { + if bytes.Compare(observedNDF.UDB.ID, udbId.Marshal()) != 0 { t.Errorf("Failed to set udbID. Expected: %v, \nRecieved: %v, \nNdf: %+v", udbId, observedNDF.UDB.ID, observedNDF) } @@ -311,12 +310,12 @@ func TestRegistrationImpl_PollNdf_NoNDF(t *testing.T) { storage.PopulateNodeRegistrationCodes(infos) RegParams = testParams //Setup udb configurations - udbId := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4} - RegParams.udbId = udbId + udbId := id.NewIdFromUInt(5, id.User, t) + RegParams.udbId = udbId.Marshal() RegParams.minimumNodes = 3 // Start registration server - impl, err := StartRegistration(testParams, nil) + impl, err := StartRegistration(testParams) if err != nil { t.Errorf(err.Error()) return @@ -335,7 +334,7 @@ func TestRegistrationImpl_PollNdf_NoNDF(t *testing.T) { //Make a client ndf hash that is not up to date clientNdfHash := []byte("test") - _, err = impl.PollNdf(clientNdfHash, &connect.Auth{}) + _, err = impl.PollNdf(clientNdfHash) if err == nil { t.Error("Expected error path, should not have an ndf ready") } @@ -358,7 +357,7 @@ func TestPoll_BannedNode(t *testing.T) { testString := "test" // Start registration server testParams.KeyPath = testkeys.GetCAKeyPath() - impl, err := StartRegistration(testParams, nil) + impl, err := StartRegistration(testParams) if err != nil { t.Errorf("Unable to start registration: %+v", err) } @@ -409,7 +408,7 @@ func TestPoll_BannedNode(t *testing.T) { t.Errorf(err.Error()) } - _, err = impl.Poll(testMsg, testAuth, "") + _, err = impl.Poll(testMsg, testAuth) if err != nil { return } @@ -892,7 +891,7 @@ func TestVerifyError(t *testing.T) { } // Start registration server ndfReady := uint32(0) - state, err := storage.NewState(pk, "", "") + state, err := storage.NewState(pk, 8) if err != nil { t.Errorf("Unable to create state: %+v", err) } @@ -945,7 +944,7 @@ func TestVerifyError(t *testing.T) { _ = nsm.AddNode(errNodeId, "", "", "", 0) n := nsm.GetNode(errNodeId) rsm := round.NewStateMap() - s, _ := rsm.AddRound(id.Round(0), 4, 5*time.Minute, connect.NewCircuit([]*id.ID{errNodeId})) + s, _ := rsm.AddRound(id.Round(0), 4, 8, 5*time.Minute, connect.NewCircuit([]*id.ID{errNodeId})) _ = n.SetRound(s) err = verifyError(msg, n, impl) @@ -953,42 +952,3 @@ func TestVerifyError(t *testing.T) { t.Error("Failed to verify error") } } - -// Tests that updateGatewayAdvertisedAddress() returns the gatewayAddress when -// no replacements need to be made. -func TestUpdateGatewayAdvertisedAddress(t *testing.T) { - gatewayAddress := "0.0.0.0:22840" - nodeAddress := "192.168.1.1:11420" - - testAddress, err := updateGatewayAdvertisedAddress(gatewayAddress, nodeAddress) - - if err != nil { - t.Errorf("updateGatewayAdvertisedAddress() produced an unexpected error."+ - "\n\texpected: %v\n\treceived: %v", nil, err) - } - - if testAddress != gatewayAddress { - t.Errorf("updateGatewayAdvertisedAddress() did not return the correct address."+ - "\n\texpected: %v\n\treceived: %v", gatewayAddress, testAddress) - } -} - -// Tests that updateGatewayAdvertisedAddress() returns the nodeAddress with the -// gatewayAddress port when the gatewayReplaceIpPlaceholder is used. -func TestUpdateGatewayAdvertisedAddress_Update(t *testing.T) { - gatewayAddress := gatewayReplaceIpPlaceholder + ":22840" - nodeAddress := "192.168.1.1:11420" - expectedAddress := "192.168.1.1:22840" - - testAddress, err := updateGatewayAdvertisedAddress(gatewayAddress, nodeAddress) - - if err != nil { - t.Errorf("updateGatewayAdvertisedAddress() produced an unexpected error."+ - "\n\texpected: %v\n\treceived: %v", nil, err) - } - - if testAddress != expectedAddress { - t.Errorf("updateGatewayAdvertisedAddress() did not return the correct address."+ - "\n\texpected: %v\n\treceived: %v", expectedAddress, testAddress) - } -} diff --git a/cmd/registration.go b/cmd/registration.go index 22257cbc461fd6c3ffd415ee3176ec437c347c7d..46bb05ee41f70c5f8d59e9043baafe75143f7790 100644 --- a/cmd/registration.go +++ b/cmd/registration.go @@ -9,88 +9,58 @@ package cmd import ( - "crypto" "crypto/rand" - "crypto/sha256" "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" - "gitlab.com/elixxir/crypto/signature/rsa" + "gitlab.com/elixxir/crypto/hash" "gitlab.com/elixxir/registration/storage" - "sync/atomic" - "time" + "gitlab.com/xx_network/crypto/signature/rsa" ) -const ( - defaultMaxRegistrationAttempts = uint64(500) - defaultRegistrationCountDuration = time.Hour * 24 -) +var rateLimitErr = errors.New("Too many client registrations. Try again later") // Handle registration attempt by a Client -func (m *RegistrationImpl) RegisterUser(registrationCode, pubKey string) ( - signature []byte, err error) { - - // Check for pre-existing registration for this public key +// Returns rsa signature and error +func (m *RegistrationImpl) RegisterUser(regCode string, pubKey string, receptionKey string) ([]byte, []byte, error) { + jww.INFO.Printf("RegisterUser %s", pubKey) + // Check for pre-existing registration for this public key first if user, err := storage.PermissioningDb.GetUser(pubKey); err == nil && user != nil { jww.INFO.Printf("Previous registration found for %s", pubKey) - } else { - // Check database to verify given registration code - jww.INFO.Printf("Attempting to use registration code %+v...", - registrationCode) - err = storage.PermissioningDb.UseCode(registrationCode) + } else if regCode != "" { + // Fail early for non-valid reg codes + err = storage.PermissioningDb.UseCode(regCode) if err != nil { - // Check if the max registration attempts have been reached - if atomic.LoadUint64(m.registrationsRemaining) >= m.maxRegistrationAttempts { - // Invalid registration code, return an error - return make([]byte, 0), errors.Errorf( - "Error validating registration code: %+v", err) - } else { - atomic.AddUint64(m.registrationsRemaining, 1) - jww.INFO.Printf("Incremented registration counter to %+v (max %v)", - atomic.LoadUint64(m.registrationsRemaining), m.maxRegistrationAttempts) - } - } - - // Record the user public key for duplicate registration support - err = storage.PermissioningDb.InsertUser(pubKey) - if err != nil { - jww.WARN.Printf("Unable to store user: %+v", - errors.New(err.Error())) + jww.INFO.Printf("RegisterUser error: %+v", err) + return nil, nil, err } + } else if regCode == "" && !m.registrationLimiting.Add(1) { + // Rate limited, fail early + jww.INFO.Printf("RegisterUser error: %+v", rateLimitErr) + return nil, nil, rateLimitErr } // Use hardcoded keypair to sign Client-provided public key //Create a hash, hash the pubKey and then truncate it - h := sha256.New() + h, _ := hash.NewCMixHash() h.Write([]byte(pubKey)) - data := h.Sum(nil) - sig, err := rsa.Sign(rand.Reader, m.State.GetPrivateKey(), crypto.SHA256, data, nil) + transmissionSig, err := rsa.Sign(rand.Reader, m.State.GetPrivateKey(), hash.CMixHash, h.Sum(nil), nil) if err != nil { - return make([]byte, 0), errors.Errorf( + jww.INFO.Printf("RegisterUser error: can't sign pubkey") + return make([]byte, 0), make([]byte, 0), errors.Errorf( "Unable to sign client public key: %+v", err) } - // Return signed public key to Client - jww.INFO.Printf("Registration for code %+v complete!", registrationCode) - return sig, nil -} - -// This has to be part of RegistrationImpl and has to return an error because -// of the way our comms are structured -func (m *RegistrationImpl) GetCurrentClientVersion() (version string, err error) { - clientVersionLock.RLock() - defer clientVersionLock.RUnlock() - return clientVersion, nil -} - -// registrationCapacityRestRunner sets the registrations remaining to zero when -// a ticker occurs. -func (m *RegistrationImpl) registrationCapacityRestRunner(ticker *time.Ticker, done chan bool) { - for { - select { - case <-done: - return - case <-ticker.C: - atomic.StoreUint64(m.registrationsRemaining, 0) - } + h.Reset() + h.Write([]byte(receptionKey)) + receptionSig, err := rsa.Sign(rand.Reader, m.State.GetPrivateKey(), hash.CMixHash, h.Sum(nil), nil) + // Record the user public key for duplicate registration support + err = storage.PermissioningDb.InsertUser(pubKey, receptionKey) + if err != nil { + jww.WARN.Printf("Unable to store user: %+v", + errors.New(err.Error())) } + + // Return signed public key to Client + jww.DEBUG.Printf("RegisterUser for code [%s] complete!", regCode) + return transmissionSig, receptionSig, nil } diff --git a/cmd/registration_test.go b/cmd/registration_test.go index 6959c6b4c6f8839252ea5f430303d8fe732a182f..2f03ae42be626d0b9a98dd3be92ba1c9f2495160 100644 --- a/cmd/registration_test.go +++ b/cmd/registration_test.go @@ -10,12 +10,12 @@ import ( jww "github.com/spf13/jwalterweatherman" pb "gitlab.com/elixxir/comms/mixmessages" nodeComms "gitlab.com/elixxir/comms/node" - "gitlab.com/elixxir/primitives/id" - "gitlab.com/elixxir/primitives/utils" "gitlab.com/elixxir/primitives/version" "gitlab.com/elixxir/registration/storage" "gitlab.com/elixxir/registration/storage/node" "gitlab.com/elixxir/registration/testkeys" + "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/utils" "os" "sync" "testing" @@ -62,18 +62,19 @@ func TestMain(m *testing.M) { } testParams = Params{ - Address: permAddr, - CertPath: testkeys.GetCACertPath(), - KeyPath: testkeys.GetCAKeyPath(), - NdfOutputPath: testkeys.GetNDFPath(), - publicAddress: permAddr, - maxRegistrationAttempts: 5, - registrationCountDuration: time.Hour, - minimumNodes: 3, - minGatewayVersion: minGatewayVersion, - minServerVersion: minServerVersion, - } - nodeComm = nodeComms.StartNode(&id.TempGateway, nodeAddr, nodeComms.NewImplementation(), nodeCert, nodeKey) + Address: permAddr, + CertPath: testkeys.GetCACertPath(), + KeyPath: testkeys.GetCAKeyPath(), + NdfOutputPath: testkeys.GetNDFPath(), + publicAddress: permAddr, + udbCertPath: testkeys.GetUdbCertPath(), + userRegCapacity: 5, + userRegLeakPeriod: time.Hour, + minimumNodes: 3, + minGatewayVersion: minGatewayVersion, + minServerVersion: minServerVersion, + } + nodeComm = nodeComms.StartNode(&id.TempGateway, nodeAddr, 0, nodeComms.NewImplementation(), nodeCert, nodeKey) runFunc := func() int { code := m.Run() @@ -84,17 +85,18 @@ func TestMain(m *testing.M) { os.Exit(runFunc()) } -//Error path: Test an insertion on an empty database +// Error path: Test an insertion on an empty database func TestEmptyDataBase(t *testing.T) { - //Start the registration server + // Start the registration server testParams := Params{ - CertPath: testkeys.GetCACertPath(), - KeyPath: testkeys.GetCAKeyPath(), - maxRegistrationAttempts: 5, - registrationCountDuration: time.Hour, + CertPath: testkeys.GetCACertPath(), + KeyPath: testkeys.GetCAKeyPath(), + udbCertPath: testkeys.GetUdbCertPath(), + userRegLeakPeriod: time.Hour, + userRegCapacity: 5, } // Start registration server - impl, err := StartRegistration(testParams, nil) + impl, err := StartRegistration(testParams) if err != nil { t.Errorf(err.Error()) } @@ -108,7 +110,7 @@ func TestEmptyDataBase(t *testing.T) { t.Errorf("%+v", err) } - //using node cert as gateway cert + // using node cert as gateway cert err = impl.RegisterNode([]byte("test"), nodeAddr, string(nodeCert), nodeAddr, string(nodeCert), "AAA") if err == nil { @@ -123,7 +125,7 @@ func TestEmptyDataBase(t *testing.T) { func TestRegCodeExists_InsertRegCode(t *testing.T) { // Start registration server testParams.Address = "0.0.0.0:5901" - impl, err := StartRegistration(testParams, nil) + impl, err := StartRegistration(testParams) if err != nil { t.Errorf(err.Error()) return @@ -138,7 +140,7 @@ func TestRegCodeExists_InsertRegCode(t *testing.T) { t.Errorf("%+v", err) } - //Insert a sample regCode + // Insert a sample regCode applicationId := uint64(10) newNode := &storage.Node{ Code: "AAAA", @@ -150,7 +152,7 @@ func TestRegCodeExists_InsertRegCode(t *testing.T) { if err != nil { t.Errorf("Failed to insert client reg code %+v", err) } - //Register a node with that regCode + // Register a node with that regCode testSalt := []byte("testtesttesttesttesttesttesttest") err = impl.RegisterNode(testSalt, nodeAddr, string(nodeCert), nodeAddr, string(nodeCert), newNode.Code) @@ -159,10 +161,10 @@ func TestRegCodeExists_InsertRegCode(t *testing.T) { } } -//Happy Path: Insert a reg code along with a node +// Happy Path: Insert a reg code along with a node func TestRegCodeExists_RegUser(t *testing.T) { - //Initialize an implementation and the permissioning server - impl, err := StartRegistration(testParams, nil) + // Initialize an implementation and the permissioning server + impl, err := StartRegistration(testParams) if err != nil { t.Errorf("Unable to start: %+v", err) } @@ -175,26 +177,27 @@ func TestRegCodeExists_RegUser(t *testing.T) { t.Errorf("%+v", err) } - //Insert regcodes into it + // Insert regcodes into it err = storage.PermissioningDb.InsertClientRegCode("AAAA", 100) if err != nil { t.Errorf("Failed to insert client reg code %+v", err) } - //Attempt to register a user - sig, err := impl.RegisterUser("AAAA", string(nodeKey)) + // Attempt to register a user + sig, receptionSig, err := impl.RegisterUser("AAAA", string(nodeKey), string(nodeKey)) if err != nil { t.Errorf("Failed to register a node when it should have worked: %+v", err) } - if sig == nil { - t.Errorf("Failed to sign public key, recieved %+v as a signature", sig) + if sig == nil || receptionSig == nil { + t.Errorf("Failed to sign public key, recieved %+v as a signature & %+v as a receptionSignature", + sig, receptionSig) } impl.Comms.Shutdown() } -//Attempt to register a node after the +// Attempt to register a node after the func TestCompleteRegistration_HappyPath(t *testing.T) { // Initialize the database var err error @@ -207,7 +210,7 @@ func TestCompleteRegistration_HappyPath(t *testing.T) { t.Errorf("%+v", err) } - //Insert a sample regCode + // Insert a sample regCode infos := make([]node.Info, 0) infos = append(infos, node.Info{RegCode: "BBBB", Order: "0"}) @@ -215,7 +218,7 @@ func TestCompleteRegistration_HappyPath(t *testing.T) { localParams := testParams localParams.minimumNodes = 1 // Start registration server - impl, err := StartRegistration(localParams, nil) + impl, err := StartRegistration(localParams) if err != nil { t.Errorf(err.Error()) return @@ -241,7 +244,7 @@ func TestCompleteRegistration_HappyPath(t *testing.T) { } } -//Error path: test that trying to register with the same reg code fails +// Error path: test that trying to register with the same reg code fails func TestDoubleRegistration(t *testing.T) { // Initialize the database var err error @@ -254,7 +257,7 @@ func TestDoubleRegistration(t *testing.T) { t.Errorf("%+v", err) } - //Create reg codes and populate the database + // Create reg codes and populate the database infos := make([]node.Info, 0) infos = append(infos, node.Info{RegCode: "AAAA", Order: "0"}, node.Info{RegCode: "BBBB", Order: "1"}, @@ -263,17 +266,17 @@ func TestDoubleRegistration(t *testing.T) { RegParams = testParams // Start registration server - impl, err := StartRegistration(testParams, nil) + impl, err := StartRegistration(testParams) if err != nil { t.Errorf(err.Error()) return } defer impl.Comms.Shutdown() - //Create a second node to register - nodeComm2 := nodeComms.StartNode(&id.TempGateway, "0.0.0.0:6901", nodeComms.NewImplementation(), nodeCert, nodeKey) + // Create a second node to register + nodeComm2 := nodeComms.StartNode(&id.TempGateway, "0.0.0.0:6901", 0, nodeComms.NewImplementation(), nodeCert, nodeKey) defer nodeComm2.Shutdown() - //Register 1st node + // Register 1st node testSalt := []byte("testtesttesttesttesttesttesttest") err = impl.RegisterNode(testSalt, nodeAddr, string(nodeCert), nodeAddr, string(nodeCert), "BBBB") @@ -281,7 +284,7 @@ func TestDoubleRegistration(t *testing.T) { t.Errorf("Expected happy path, recieved error: %+v", err) } - //Register 2nd node + // Register 2nd node err = impl.RegisterNode(testSalt, "0.0.0.0:6901", string(nodeCert), "0.0.0.0:6901", string(nodeCert), "BBBB") if err != nil { @@ -291,7 +294,7 @@ func TestDoubleRegistration(t *testing.T) { t.Errorf("Expected happy path, recieved error: %+v", err) } -//Happy path: attempt to register 2 nodes +// Happy path: attempt to register 2 nodes func TestTopology_MultiNodes(t *testing.T) { // Initialize the database var err error @@ -304,7 +307,7 @@ func TestTopology_MultiNodes(t *testing.T) { t.Errorf("%+v", err) } - //Create reg codes and populate the database + // Create reg codes and populate the database infos := make([]node.Info, 0) infos = append(infos, node.Info{RegCode: "AAAA", Order: "0"}, node.Info{RegCode: "BBBB", Order: "1"}, @@ -316,20 +319,20 @@ func TestTopology_MultiNodes(t *testing.T) { localParams.minimumNodes = 2 // Start registration server - impl, err := StartRegistration(localParams, nil) + impl, err := StartRegistration(localParams) if err != nil { t.Errorf(err.Error()) return } defer impl.Comms.Shutdown() - //Create a second node to register - nodeComm2 := nodeComms.StartNode(&id.TempGateway, "0.0.0.0:6901", nodeComms.NewImplementation(), nodeCert, nodeKey) - //Kill the connections for the next test + // Create a second node to register + nodeComm2 := nodeComms.StartNode(&id.TempGateway, "0.0.0.0:6901", 0, nodeComms.NewImplementation(), nodeCert, nodeKey) + // Kill the connections for the next test defer nodeComm2.Shutdown() go func() { testSalt := []byte("testtesttesttesttesttesttesttest") - //Register 1st node + // Register 1st node err = impl.RegisterNode(testSalt, nodeAddr, string(nodeCert), nodeAddr, string(nodeCert), "BBBB") @@ -337,7 +340,7 @@ func TestTopology_MultiNodes(t *testing.T) { t.Errorf("Expected happy path, recieved error: %+v", err) } - //Register 2nd node + // Register 2nd node err = impl.RegisterNode(testSalt, "0.0.0.0:6901", string(gatewayCert), "0.0.0.0:6901", string(gatewayCert), "CCCC") @@ -367,7 +370,7 @@ func TestRegistrationImpl_CheckNodeRegistration(t *testing.T) { t.Errorf("%+v", err) } - //Create reg codes and populate the database + // Create reg codes and populate the database infos := make([]node.Info, 0) infos = append(infos, node.Info{RegCode: "AAAA", Order: "0"}, node.Info{RegCode: "BBBB", Order: "1"}, @@ -379,18 +382,18 @@ func TestRegistrationImpl_CheckNodeRegistration(t *testing.T) { localParams.minimumNodes = 2 // Start registration server - impl, err := StartRegistration(localParams, nil) + impl, err := StartRegistration(localParams) if err != nil { t.Errorf(err.Error()) return } - //Kill the connections for the next test + // Kill the connections for the next test defer impl.Comms.Shutdown() // Craft registered node id testNodeID := id.NewIdFromString("A", id.Node, t) - //Register 1st node + // Register 1st node err = impl.RegisterNode(testNodeID.Marshal(), nodeAddr, string(nodeCert), nodeAddr, string(nodeCert), "BBBB") @@ -438,7 +441,7 @@ func TestCheckRegistration_NilMsg(t *testing.T) { t.Errorf("%+v", err) } - //Create reg codes and populate the database + // Create reg codes and populate the database infos := make([]node.Info, 0) infos = append(infos, node.Info{RegCode: "AAAA", Order: "0"}, node.Info{RegCode: "BBBB", Order: "1"}, @@ -450,18 +453,18 @@ func TestCheckRegistration_NilMsg(t *testing.T) { localParams.minimumNodes = 2 // Start registration server - impl, err := StartRegistration(localParams, nil) + impl, err := StartRegistration(localParams) if err != nil { t.Errorf(err.Error()) return } - //Kill the connections for the next test + // Kill the connections for the next test defer impl.Comms.Shutdown() // Craft registered node id testNodeID := id.NewIdFromString("A", id.Node, t) - //Register 1st node + // Register 1st node err = impl.RegisterNode(testNodeID.Marshal(), nodeAddr, string(nodeCert), nodeAddr, string(nodeCert), "BBBB") @@ -488,7 +491,7 @@ func TestCheckRegistration_InvalidID(t *testing.T) { t.Errorf("%+v", err) } - //Create reg codes and populate the database + // Create reg codes and populate the database infos := make([]node.Info, 0) infos = append(infos, node.Info{RegCode: "AAAA", Order: "0"}, node.Info{RegCode: "BBBB", Order: "1"}, @@ -500,18 +503,18 @@ func TestCheckRegistration_InvalidID(t *testing.T) { localParams.minimumNodes = 2 // Start registration server - impl, err := StartRegistration(localParams, nil) + impl, err := StartRegistration(localParams) if err != nil { t.Errorf(err.Error()) return } - //Kill the connections for the next test + // Kill the connections for the next test defer impl.Comms.Shutdown() // Craft registered node id testNodeID := id.NewIdFromString("A", id.Node, t) - //Register 1st node + // Register 1st node err = impl.RegisterNode(testNodeID.Marshal(), nodeAddr, string(nodeCert), nodeAddr, string(nodeCert), "BBBB") @@ -530,23 +533,6 @@ func TestCheckRegistration_InvalidID(t *testing.T) { } } -func TestRegistrationImpl_GetCurrentClientVersion(t *testing.T) { - impl, err := StartRegistration(testParams, nil) - if err != nil { - t.Errorf(err.Error()) - return - } - testVersion := "0.0.0a" - setClientVersion(testVersion) - ver, err := impl.GetCurrentClientVersion() - if err != nil { - t.Error(err) - } - if ver != testVersion { - t.Errorf("Version was %+v, expected %+v", ver, testVersion) - } -} - // Test a case that should pass validation func TestValidateClientVersion_Success(t *testing.T) { err := validateVersion("0.0.0a") @@ -584,17 +570,18 @@ func TestValidateClientVersion_Failure(t *testing.T) { func TestRegCodeExists_RegUser_Timer(t *testing.T) { testParams2 := Params{ - Address: "0.0.0.0:5905", - CertPath: testkeys.GetCACertPath(), - KeyPath: testkeys.GetCAKeyPath(), - NdfOutputPath: testkeys.GetNDFPath(), - publicAddress: "0.0.0.0:5905", - maxRegistrationAttempts: 4, - registrationCountDuration: 3 * time.Second, + Address: "0.0.0.0:5905", + CertPath: testkeys.GetCACertPath(), + KeyPath: testkeys.GetCAKeyPath(), + NdfOutputPath: testkeys.GetNDFPath(), + udbCertPath: testkeys.GetUdbCertPath(), + publicAddress: "0.0.0.0:5905", + userRegCapacity: 4, + userRegLeakPeriod: 3 * time.Second, } // Start registration server - impl, err := StartRegistration(testParams2, make(chan bool)) + impl, err := StartRegistration(testParams2) if err != nil { t.Errorf(err.Error()) } @@ -609,38 +596,38 @@ func TestRegCodeExists_RegUser_Timer(t *testing.T) { } // Attempt to register a user - _, err = impl.RegisterUser("b", "B") + _, _, err = impl.RegisterUser("", "B", "C") if err != nil { t.Errorf("Failed to register a user when it should have worked: %+v", err) } // Attempt to register a user - _, err = impl.RegisterUser("c", "C") + _, _, err = impl.RegisterUser("", "C", "D") if err != nil { t.Errorf("Failed to register a user when it should have worked: %+v", err) } // Attempt to register a user - _, err = impl.RegisterUser("d", "D") + _, _, err = impl.RegisterUser("", "D", "E") if err != nil { t.Errorf("Failed to register a user when it should have worked: %+v", err) } // Attempt to register a user - _, err = impl.RegisterUser("e", "E") + _, _, err = impl.RegisterUser("", "E", "F") if err != nil { t.Errorf("Failed to register a user when it should have worked: %+v", err) } // Attempt to register a user - _, err = impl.RegisterUser("f", "F") + _, _, err = impl.RegisterUser("", "F", "G") if err == nil { t.Errorf("Did not fail to register a user when it should not have worked: %+v", err) } - time.Sleep(testParams2.registrationCountDuration) + time.Sleep(testParams2.userRegLeakPeriod) // Attempt to register a user - _, err = impl.RegisterUser("g", "G") + _, _, err = impl.RegisterUser("", "G", "H") if err != nil { t.Errorf("Failed to register a user when it should have worked: %+v", err) } diff --git a/cmd/root.go b/cmd/root.go index a5ec26086e47a37f78b99d48a74646ef45411536..a44619a582bd1e3147316db52a71c9930bc533c5 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -16,12 +16,13 @@ import ( "github.com/spf13/cobra" jww "github.com/spf13/jwalterweatherman" "github.com/spf13/viper" + "gitlab.com/elixxir/client/interfaces/contact" "gitlab.com/elixxir/comms/mixmessages" - "gitlab.com/elixxir/primitives/utils" "gitlab.com/elixxir/primitives/version" "gitlab.com/elixxir/registration/scheduling" "gitlab.com/elixxir/registration/storage" "gitlab.com/elixxir/registration/storage/node" + "gitlab.com/xx_network/primitives/utils" "net" "os" "path" @@ -43,6 +44,10 @@ var ( disablePermissioning bool disabledNodesPath string + // Storage of registration codes from file so it can be loaded from disableRegCodes + regCodeInfos []node.Info + disableRegCodes bool + // Duration between polls of the disabled Node list for updates. disabledNodesPollDuration time.Duration ) @@ -76,22 +81,10 @@ var rootCmd = &cobra.Command{ ndfOutputPath := viper.GetString("ndfOutputPath") setClientVersion(viper.GetString("clientVersion")) ipAddr := viper.GetString("publicAddress") - //Get Notification Server address and cert Path + // Get Notification Server address and cert Path nsCertPath := viper.GetString("nsCertPath") nsAddress := viper.GetString("nsAddress") publicAddress := fmt.Sprintf("%s:%d", ipAddr, viper.GetInt("port")) - roundIdPath := viper.GetString("roundIdPath") - updateIdPath := viper.GetString("updateIdPath") - - maxRegistrationAttempts := viper.GetUint64("maxRegistrationAttempts") - if maxRegistrationAttempts == 0 { - maxRegistrationAttempts = defaultMaxRegistrationAttempts - } - - registrationCountDuration := viper.GetDuration("registrationCountDuration") - if registrationCountDuration == 0 { - registrationCountDuration = defaultRegistrationCountDuration - } // Set up database connection rawAddr := viper.GetString("dbAddress") @@ -119,7 +112,7 @@ var rootCmd = &cobra.Command{ // Populate Node registration codes into the database RegCodesFilePath := viper.GetString("regCodesFilePath") if RegCodesFilePath != "" { - regCodeInfos, err := node.LoadInfo(RegCodesFilePath) + regCodeInfos, err = node.LoadInfo(RegCodesFilePath) if err != nil { jww.ERROR.Printf("Failed to load registration codes from the "+ "file %s: %+v", RegCodesFilePath, err) @@ -134,10 +127,14 @@ var rootCmd = &cobra.Command{ ClientRegCodes = viper.GetStringSlice("clientRegCodes") storage.PopulateClientRegistrationCodes(ClientRegCodes, 1000) - udbId := make([]byte, 32) - udbId[len(udbId)-1] = byte(viper.GetInt("udbID")) + // Get user discovery ID and DH public key from contact file + udbId, udbDhPubKey := readUdContact(viper.GetString("udContactPath")) + + // Get UDB cert path and address + udbCertPath := viper.GetString("udbCertPath") + udbAddress := viper.GetString("udbAddress") - //load the scheduling params file as a string + // load the scheduling params file as a string SchedulingConfigPath := viper.GetString("schedulingConfigPath") SchedulingConfig, err := utils.ReadFile(SchedulingConfigPath) if err != nil { @@ -159,6 +156,13 @@ var rootCmd = &cobra.Command{ minServerVersionString, err) } + minClientVersionString := viper.GetString("minClientVersion") + minClientVersion, err := version.ParseVersion(minClientVersionString) + if err != nil { + jww.FATAL.Panicf("Could not parse minClientVersion %#v: %+v", + minClientVersionString, err) + } + // Get the amount of time to wait for scheduling to end // This should default to 10 seconds in StartRegistration if not set schedulingKillTimeout, err := time.ParseDuration( @@ -176,35 +180,55 @@ var rootCmd = &cobra.Command{ disableGatewayPing := viper.GetBool("disableGatewayPing") + userRegLeakPeriodString := viper.GetString("userRegLeakPeriod") + var userRegLeakPeriod time.Duration + if userRegLeakPeriodString != "" { + // specified, so try to parse + userRegLeakPeriod, err = time.ParseDuration(userRegLeakPeriodString) + if err != nil { + jww.FATAL.Panicf("Could not parse duration: %+v", err) + } + } else { + // use default + userRegLeakPeriod = time.Hour * 24 + } + userRegCapacity := viper.GetUint32("userRegCapacity") + if userRegCapacity == 0 { + // use default + userRegCapacity = 1000 + } + // Populate params RegParams = Params{ - Address: localAddress, - CertPath: certPath, - KeyPath: keyPath, - NdfOutputPath: ndfOutputPath, - cmix: *cmix, - e2e: *e2e, - publicAddress: publicAddress, - NsAddress: nsAddress, - NsCertPath: nsCertPath, - maxRegistrationAttempts: maxRegistrationAttempts, - registrationCountDuration: registrationCountDuration, - schedulingKillTimeout: schedulingKillTimeout, - closeTimeout: closeTimeout, - udbId: udbId, - minimumNodes: viper.GetUint32("minimumNodes"), - minGatewayVersion: minGatewayVersion, - minServerVersion: minServerVersion, - roundIdPath: roundIdPath, - updateIdPath: updateIdPath, - disableGatewayPing: disableGatewayPing, + Address: localAddress, + CertPath: certPath, + KeyPath: keyPath, + NdfOutputPath: ndfOutputPath, + cmix: *cmix, + e2e: *e2e, + publicAddress: publicAddress, + NsAddress: nsAddress, + NsCertPath: nsCertPath, + schedulingKillTimeout: schedulingKillTimeout, + closeTimeout: closeTimeout, + udbId: udbId, + udbDhPubKey: udbDhPubKey, + udbCertPath: udbCertPath, + udbAddress: udbAddress, + minimumNodes: viper.GetUint32("minimumNodes"), + minGatewayVersion: minGatewayVersion, + minServerVersion: minServerVersion, + minClientVersion: minClientVersion, + disableGatewayPing: disableGatewayPing, + userRegLeakPeriod: userRegLeakPeriod, + userRegCapacity: userRegCapacity, + addressSpace: viper.GetUint32("addressSpace"), } jww.INFO.Println("Starting Permissioning Server...") // Start registration server - quitRegistrationCapacity := make(chan bool) - impl, err := StartRegistration(RegParams, quitRegistrationCapacity) + impl, err := StartRegistration(RegParams) if err != nil { jww.FATAL.Panicf(err.Error()) } @@ -328,12 +352,6 @@ var rootCmd = &cobra.Command{ jww.ERROR.Print("couldn't stop round creation!") } - // Try a non-blocking send for the registration capacity - select { - case quitRegistrationCapacity <- true: - default: - } - bannedNodeTrackerQuitChan <- struct{}{} // Prevent node updates after round creation stops @@ -412,13 +430,16 @@ func init() { rootCmd.Flags().BoolVar(&noTLS, "noTLS", false, "Runs without TLS enabled") + rootCmd.Flags().BoolVar(&disableRegCodes, "disableRegCodes", false, + "Automatically provide registration codes to Nodes. (For testing only)") + rootCmd.Flags().StringP("close-timeout", "t", "60s", - ("Amount of time to wait for rounds to stop running after" + - " receiving the SIGUSR1 and SIGTERM signals")) + "Amount of time to wait for rounds to stop running after"+ + " receiving the SIGUSR1 and SIGTERM signals") rootCmd.Flags().StringP("kill-timeout", "k", "60s", - ("Amount of time to wait for round creation to stop after" + - " receiving the SIGUSR2 and SIGTERM signals")) + "Amount of time to wait for round creation to stop after"+ + " receiving the SIGUSR2 and SIGTERM signals") rootCmd.Flags().BoolVarP(&disablePermissioning, "disablePermissioning", "", false, "Disables registration server checking for ndf updates") @@ -435,11 +456,25 @@ func init() { jww.FATAL.Panicf("could not bind flag: %+v", err) } + err = viper.BindPFlag("schedulingKillTimeout", + rootCmd.Flags().Lookup("kill-timeout")) + if err != nil { + jww.FATAL.Panicf("could not bind flag: %+v", err) + } + + rootCmd.Flags().String("udContactPath", "", + "Location of the user discovery contact file.") + + err = viper.BindPFlag("udContactPath", rootCmd.Flags().Lookup("udContactPath")) + if err != nil { + jww.FATAL.Panicf("could not bind flag: %+v", err) + } + } // initConfig reads in config file and ENV variables if set. func initConfig() { - //Use default config location if none is passed + // Use default config location if none is passed if cfgFile == "" { // Find home directory. home, err := homedir.Dir() @@ -556,7 +591,10 @@ func initLog() { // Create log file, overwrites if existing logPath := viper.GetString("logPath") - logFile, err := os.Create(logPath) + fullLogPath, _ := utils.ExpandPath(logPath) + logFile, err := os.OpenFile(fullLogPath, + os.O_CREATE|os.O_WRONLY|os.O_APPEND, + 0644) if err != nil { jww.WARN.Println("Invalid or missing log path, default path used.") } else { @@ -564,3 +602,28 @@ func initLog() { } } } + +// readUdContact reads and unmarshal the contact from file and returns the +// marshaled ID and DH public key. +func readUdContact(filePath string) ([]byte, []byte) { + if filePath == "" { + return nil, nil + } + + data, err := utils.ReadFile(filePath) + if err != nil { + jww.FATAL.Panicf("Failed to read contact file: %+v", err) + } + + c, err := contact.Unmarshal(data) + if err != nil { + jww.FATAL.Panicf("Failed to unmarshal contact: %+v", err) + } + + dhPubKeyJson, err := c.DhPubKey.MarshalJSON() + if err != nil { + jww.FATAL.Panicf("Failed to marshal contact DH public key: %+v", err) + } + + return c.ID.Marshal(), dhPubKeyJson +} diff --git a/cmd/version.go b/cmd/version.go index 18588db0a2f7ec8e655d28a9ce28e266575685a5..848188f02b942e09a501c2fadb75fa83e1bb4717 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -10,12 +10,13 @@ package cmd import ( "fmt" + "github.com/spf13/cobra" - "gitlab.com/elixxir/primitives/utils" + "gitlab.com/xx_network/primitives/utils" ) // Change this value to set the version for this build -const currentVersion = "1.5.0" +const currentVersion = "2.0.0" func printVersion() { fmt.Printf("xx network Permissioning Server v%s -- %s\n\n", diff --git a/cmd/version_vars.go b/cmd/version_vars.go index bcb4238480295112b9c6299c4440dbd6ee15ee27..7911f58eda25589cbfc023f274a6355615000de2 100644 --- a/cmd/version_vars.go +++ b/cmd/version_vars.go @@ -1,10 +1,10 @@ // Code generated by go generate; DO NOT EDIT. // This file was generated by robots at -// 2021-01-28 10:47:07.899147 -0600 CST m=+0.063060483 +// 2021-03-10 10:12:36.159679 -0800 PST m=+0.033523506 package cmd -const GITVERSION = `853bcda Merge branch 'XX-2875/ClientErrMaster' into 'master'` -const SEMVER = "1.5.0" +const GITVERSION = `93d59af update deps` +const SEMVER = "2.0.0" const DEPENDENCIES = `module gitlab.com/elixxir/registration go 1.13 @@ -14,26 +14,23 @@ require ( github.com/fsnotify/fsnotify v1.4.9 github.com/go-sql-driver/mysql v1.5.0 // indirect github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 - github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect github.com/jinzhu/gorm v1.9.12 github.com/jinzhu/now v1.1.1 // indirect github.com/lib/pq v1.5.2 // indirect github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect github.com/mitchellh/go-homedir v1.1.0 - github.com/mitchellh/mapstructure v1.3.0 // indirect - github.com/pelletier/go-toml v1.7.0 // indirect github.com/pkg/errors v0.9.1 github.com/smartystreets/assertions v1.1.0 // indirect - github.com/spf13/afero v1.2.2 // indirect - github.com/spf13/cast v1.3.1 // indirect - github.com/spf13/cobra v1.0.0 + github.com/spf13/cobra v1.1.1 github.com/spf13/jwalterweatherman v1.1.0 - github.com/spf13/pflag v1.0.5 // indirect - github.com/spf13/viper v1.7.0 - gitlab.com/elixxir/comms v0.0.0-20201119183624-cd133e949837 - gitlab.com/elixxir/crypto v0.0.5 - gitlab.com/elixxir/primitives v0.0.0-20201007171034-21ce972dc81d - gitlab.com/xx_network/comms v0.0.0-20200918162019-06b733db60e6 + github.com/spf13/viper v1.7.1 + gitlab.com/elixxir/client v1.2.1-0.20210222224029-4300043d7ce8 + gitlab.com/elixxir/comms v0.0.4-0.20210309193245-64181ff10b68 + gitlab.com/elixxir/crypto v0.0.7-0.20210309193114-8a6225c667e2 + gitlab.com/elixxir/primitives v0.0.3-0.20210309193003-ef42ebb4800b + gitlab.com/xx_network/comms v0.0.4-0.20210309192940-6b7fb39b4d01 + gitlab.com/xx_network/crypto v0.0.5-0.20210309192854-cf32117afb96 + gitlab.com/xx_network/primitives v0.0.4-0.20210309173740-eb8cd411334a ) replace google.golang.org/grpc => github.com/grpc/grpc-go v1.27.1 diff --git a/go.mod b/go.mod index caef220e101dc3f3ff5566d62110a317ae0955d2..9d8d546e1db743e56bb18de2835e648a000b6def 100644 --- a/go.mod +++ b/go.mod @@ -7,26 +7,23 @@ require ( github.com/fsnotify/fsnotify v1.4.9 github.com/go-sql-driver/mysql v1.5.0 // indirect github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 - github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect github.com/jinzhu/gorm v1.9.12 github.com/jinzhu/now v1.1.1 // indirect github.com/lib/pq v1.5.2 // indirect github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect github.com/mitchellh/go-homedir v1.1.0 - github.com/mitchellh/mapstructure v1.3.0 // indirect - github.com/pelletier/go-toml v1.7.0 // indirect github.com/pkg/errors v0.9.1 github.com/smartystreets/assertions v1.1.0 // indirect - github.com/spf13/afero v1.2.2 // indirect - github.com/spf13/cast v1.3.1 // indirect - github.com/spf13/cobra v1.0.0 + github.com/spf13/cobra v1.1.1 github.com/spf13/jwalterweatherman v1.1.0 - github.com/spf13/pflag v1.0.5 // indirect - github.com/spf13/viper v1.7.0 - gitlab.com/elixxir/comms v0.0.0-20201119183624-cd133e949837 - gitlab.com/elixxir/crypto v0.0.5 - gitlab.com/elixxir/primitives v0.0.0-20201007171034-21ce972dc81d - gitlab.com/xx_network/comms v0.0.0-20200918162019-06b733db60e6 + github.com/spf13/viper v1.7.1 + gitlab.com/elixxir/client v1.2.1-0.20210222224029-4300043d7ce8 + gitlab.com/elixxir/comms v0.0.4-0.20210311180506-28ae742c5e35 + gitlab.com/elixxir/crypto v0.0.7-0.20210309193114-8a6225c667e2 + gitlab.com/elixxir/primitives v0.0.3-0.20210309193003-ef42ebb4800b + gitlab.com/xx_network/comms v0.0.4-0.20210309192940-6b7fb39b4d01 + gitlab.com/xx_network/crypto v0.0.5-0.20210309192854-cf32117afb96 + gitlab.com/xx_network/primitives v0.0.4-0.20210309173740-eb8cd411334a ) replace google.golang.org/grpc => github.com/grpc/grpc-go v1.27.1 diff --git a/go.sum b/go.sum index 275a833e05e175ae4c94c53a5296e6e402bb491e..627e6e5fb860aaaf1d8b8a80fd83b129ed6abb3f 100644 --- a/go.sum +++ b/go.sum @@ -17,9 +17,10 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/badoux/checkmail v1.2.1 h1:TzwYx5pnsV6anJweMx2auXdekBwGr/yt1GgalIx9nBQ= +github.com/badoux/checkmail v1.2.1/go.mod h1:XroCOBU5zzZJcLvgwU15I+2xXyCdTWXyR9MGfRhBYy0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= @@ -27,9 +28,7 @@ github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJm github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= @@ -87,6 +86,8 @@ github.com/golang/protobuf v1.4.1 h1:ZFgWrT+bLgsYPirOnRfKLYJLvssAegOj/hgyMFdJZe0 github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -96,6 +97,8 @@ github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -106,7 +109,6 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGa github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 h1:l5lAOZEym3oK3SQ2HBHWsJUfbNBiTXJDeW2QDxw9AQ0= github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 h1:Iju5GlWwrvL6UBg4zJJt3btmonfrMlCDdsejg4CZE7c= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= @@ -153,9 +155,12 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 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/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 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= @@ -165,9 +170,10 @@ github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4= github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.5.2 h1:yTSXVswvWUOQ3k1sd7vJfDrbSl8lKuscqFJRqjC0ifw= github.com/lib/pq v1.5.2/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.4 h1:8KGKTcQQGm0Kv7vEbKFErAoAOFyyacLStRtQSeYtvkY= +github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= @@ -185,23 +191,26 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.3.0 h1:iDwIio/3gk2QtLLEsqU5lInaMzos0hDTz8a6lazSFVw= -github.com/mitchellh/mapstructure v1.3.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.0 h1:7ks8ZkOP5/ujthUsT07rNv+nkLXCQWKNHuwzOAesEks= +github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nyaruka/phonenumbers v1.0.60 h1:nnAcNwmZflhegiImm6MkvjlRRyoaSw1ox/jGPAewWTg= +github.com/nyaruka/phonenumbers v1.0.60/go.mod h1:sDaTZ/KPX5f8qyV9qN+hIm+4ZBARJrupC6LuhshJq1U= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.7.0 h1:7utD74fnzVc/cpcyy8sjrlFr5vYpypUixARcHIMIGuI= -github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= +github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM= +github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= @@ -224,6 +233,7 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= github.com/smartystreets/assertions v1.1.0 h1:MkTeG1DMwsrdH7QtLXy5W+fUxWq+vmb6cLmyJ7aRtF0= github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= @@ -231,14 +241,14 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9 github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/afero v1.5.1 h1:VHu76Lk0LSP1x254maIu2bplkWpfBWI+B+6fdoZprcg= +github.com/spf13/afero v1.5.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4= +github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= @@ -246,13 +256,15 @@ github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk= +github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= @@ -260,27 +272,66 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -gitlab.com/elixxir/comms v0.0.0-20200707210150-b8ebd0951d23/go.mod h1:OsWMZ1O/R9fOkm+PoHnR3rkXfFtipGoPs73FuKuurHY= -gitlab.com/elixxir/comms v0.0.0-20201119183624-cd133e949837 h1:ZKh1kIdkP31vtV6QIpXCwz5TmfEXg7suuwcfu9lv7xs= -gitlab.com/elixxir/comms v0.0.0-20201119183624-cd133e949837/go.mod h1:L2Va13j2AbQkpkveOQmNzrQD37uI5NKeBhYH+LWMOx0= -gitlab.com/elixxir/crypto v0.0.0-20200707005343-97f868cbd930/go.mod h1:LHBAaEf48a0/AjU118rjoworH0LgXifhAqmNX3ZRvME= -gitlab.com/elixxir/crypto v0.0.0-20200731174640-0503cf80524a h1:peZpulfSqLSceA5ovtzQ5MPgQt4YbJY8FzpV2S2Nrhc= -gitlab.com/elixxir/crypto v0.0.0-20200731174640-0503cf80524a/go.mod h1:LHBAaEf48a0/AjU118rjoworH0LgXifhAqmNX3ZRvME= -gitlab.com/elixxir/crypto v0.0.5 h1:QS/3PEA6Hni61r6YAV8IfneKydtZjcC5E4ZLUPrEypc= -gitlab.com/elixxir/crypto v0.0.5/go.mod h1:PFeyONfhulnM72O2wROslwhNadtnyvKAD2QLtQzAifM= -gitlab.com/elixxir/primitives v0.0.0-20200706165052-9fe7a4fb99a3/go.mod h1:OQgUZq7SjnE0b+8+iIAT2eqQF+2IFHn73tOo+aV11mg= -gitlab.com/elixxir/primitives v0.0.0-20200708185800-a06e961280e6 h1:7xLD8w5qAKN1YqG2UiMiN3rODUACyQME83uDlVhvWLo= -gitlab.com/elixxir/primitives v0.0.0-20200708185800-a06e961280e6/go.mod h1:OQgUZq7SjnE0b+8+iIAT2eqQF+2IFHn73tOo+aV11mg= -gitlab.com/elixxir/primitives v0.0.0-20200929195204-dd3970d93573/go.mod h1:OQgUZq7SjnE0b+8+iIAT2eqQF+2IFHn73tOo+aV11mg= -gitlab.com/elixxir/primitives v0.0.0-20201007171034-21ce972dc81d h1:UTB9Aayt7UyaBMxanP3HjWFz7PhcY8U8lgBHZ+97dr4= -gitlab.com/elixxir/primitives v0.0.0-20201007171034-21ce972dc81d/go.mod h1:OQgUZq7SjnE0b+8+iIAT2eqQF+2IFHn73tOo+aV11mg= -gitlab.com/xx_network/comms v0.0.0-20200916172635-6ab807c3c820 h1:vdozJQgrnznmHJLS38aOXprLKZXClnHJ9ljY/70aWuI= -gitlab.com/xx_network/comms v0.0.0-20200916172635-6ab807c3c820/go.mod h1:J+GJ6fn71a4xnYVvbcrhtvWSOQIqqhaGcaej5xB3/JY= -gitlab.com/xx_network/comms v0.0.0-20200918162019-06b733db60e6 h1:qBR6vf6Mkmv00LBcQm3Rd1+RreVcbzsYqJCGDXVGc84= -gitlab.com/xx_network/comms v0.0.0-20200918162019-06b733db60e6/go.mod h1:J+GJ6fn71a4xnYVvbcrhtvWSOQIqqhaGcaej5xB3/JY= +github.com/zeebo/assert v0.0.0-20181109011804-10f827ce2ed6/go.mod h1:yssERNPivllc1yU3BvpjYI5BUW+zglcz6QWqeVRL5t0= +github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= +github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/blake3 v0.0.4 h1:vtZ4X8B2lKXZFg2Xyg6Wo36mvmnJvc2VQYTtA4RDCkI= +github.com/zeebo/blake3 v0.0.4/go.mod h1:YOZo8A49yNqM0X/Y+JmDUZshJWLt1laHsNSn5ny2i34= +github.com/zeebo/blake3 v0.1.0 h1:sP3n5SxSbzU8x4Svc4ZcQv7SmQOqCkiKBeAZWP+hePo= +github.com/zeebo/blake3 v0.1.0/go.mod h1:YOZo8A49yNqM0X/Y+JmDUZshJWLt1laHsNSn5ny2i34= +github.com/zeebo/pcg v0.0.0-20181207190024-3cdc6b625a05 h1:4pW5fMvVkrgkMXdvIsVRRTs69DWYA8uNNQsu1stfVKU= +github.com/zeebo/pcg v0.0.0-20181207190024-3cdc6b625a05/go.mod h1:Gr+78ptB0MwXxm//LBaEvBiaXY7hXJ6KGe2V32X2F6E= +github.com/zeebo/pcg v1.0.0 h1:dt+dx+HvX8g7Un32rY9XWoYnd0NmKmrIzpHF7qiTDj0= +github.com/zeebo/pcg v1.0.0/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= +gitlab.com/elixxir/bloomfilter v0.0.0-20200930191214-10e9ac31b228 h1:Gi6rj4mAlK0BJIk1HIzBVMjWNjIUfstrsXC2VqLYPcA= +gitlab.com/elixxir/bloomfilter v0.0.0-20200930191214-10e9ac31b228/go.mod h1:H6jztdm0k+wEV2QGK/KYA+MY9nj9Zzatux/qIvDDv3k= +gitlab.com/elixxir/client v1.2.1-0.20210222224029-4300043d7ce8 h1:2j6FUBt0uAab+WOD2Wvu0rVSgJU0EklBpsfV7lv3eEg= +gitlab.com/elixxir/client v1.2.1-0.20210222224029-4300043d7ce8/go.mod h1:zMjfKGqIBi/rYeWA9ca/Vxhr6a3yGywbURWUutBJMok= +gitlab.com/elixxir/comms v0.0.4-0.20210218234550-f2e03b19bdb2 h1:p5GunVi5sP9atTw3DKBkgV6k3eR9iTyI6m9GbUr8hhA= +gitlab.com/elixxir/comms v0.0.4-0.20210218234550-f2e03b19bdb2/go.mod h1:GCbfPWB7VF5ZeDsLBCwfy0JiquG4OK6gsRjaIS66+yg= +gitlab.com/elixxir/comms v0.0.4-0.20210309193245-64181ff10b68 h1:HR45PZyVl+gvksIKoHPCxFndhOpBT6z3rl7vaa3BaAc= +gitlab.com/elixxir/comms v0.0.4-0.20210309193245-64181ff10b68/go.mod h1:96cMuVVlarB+I6nuFKdq4zCagQkbhVK/MUzRk3yOymI= +gitlab.com/elixxir/comms v0.0.4-0.20210311180506-28ae742c5e35 h1:t/ILeoWel5Im+zLQUX2FIroZvrfAkxOaL3DCA8enKcE= +gitlab.com/elixxir/comms v0.0.4-0.20210311180506-28ae742c5e35/go.mod h1:96cMuVVlarB+I6nuFKdq4zCagQkbhVK/MUzRk3yOymI= +gitlab.com/elixxir/crypto v0.0.0-20200804182833-984246dea2c4/go.mod h1:ucm9SFKJo+K0N2GwRRpaNr+tKXMIOVWzmyUD0SbOu2c= +gitlab.com/elixxir/crypto v0.0.3/go.mod h1:ZNgBOblhYToR4m8tj4cMvJ9UsJAUKq+p0gCp07WQmhA= +gitlab.com/elixxir/crypto v0.0.7-0.20210216174551-f806f79610eb h1:aPcrTC0QdrPqz4NgoAt5sfXt/+EFrNUwIns0s0VCQmg= +gitlab.com/elixxir/crypto v0.0.7-0.20210216174551-f806f79610eb/go.mod h1:CLP8kULKW9A5oZHQ1zMCx4swMhBw2IMO68z4U/FkvcU= +gitlab.com/elixxir/crypto v0.0.7-0.20210309193114-8a6225c667e2 h1:JMbUxcOjFpdCBUMZS5g8CWfNdPJ6pP8xsAZbnLj66jc= +gitlab.com/elixxir/crypto v0.0.7-0.20210309193114-8a6225c667e2/go.mod h1:TMZMB24OsjF6y3LCyBMzDucbOx1cGQCCeuKV9lJA/DU= +gitlab.com/elixxir/ekv v0.1.4 h1:NLVMwsFEKArWcsDHu2DbXlm9374iSgn7oIA3rVSsvjc= +gitlab.com/elixxir/ekv v0.1.4/go.mod h1:e6WPUt97taFZe5PFLPb1Dupk7tqmDCTQu1kkstqJvw4= +gitlab.com/elixxir/primitives v0.0.0-20200731184040-494269b53b4d/go.mod h1:OQgUZq7SjnE0b+8+iIAT2eqQF+2IFHn73tOo+aV11mg= +gitlab.com/elixxir/primitives v0.0.0-20200804170709-a1896d262cd9/go.mod h1:p0VelQda72OzoUckr1O+vPW0AiFe0nyKQ6gYcmFSuF8= +gitlab.com/elixxir/primitives v0.0.0-20200804182913-788f47bded40/go.mod h1:tzdFFvb1ESmuTCOl1z6+yf6oAICDxH2NPUemVgoNLxc= +gitlab.com/elixxir/primitives v0.0.1/go.mod h1:kNp47yPqja2lHSiS4DddTvFpB/4D9dB2YKnw5c+LJCE= +gitlab.com/elixxir/primitives v0.0.3-0.20210216174458-2a23825c1eb1 h1:BfcaQtKgIbafExdHkeKIJ5XEGe9MvUiv+yg9u7jwqhY= +gitlab.com/elixxir/primitives v0.0.3-0.20210216174458-2a23825c1eb1/go.mod h1:Wpz7WGZ/CpO6oHNmVTgTNBETTRXi40arWjom1uwu/1s= +gitlab.com/elixxir/primitives v0.0.3-0.20210309193003-ef42ebb4800b h1:TswWfqiZqsdPLeWsfe7VJHMlV01W792kRHGYfYwb2Lk= +gitlab.com/elixxir/primitives v0.0.3-0.20210309193003-ef42ebb4800b/go.mod h1:/e3a4KPqmA9V22qKSZ9prfYYNzIzvLI8xh7noVV091w= +gitlab.com/xx_network/comms v0.0.0-20200805174823-841427dd5023/go.mod h1:owEcxTRl7gsoM8c3RQ5KAm5GstxrJp5tn+6JfQ4z5Hw= +gitlab.com/xx_network/comms v0.0.4-0.20210216174438-0790d1f1f225 h1:ZVxPD76xDLdTSGY2w/aGRMiiry7SauD8sq4c+see6aM= +gitlab.com/xx_network/comms v0.0.4-0.20210216174438-0790d1f1f225/go.mod h1:e7dy2wznC4U4bG/U3xFGYYsnnd8zHOhoSxzFkGPQYX4= +gitlab.com/xx_network/comms v0.0.4-0.20210309192940-6b7fb39b4d01 h1:f93iz7mTHt3r37O97vaQD8otohihLN3OnAEEbDGQdVs= +gitlab.com/xx_network/comms v0.0.4-0.20210309192940-6b7fb39b4d01/go.mod h1:aNPRHmPssXc1JMJ83DAknT2C2iMgKL1wH3//AqQrhQc= +gitlab.com/xx_network/crypto v0.0.3/go.mod h1:DF2HYvvCw9wkBybXcXAgQMzX+MiGbFPjwt3t17VRqRE= +gitlab.com/xx_network/crypto v0.0.4/go.mod h1:+lcQEy+Th4eswFgQDwT0EXKp4AXrlubxalwQFH5O0Mk= +gitlab.com/xx_network/crypto v0.0.5-0.20210216174356-e81e1ddf8fb7 h1:vyL+m7D7w+RgMPARzcKCR8UMGC2foqNU6cSb1J6Dkis= +gitlab.com/xx_network/crypto v0.0.5-0.20210216174356-e81e1ddf8fb7/go.mod h1:8J+/VBcMlBj2sZuSDaVKI/i58awFZ5Zdb4JdEwGVrqo= +gitlab.com/xx_network/crypto v0.0.5-0.20210309192854-cf32117afb96 h1:VZGJNhuU6YunKyK4MbNZf25UxQsmU1bH5SnbK93tI7Q= +gitlab.com/xx_network/crypto v0.0.5-0.20210309192854-cf32117afb96/go.mod h1:TtaHpuX0lcuTTtcq+pz+lMusjyTgvSohIHFOlVwN1uU= +gitlab.com/xx_network/primitives v0.0.0-20200803231956-9b192c57ea7c/go.mod h1:wtdCMr7DPePz9qwctNoAUzZtbOSHSedcK++3Df3psjA= +gitlab.com/xx_network/primitives v0.0.0-20200804183002-f99f7a7284da/go.mod h1:OK9xevzWCaPO7b1wiluVJGk7R5ZsuC7pHY5hteZFQug= +gitlab.com/xx_network/primitives v0.0.2/go.mod h1:cs0QlFpdMDI6lAo61lDRH2JZz+3aVkHy+QogOB6F/qc= +gitlab.com/xx_network/primitives v0.0.4-0.20210215192713-e32335847d4f h1:0wFEYIHuPkWJuDkbDXNrwC5yGwkd7Mugt2BwcTqQbFY= +gitlab.com/xx_network/primitives v0.0.4-0.20210215192713-e32335847d4f/go.mod h1:9imZHvYwNFobxueSvVtHneZLk9wTK7HQTzxPm+zhFhE= +gitlab.com/xx_network/primitives v0.0.4-0.20210219231511-983054dbee36 h1:41qeW7XB9Rllsi6fe37+eaQCLjTGchpvcJqwEvZxeXE= +gitlab.com/xx_network/primitives v0.0.4-0.20210219231511-983054dbee36/go.mod h1:9imZHvYwNFobxueSvVtHneZLk9wTK7HQTzxPm+zhFhE= +gitlab.com/xx_network/primitives v0.0.4-0.20210309173740-eb8cd411334a h1:Ume9QbJ4GoJh7v5yg/YVDjowJHx/VFeOC/A4PJZUm9g= +gitlab.com/xx_network/primitives v0.0.4-0.20210309173740-eb8cd411334a/go.mod h1:9imZHvYwNFobxueSvVtHneZLk9wTK7HQTzxPm+zhFhE= +gitlab.com/xx_network/ring v0.0.2 h1:TlPjlbFdhtJrwvRgIg4ScdngMTaynx/ByHBRZiXCoL0= +gitlab.com/xx_network/ring v0.0.2/go.mod h1:aLzpP2TiZTQut/PVHR40EJAomzugDdHXetbieRClXIM= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= @@ -293,13 +344,17 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200707235045-ab33eee955e0 h1:eIYIE7EC5/Wv5Kbz8bJPaq+TN3kq3W8S+LSm62vM0DY= golang.org/x/crypto v0.0.0-20200707235045-ab33eee955e0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig= +golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -328,12 +383,14 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201029221708-28c70e62bb1d h1:dOiJ2n2cMwGLce/74I/QHMbnpk5GfY7InR8rczoMqRM= +golang.org/x/net v0.0.0-20201029221708-28c70e62bb1d/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b h1:iFwSg7t5GZmB/Q5TjiEAsdoLDrdJRC1RiF2WhuV29Qw= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -354,11 +411,20 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae h1:Ih9Yo4hSPImZOpfGuA4bR/ORKTAbhZo2AbWNRCnevdo= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201029080932-201ba4db2418 h1:HlFl4V6pEMziuLXyRkm5BIYq1y1GAbb02pRlWvI54OM= +golang.org/x/sys v0.0.0-20201029080932-201ba4db2418/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210105210732-16f7687f5001 h1:/dSxr6gT0FNI1MO5WLJo8mTmItROeOKTkDn+7OwWBos= +golang.org/x/sys v0.0.0-20210105210732-16f7687f5001/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -366,6 +432,8 @@ golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -388,6 +456,8 @@ golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -405,11 +475,12 @@ google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98 google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200514193133-8feb7f20f2a2/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200709005830-7a2ca40e9dc3 h1:JwLN1jVnmIsfE4HkDVe2AblFAbo0Z+4cjteDSOnv6oE= -google.golang.org/genproto v0.0.0-20200709005830-7a2ca40e9dc3/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201030142918-24207fddd1c3 h1:sg8vLDNIxFPHTchfhH1E3AI32BL3f23oie38xUWnJM8= +google.golang.org/genproto v0.0.0-20201030142918-24207fddd1c3/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210105202744-fe13368bc0e1 h1:Zk6zlGXdtYdcY5TL+VrbTfmifvk3VvsXopCpszsHPBA= +google.golang.org/genproto v0.0.0-20210105202744-fe13368bc0e1/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -429,9 +500,13 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -443,6 +518,8 @@ gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/scheduling/nodeStateChange.go b/scheduling/nodeStateChange.go index 4d10f5a60bd0f881d416a8a1eaef0a7e16c8a1b9..007cc7d1d1b8a407d8dba919b4d21cff4e70b64d 100644 --- a/scheduling/nodeStateChange.go +++ b/scheduling/nodeStateChange.go @@ -11,13 +11,13 @@ import ( "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" pb "gitlab.com/elixxir/comms/mixmessages" - "gitlab.com/elixxir/crypto/signature" "gitlab.com/elixxir/primitives/current" - "gitlab.com/elixxir/primitives/id" "gitlab.com/elixxir/primitives/states" "gitlab.com/elixxir/registration/storage" "gitlab.com/elixxir/registration/storage/node" "gitlab.com/elixxir/registration/storage/round" + "gitlab.com/xx_network/comms/signature" + "gitlab.com/xx_network/primitives/id" "time" ) @@ -39,6 +39,9 @@ func HandleNodeUpdates(update node.UpdateNotification, pool *waitingPool, state if roundErrored { return nil } + if update.ClientErrors != nil && len(update.ClientErrors) > 0 { + r.AppendClientErrors(update.ClientErrors) + } //ban the node if it is supposed to be banned if update.ToStatus == node.Banned { if hasRound { @@ -275,23 +278,7 @@ func killRound(state *storage.NetworkState, r *round.State, jww.WARN.Printf("Could not insert round error: %+v", err) err = nil } - - // fix a potential error case where a node crashes after the round is - // created but before it updates to precomputing and then gets stuck - topology := r.GetTopology() - for i := 0; i < topology.Len(); i++ { - nid := topology.GetNodeAtIndex(i) - n := state.GetNodeMap().GetNode(nid) - if n != nil { - if n.GetActivity() == current.WAITING { - hasRound, rNode := n.GetCurrentRound() - if hasRound && rNode.GetRoundID() == r.GetRoundID() { - n.ClearRound() - pool.Add(n) - } - } - } - } + state.GetNodeMap().GetNodeStates() return err } diff --git a/scheduling/nodeStateChange_test.go b/scheduling/nodeStateChange_test.go index 4456b3d08ab039967369d68c23949919d9db987f..4148c58a2102add5f20a638b15d4287d6636a2bb 100644 --- a/scheduling/nodeStateChange_test.go +++ b/scheduling/nodeStateChange_test.go @@ -8,13 +8,13 @@ package scheduling import ( "crypto/rand" "gitlab.com/elixxir/comms/mixmessages" - "gitlab.com/elixxir/crypto/signature/rsa" "gitlab.com/elixxir/primitives/current" - "gitlab.com/elixxir/primitives/id" "gitlab.com/elixxir/registration/storage" "gitlab.com/elixxir/registration/storage/node" "gitlab.com/elixxir/registration/storage/round" "gitlab.com/xx_network/comms/connect" + "gitlab.com/xx_network/crypto/signature/rsa" + "gitlab.com/xx_network/primitives/id" "strconv" "testing" "time" @@ -27,10 +27,15 @@ func TestHandleNodeStateChance_Waiting(t *testing.T) { BatchSize: 32, RandomOrdering: false, } + var err error + storage.PermissioningDb, _, err = storage.NewDatabase("", "", "", "", "") + if err != nil { + t.Errorf(err.Error()) + } privKey, _ := rsa.GenerateKey(rand.Reader, 2048) - testState, err := storage.NewState(privKey, "", "") + testState, err := storage.NewState(privKey, 8) if err != nil { t.Errorf("Failed to create test state: %v", err) t.FailNow() @@ -46,7 +51,10 @@ func TestHandleNodeStateChance_Waiting(t *testing.T) { } } - roundID := testState.GetRoundID() + roundID, err := testState.GetRoundID() + if err != nil { + t.Errorf(err.Error()) + } // Set a round for the node in order to fully test the code path for // a waiting transition @@ -81,7 +89,7 @@ func TestHandleNodeStateChance_Waiting_SetNodeToOnline(t *testing.T) { privKey, _ := rsa.GenerateKey(rand.Reader, 2048) - testState, err := storage.NewState(privKey, "", "") + testState, err := storage.NewState(privKey, 8) if err != nil { t.Errorf("Failed to create test state: %v", err) t.FailNow() @@ -97,7 +105,10 @@ func TestHandleNodeStateChance_Waiting_SetNodeToOnline(t *testing.T) { } } - roundID := testState.GetRoundID() + roundID, err := testState.GetRoundID() + if err != nil { + t.Errorf(err.Error()) + } // Set a round for the node in order to fully test the code path for // a waiting transition @@ -138,7 +149,7 @@ func TestHandleNodeStateChance_Standby(t *testing.T) { } privKey, _ := rsa.GenerateKey(rand.Reader, 2048) - testState, err := storage.NewState(privKey, "", "") + testState, err := storage.NewState(privKey, 8) if err != nil { t.Errorf("Failed to create test state: %v", err) t.FailNow() @@ -155,9 +166,12 @@ func TestHandleNodeStateChance_Standby(t *testing.T) { } circuit := connect.NewCircuit(nodeList) - roundID := testState.GetRoundID() + roundID, err := testState.GetRoundID() + if err != nil { + t.Errorf(err.Error()) + } - roundState, err := testState.GetRoundMap().AddRound(roundID, testParams.BatchSize, 5*time.Minute, circuit) + roundState, err := testState.GetRoundMap().AddRound(roundID, testParams.BatchSize, 8, 5*time.Minute, circuit) if err != nil { t.Errorf("Failed to add round: %v", err) } @@ -211,7 +225,7 @@ func TestHandleNodeStateChance_Standby_NoRound(t *testing.T) { privKey, _ := rsa.GenerateKey(rand.Reader, 2048) - testState, err := storage.NewState(privKey, "", "") + testState, err := storage.NewState(privKey, 8) if err != nil { t.Errorf("Failed to create test state: %v", err) t.FailNow() @@ -270,7 +284,7 @@ func TestHandleNodeUpdates_Completed(t *testing.T) { privKey, _ := rsa.GenerateKey(rand.Reader, 2048) - testState, err := storage.NewState(privKey, "", "") + testState, err := storage.NewState(privKey, 8) if err != nil { t.Errorf("Failed to create test state: %v", err) t.FailNow() @@ -287,9 +301,12 @@ func TestHandleNodeUpdates_Completed(t *testing.T) { } circuit := connect.NewCircuit(nodeList) - roundID := testState.GetRoundID() + roundID, err := testState.GetRoundID() + if err != nil { + t.Errorf(err.Error()) + } - roundState, err := testState.GetRoundMap().AddRound(roundID, testParams.BatchSize, 5*time.Minute, circuit) + roundState, err := testState.GetRoundMap().AddRound(roundID, testParams.BatchSize, 8, 5*time.Minute, circuit) if err != nil { t.Errorf("Failed to add round: %v", err) } @@ -341,7 +358,7 @@ func TestHandleNodeUpdates_Completed_NoRound(t *testing.T) { privKey, _ := rsa.GenerateKey(rand.Reader, 2048) - testState, err := storage.NewState(privKey, "", "") + testState, err := storage.NewState(privKey, 8) if err != nil { t.Errorf("Failed to create test state: %v", err) t.FailNow() @@ -386,9 +403,15 @@ func TestHandleNodeUpdates_Error(t *testing.T) { RandomOrdering: false, } + var err error + storage.PermissioningDb, _, err = storage.NewDatabase("", "", "", "", "") + if err != nil { + t.Errorf(err.Error()) + } + privKey, _ := rsa.GenerateKey(rand.Reader, 2048) - testState, err := storage.NewState(privKey, "", "") + testState, err := storage.NewState(privKey, 8) if err != nil { t.Errorf("Failed to create test state: %v", err) t.FailNow() @@ -404,7 +427,10 @@ func TestHandleNodeUpdates_Error(t *testing.T) { } } - roundID := testState.GetRoundID() + roundID, err := testState.GetRoundID() + if err != nil { + t.Errorf(err.Error()) + } topology := connect.NewCircuit(nodeList) // Set a round for the node in order to fully test the code path for @@ -427,7 +453,6 @@ func TestHandleNodeUpdates_Error(t *testing.T) { } testState.GetNodeMap().GetNode(testUpdate.Node).GetPollingLock().Lock() - storage.PermissioningDb, _, err = storage.NewDatabase("", "", "", "", "") testTracker := NewRoundTracker() err = HandleNodeUpdates(testUpdate, testPool, testState, 0, testTracker) @@ -446,7 +471,7 @@ func TestHandleNodeUpdates_BannedNode(t *testing.T) { privKey, _ := rsa.GenerateKey(rand.Reader, 2048) - testState, err := storage.NewState(privKey, "", "") + testState, err := storage.NewState(privKey, 8) if err != nil { t.Errorf("Failed to create test state: %v", err) t.FailNow() @@ -535,7 +560,7 @@ func TestKillRound(t *testing.T) { privKey, _ := rsa.GenerateKey(rand.Reader, 2048) - testState, err := storage.NewState(privKey, "", "") + testState, err := storage.NewState(privKey, 8) if err != nil { t.Errorf("Failed to create test state: %v", err) t.FailNow() @@ -578,75 +603,6 @@ func TestKillRound(t *testing.T) { } } -// Happy path -func TestKillRound_NodeCrash(t *testing.T) { - testParams := Params{ - TeamSize: 5, - BatchSize: 32, - RandomOrdering: false, - } - - privKey, _ := rsa.GenerateKey(rand.Reader, 2048) - - testState, err := storage.NewState(privKey, "", "") - if err != nil { - t.Errorf("Failed to create test state: %v", err) - t.FailNow() - } - - testPool := NewWaitingPool() - - // Build mock nodes and place in map - nodeList := make([]*id.ID, testParams.TeamSize) - nodeStateList := make([]*node.State, testParams.TeamSize) - for i := uint64(0); i < uint64(len(nodeList)); i++ { - nodeList[i] = id.NewIdFromUInt(i, id.Node, t) - err := testState.GetNodeMap().AddNode(nodeList[i], strconv.Itoa(int(i)), "", "", 0) - if err != nil { - t.Errorf("Couldn't add node: %v", err) - t.FailNow() - } - - // Add node to pool - ns := testState.GetNodeMap().GetNode(nodeList[i]) - if i == 0 { - _, _, _ = ns.Update(current.WAITING) - roundState := round.NewState_Testing(42, 6, nil, t) - _ = ns.SetRound(roundState) - } else { - nodeStateList = append(nodeStateList, ns) - testPool.Add(ns) - } - } - - topology := connect.NewCircuit(nodeList) - - r := round.NewState_Testing(42, 0, topology, t) - - re := &mixmessages.RoundError{ - Id: 0, - NodeId: nil, - Error: "test", - } - - tesTracker := NewRoundTracker() - - err = killRound(testState, r, re, tesTracker, testPool) - if err != nil { - t.Errorf("Unexpected error in happy path: %v", err) - } - - if !testPool.pool.Has(testState.GetNodeMap().GetNode(nodeList[0])) { - t.Errorf("The node was not added to the pool.") - } - - _, currentRound := testState.GetNodeMap().GetNode(nodeList[0]).GetCurrentRound() - - if currentRound != nil { - t.Errorf("The round was not cleared.") - } -} - // Tests that the Precomputing case of HandleNodeUpdates produces the correct // error when there is no round. func TestHandleNodeUpdates_Precomputing_RoundError(t *testing.T) { @@ -658,7 +614,7 @@ func TestHandleNodeUpdates_Precomputing_RoundError(t *testing.T) { privKey, _ := rsa.GenerateKey(rand.Reader, 2048) - testState, err := storage.NewState(privKey, "", "") + testState, err := storage.NewState(privKey, 8) if err != nil { t.Errorf("Failed to create test state: %v", err) t.FailNow() @@ -706,7 +662,7 @@ func TestHandleNodeUpdates_Realtime(t *testing.T) { privKey, _ := rsa.GenerateKey(rand.Reader, 2048) - testState, err := storage.NewState(privKey, "", "") + testState, err := storage.NewState(privKey, 8) if err != nil { t.Errorf("Failed to create test state: %v", err) t.FailNow() @@ -722,7 +678,10 @@ func TestHandleNodeUpdates_Realtime(t *testing.T) { } } - roundID := testState.GetRoundID() + roundID, err := testState.GetRoundID() + if err != nil { + t.Errorf(err.Error()) + } // Set a round for the node in order to fully test the code path for // a waiting transition @@ -756,7 +715,7 @@ func TestHandleNodeUpdates_Realtime_RoundError(t *testing.T) { privKey, _ := rsa.GenerateKey(rand.Reader, 2048) - testState, err := storage.NewState(privKey, "", "") + testState, err := storage.NewState(privKey, 8) if err != nil { t.Errorf("Failed to create test state: %v", err) t.FailNow() @@ -805,7 +764,7 @@ func TestHandleNodeUpdates_Realtime_UpdateError(t *testing.T) { privKey, _ := rsa.GenerateKey(rand.Reader, 2048) - testState, err := storage.NewState(privKey, "", "") + testState, err := storage.NewState(privKey, 8) if err != nil { t.Errorf("Failed to create test state: %v", err) t.FailNow() @@ -821,7 +780,10 @@ func TestHandleNodeUpdates_Realtime_UpdateError(t *testing.T) { } } - roundID := testState.GetRoundID() + roundID, err := testState.GetRoundID() + if err != nil { + t.Errorf(err.Error()) + } // Set a round for the node in order to fully test the code path for // a waiting transition @@ -859,7 +821,7 @@ func TestHandleNodeUpdates_RoundErrored(t *testing.T) { privKey, _ := rsa.GenerateKey(rand.Reader, 2048) - testState, err := storage.NewState(privKey, "", "") + testState, err := storage.NewState(privKey, 8) if err != nil { t.Errorf("Failed to create test state: %v", err) t.FailNow() @@ -875,7 +837,10 @@ func TestHandleNodeUpdates_RoundErrored(t *testing.T) { } } - roundID := testState.GetRoundID() + roundID, err := testState.GetRoundID() + if err != nil { + t.Errorf(err.Error()) + } // Set a round for the node in order to fully test the code path for // a waiting transition @@ -910,7 +875,7 @@ func TestHandleNodeUpdates_NOT_STARTED(t *testing.T) { privKey, _ := rsa.GenerateKey(rand.Reader, 2048) - testState, err := storage.NewState(privKey, "", "") + testState, err := storage.NewState(privKey, 8) if err != nil { t.Errorf("Failed to create test state: %v", err) t.FailNow() diff --git a/scheduling/params.go b/scheduling/params.go index 04c9b8d538d8fcbdbc443d226cf750668e8ef4a5..556dee4fa6bd7c349d3394c807d207dad97342c0 100644 --- a/scheduling/params.go +++ b/scheduling/params.go @@ -8,9 +8,9 @@ package scheduling // Contains the scheduling params object and the internal protoround object import ( - "gitlab.com/elixxir/primitives/id" "gitlab.com/elixxir/registration/storage/node" "gitlab.com/xx_network/comms/connect" + "gitlab.com/xx_network/primitives/id" "time" ) diff --git a/scheduling/permute_test.go b/scheduling/permute_test.go index 1961449dae62846576931babc631079c6a714c67..15285b6c2272a66142368259f85ae5b96a38e2f5 100644 --- a/scheduling/permute_test.go +++ b/scheduling/permute_test.go @@ -71,7 +71,7 @@ func TestPermute(t *testing.T) { func factorial(n int) int { factVal := 1 if n < 0 { - fmt.Print("Factorial of negative number doesn't exist.") + fmt.Println("Factorial of negative number doesn't exist.") } else { for i := 1; i <= n; i++ { factVal *= i diff --git a/scheduling/pool_test.go b/scheduling/pool_test.go index 636a2e40d3cd707242924f1ab69d2c87fd974294..f2c59d12919080074dbe29ad0a36e1a5145f662a 100644 --- a/scheduling/pool_test.go +++ b/scheduling/pool_test.go @@ -8,10 +8,10 @@ package scheduling import ( "crypto/rand" "github.com/golang-collections/collections/set" - "gitlab.com/elixxir/crypto/signature/rsa" - "gitlab.com/elixxir/primitives/id" "gitlab.com/elixxir/registration/storage" "gitlab.com/elixxir/registration/storage/node" + "gitlab.com/xx_network/crypto/signature/rsa" + "gitlab.com/xx_network/primitives/id" "reflect" "testing" "time" @@ -338,7 +338,7 @@ func setupNode(t *testing.T, testState *storage.NetworkState, newId uint64) *nod func setupNodeMap(t *testing.T) *storage.NetworkState { // Build network state privKey, _ := rsa.GenerateKey(rand.Reader, 2048) - testState, err := storage.NewState(privKey, "", "") + testState, err := storage.NewState(privKey, 8) if err != nil { t.Errorf("Failed to create test state: %v", err) t.FailNow() diff --git a/scheduling/roundTracker.go b/scheduling/roundTracker.go index b7f2ca8a314e915fc5dcf12d1988a45c930e6437..e8b43de4cf5476d0ff2b6b2607183cdac2e254c6 100644 --- a/scheduling/roundTracker.go +++ b/scheduling/roundTracker.go @@ -9,7 +9,7 @@ package scheduling import ( - "gitlab.com/elixxir/primitives/id" + "gitlab.com/xx_network/primitives/id" "sync" ) diff --git a/scheduling/roundTracker_test.go b/scheduling/roundTracker_test.go index a7272026a9452a02146b8c1095773dea35c9725b..3019fee382aa80eb62486c98ecf4545861bfda02 100644 --- a/scheduling/roundTracker_test.go +++ b/scheduling/roundTracker_test.go @@ -7,7 +7,7 @@ package scheduling import ( - "gitlab.com/elixxir/primitives/id" + "gitlab.com/xx_network/primitives/id" "math/rand" "reflect" "testing" diff --git a/scheduling/schedule.go b/scheduling/schedule.go index 3b2d9f252bf2616c218cef081e5392ccb5f27961..dd1f80d50f8e2dc4b2a7d639c26bdc4355e65952 100644 --- a/scheduling/schedule.go +++ b/scheduling/schedule.go @@ -12,12 +12,12 @@ import ( "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" pb "gitlab.com/elixxir/comms/mixmessages" - "gitlab.com/elixxir/crypto/signature" - "gitlab.com/elixxir/primitives/id" "gitlab.com/elixxir/primitives/states" "gitlab.com/elixxir/registration/storage" "gitlab.com/elixxir/registration/storage/node" "gitlab.com/elixxir/registration/storage/round" + "gitlab.com/xx_network/comms/signature" + "gitlab.com/xx_network/primitives/id" "sync/atomic" "time" ) @@ -124,6 +124,12 @@ func scheduler(params Params, state *storage.NetworkState, killchan chan chan st }() + unstickerQuitChan := make(chan struct{}) + // begin the thread that takes nodes stuck in waiting out of waiting + go func() { + UnstickNodes(state, pool, params.RoundTimeout*time.Second, unstickerQuitChan) + }() + var killed chan struct{} iterationsCount := uint32(0) @@ -144,6 +150,7 @@ func scheduler(params Params, state *storage.NetworkState, killchan chan chan st select { // Receive a signal to kill the scheduler case killed = <-killchan: + // Also kill the unsticker jww.WARN.Printf("Scheduler has recived a kill signal, exit process has begun") // When we get a node update, move past the select statement case update = <-state.GetNodeUpdateChannel(): @@ -210,8 +217,11 @@ func scheduler(params Params, state *storage.NetworkState, killchan chan chan st // If the scheduler is to be killed and no rounds are in progress, // kill the scheduler if killed != nil && roundTracker.Len() == 0 { + // Stop round creation close(newRoundChan) jww.WARN.Printf("Scheduler is exiting due to kill signal") + // Also kill the unsticking thread + unstickerQuitChan <- struct{}{} killed <- struct{}{} return nil } diff --git a/scheduling/schedule_test.go b/scheduling/schedule_test.go index 4a7a91b27e346cdc6e26c81d54833d8f408d65a7..35e5d3036d573056d428f1fc757d6dfcb807ac8b 100644 --- a/scheduling/schedule_test.go +++ b/scheduling/schedule_test.go @@ -2,13 +2,13 @@ package scheduling import ( "encoding/json" - "gitlab.com/elixxir/crypto/signature/rsa" "gitlab.com/elixxir/primitives/current" - "gitlab.com/elixxir/primitives/id" - "gitlab.com/elixxir/primitives/utils" "gitlab.com/elixxir/registration/storage" "gitlab.com/elixxir/registration/storage/node" "gitlab.com/elixxir/registration/testkeys" + "gitlab.com/xx_network/crypto/signature/rsa" + "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/utils" "reflect" "strconv" "testing" @@ -41,7 +41,7 @@ func TestScheduler_NonRandom(t *testing.T) { "PermissioningKey is %+v", err, pk) } // Start registration server - state, err := storage.NewState(pk, "", "") + state, err := storage.NewState(pk, 8) if err != nil { t.Errorf("Unable to create state: %+v", err) } diff --git a/scheduling/secureCreateRound.go b/scheduling/secureCreateRound.go index 6b2408c003199f94356f4016d68b458d891e33c7..0c2acfae2b0d7ea2a166ff529b5b0994c955c3e4 100644 --- a/scheduling/secureCreateRound.go +++ b/scheduling/secureCreateRound.go @@ -4,10 +4,10 @@ import ( "github.com/golang-collections/collections/set" "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" - "gitlab.com/elixxir/primitives/id" "gitlab.com/elixxir/registration/storage" "gitlab.com/elixxir/registration/storage/node" "gitlab.com/xx_network/comms/connect" + "gitlab.com/xx_network/primitives/id" "time" ) diff --git a/scheduling/secureCreateRound_test.go b/scheduling/secureCreateRound_test.go index b63a641fe4448c2e0978de087a55b82e1b29652c..49bdcf4c4da129997152a57ae7ddab59eff9f221 100644 --- a/scheduling/secureCreateRound_test.go +++ b/scheduling/secureCreateRound_test.go @@ -2,11 +2,10 @@ package scheduling import ( "crypto/rand" - "fmt" - "gitlab.com/elixxir/crypto/signature/rsa" - "gitlab.com/elixxir/primitives/id" "gitlab.com/elixxir/registration/storage" "gitlab.com/elixxir/registration/storage/node" + "gitlab.com/xx_network/crypto/signature/rsa" + "gitlab.com/xx_network/primitives/id" mathRand "math/rand" "strconv" @@ -29,7 +28,7 @@ func TestCreateRound(t *testing.T) { // Build network state privKey, _ := rsa.GenerateKey(rand.Reader, 2048) - testState, err := storage.NewState(privKey, "", "") + testState, err := storage.NewState(privKey, 8) if err != nil { t.Errorf("Failed to create test state: %v", err) t.FailNow() @@ -52,7 +51,10 @@ func TestCreateRound(t *testing.T) { testpool.Add(nodeState) } - roundID := testState.GetRoundID() + roundID, err := testState.GetRoundID() + if err != nil { + t.Errorf(err.Error()) + } _, err = createSecureRound(testParams, testpool, roundID, testState, nil) if err != nil { @@ -74,7 +76,7 @@ func TestCreateRound_Error_NotEnoughForTeam(t *testing.T) { // Build network state privKey, _ := rsa.GenerateKey(rand.Reader, 2048) - testState, err := storage.NewState(privKey, "", "") + testState, err := storage.NewState(privKey, 8) if err != nil { t.Errorf("Failed to create test state: %v", err) t.FailNow() @@ -127,7 +129,7 @@ func TestCreateRound_Error_NotEnoughForThreshold(t *testing.T) { // Build network state privKey, _ := rsa.GenerateKey(rand.Reader, 2048) - testState, err := storage.NewState(privKey, "", "") + testState, err := storage.NewState(privKey, 8) if err != nil { t.Errorf("Failed to create test state: %v", err) t.FailNow() @@ -182,7 +184,7 @@ func TestCreateRound_EfficientTeam_AllRegions(t *testing.T) { // Build network state privKey, _ := rsa.GenerateKey(rand.Reader, 2048) - testState, err := storage.NewState(privKey, "", "") + testState, err := storage.NewState(privKey, 8) if err != nil { t.Errorf("Failed to create test state: %v", err) t.FailNow() @@ -281,7 +283,7 @@ func TestCreateRound_EfficientTeam_RandomRegions(t *testing.T) { // Build network state privKey, _ := rsa.GenerateKey(rand.Reader, 2048) - testState, err := storage.NewState(privKey, "", "") + testState, err := storage.NewState(privKey, 8) if err != nil { t.Errorf("Failed to create test state: %v", err) t.FailNow() @@ -331,8 +333,6 @@ func TestCreateRound_EfficientTeam_RandomRegions(t *testing.T) { } duration := time.Now().Sub(start) - fmt.Printf("CreateRound took: %v\n", duration) - expectedDuration := int64(40) // Check that it did not take an excessive amount of time diff --git a/scheduling/simpleCreateRound.go b/scheduling/simpleCreateRound.go index e813bbca6c896d4e95ebab730ba0998791bd13c5..6bf6349dc186443f21381c377b4e75b79b0c1873 100644 --- a/scheduling/simpleCreateRound.go +++ b/scheduling/simpleCreateRound.go @@ -9,10 +9,10 @@ import ( "github.com/golang-collections/collections/set" "github.com/pkg/errors" "gitlab.com/elixxir/crypto/shuffle" - "gitlab.com/elixxir/primitives/id" "gitlab.com/elixxir/registration/storage" "gitlab.com/elixxir/registration/storage/node" "gitlab.com/xx_network/comms/connect" + "gitlab.com/xx_network/primitives/id" "strconv" ) diff --git a/scheduling/simpleCreateRound_test.go b/scheduling/simpleCreateRound_test.go index 59e1beca5128301d181d9c94794c06914e5dbbbd..d33801ede33c5833a3014c1a1e484c154bd6ca40 100644 --- a/scheduling/simpleCreateRound_test.go +++ b/scheduling/simpleCreateRound_test.go @@ -7,11 +7,11 @@ package scheduling import ( "crypto/rand" - "gitlab.com/elixxir/crypto/signature/rsa" - "gitlab.com/elixxir/primitives/id" "gitlab.com/elixxir/registration/storage" "gitlab.com/elixxir/registration/storage/node" "gitlab.com/xx_network/comms/connect" + "gitlab.com/xx_network/crypto/signature/rsa" + "gitlab.com/xx_network/primitives/id" mathRand "math/rand" "reflect" "strconv" @@ -32,7 +32,7 @@ func TestCreateRound_NonRandom(t *testing.T) { // Build network state privKey, _ := rsa.GenerateKey(rand.Reader, 2048) - testState, err := storage.NewState(privKey, "", "") + testState, err := storage.NewState(privKey, 8) if err != nil { t.Errorf("Failed to create test state: %v", err) t.FailNow() @@ -105,7 +105,7 @@ func TestCreateRound_Random(t *testing.T) { // Build network state privKey, _ := rsa.GenerateKey(rand.Reader, 2048) - testState, err := storage.NewState(privKey, "", "") + testState, err := storage.NewState(privKey, 8) if err != nil { t.Errorf("Failed to create test state: %v", err) t.FailNow() @@ -167,7 +167,7 @@ func TestCreateRound_BadOrdering(t *testing.T) { // Build network state privKey, _ := rsa.GenerateKey(rand.Reader, 2048) - testState, err := storage.NewState(privKey, "", "") + testState, err := storage.NewState(privKey, 8) if err != nil { t.Errorf("Failed to create test state: %v", err) t.FailNow() @@ -218,7 +218,7 @@ func TestCreateRound_RandomOrdering(t *testing.T) { // Build network state privKey, _ := rsa.GenerateKey(rand.Reader, 2048) - testState, err := storage.NewState(privKey, "", "") + testState, err := storage.NewState(privKey, 8) if err != nil { t.Errorf("Failed to create test state: %v", err) t.FailNow() @@ -292,7 +292,7 @@ func TestCreateSimpleRound_SemiOptimal(t *testing.T) { // Build network state privKey, _ := rsa.GenerateKey(rand.Reader, 2048) - testState, err := storage.NewState(privKey, "", "") + testState, err := storage.NewState(privKey, 8) if err != nil { t.Errorf("Failed to create test state: %v", err) t.FailNow() @@ -401,7 +401,7 @@ func TestCreateSimpleRound_SemiOptimal_BadRegion(t *testing.T) { // Build network state privKey, _ := rsa.GenerateKey(rand.Reader, 2048) - testState, err := storage.NewState(privKey, "", "") + testState, err := storage.NewState(privKey, 8) if err != nil { t.Errorf("Failed to create test state: %v", err) t.FailNow() diff --git a/scheduling/startRound.go b/scheduling/startRound.go index 95cbcd17a8acfdcee9960564e830ed9bbbbf0a9b..01e3bf5b298260e1c868ca7db55ba41fdd95e80b 100644 --- a/scheduling/startRound.go +++ b/scheduling/startRound.go @@ -6,8 +6,10 @@ package scheduling import ( + "fmt" "github.com/pkg/errors" "github.com/spf13/jwalterweatherman" + jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/primitives/states" "gitlab.com/elixxir/registration/storage" "gitlab.com/elixxir/registration/storage/round" @@ -18,7 +20,7 @@ import ( // node and network states in order to begin the round func startRound(round protoRound, state *storage.NetworkState, roundTracker *RoundTracker) (*round.State, error) { // Add the round to the manager - r, err := state.GetRoundMap().AddRound(round.ID, round.BatchSize, round.ResourceQueueTimeout, + r, err := state.GetRoundMap().AddRound(round.ID, round.BatchSize, state.GetAddressSpaceSize(), round.ResourceQueueTimeout, round.Topology) if err != nil { err = errors.WithMessagef(err, "Failed to create new round %v", round.ID) @@ -54,5 +56,12 @@ func startRound(round protoRound, state *storage.NetworkState, roundTracker *Rou // Add round to active set of rounds roundTracker.AddActiveRound(r.GetRoundID()) + //print the round to the log + roundPrnt := fmt.Sprintf("Scheduling round %d with nodes: ", round.ID) + for i := 0; i < round.Topology.Len(); i++ { + roundPrnt += fmt.Sprintf("\n\t (%d/%d) %s", i+1, round.Topology.Len(), round.Topology.GetNodeAtIndex(i)) + } + jww.DEBUG.Println(roundPrnt) + return r, nil } diff --git a/scheduling/startRound_test.go b/scheduling/startRound_test.go index 38d60d709646f2d46b73f461a6529e0270ddf3db..cf61f93fae96435cde0f9e10eb4de013c7f1cb86 100644 --- a/scheduling/startRound_test.go +++ b/scheduling/startRound_test.go @@ -7,12 +7,12 @@ package scheduling import ( "crypto/rand" - "gitlab.com/elixxir/crypto/signature/rsa" - "gitlab.com/elixxir/primitives/id" "gitlab.com/elixxir/primitives/states" "gitlab.com/elixxir/registration/storage" "gitlab.com/elixxir/registration/storage/node" "gitlab.com/elixxir/registration/storage/round" + "gitlab.com/xx_network/crypto/signature/rsa" + "gitlab.com/xx_network/primitives/id" "testing" ) @@ -29,9 +29,15 @@ func TestStartRound(t *testing.T) { NodeCleanUpInterval: 3, } + var err error + storage.PermissioningDb, _, err = storage.NewDatabase("", "", "", "", "") + if err != nil { + t.Errorf(err.Error()) + } + // Build network state privKey, _ := rsa.GenerateKey(rand.Reader, 2048) - testState, err := storage.NewState(privKey, "", "") + testState, err := storage.NewState(privKey, 8) if err != nil { t.Errorf("Failed to create test state: %v", err) t.FailNow() @@ -91,9 +97,15 @@ func TestStartRound_BadState(t *testing.T) { NodeCleanUpInterval: 3, } + var err error + storage.PermissioningDb, _, err = storage.NewDatabase("", "", "", "", "") + if err != nil { + t.Errorf(err.Error()) + } + // Build network state privKey, _ := rsa.GenerateKey(rand.Reader, 2048) - testState, err := storage.NewState(privKey, "", "") + testState, err := storage.NewState(privKey, 8) if err != nil { t.Errorf("Failed to create test state: %v", err) t.FailNow() @@ -160,7 +172,7 @@ func TestStartRound_BadNode(t *testing.T) { // Build network state privKey, _ := rsa.GenerateKey(rand.Reader, 2048) - testState, err := storage.NewState(privKey, "", "") + testState, err := storage.NewState(privKey, 8) if err != nil { t.Errorf("Failed to create test state: %v", err) t.FailNow() diff --git a/scheduling/trackRounds.go b/scheduling/trackRounds.go index bdd539a4335f024e3cdff9dae243bcf3bd4ad930..dcfbb4337b5b59fcd6787223383fe5bc719dadcb 100644 --- a/scheduling/trackRounds.go +++ b/scheduling/trackRounds.go @@ -28,6 +28,7 @@ func trackRounds(params Params, state *storage.NetworkState, pool *waitingPool, notUpdating := make([]string, 0) goodNode := make([]string, 0) noContact := make([]string, 0) + banned := make([]string, 0) precompRounds := make([]*round.State, 0) queuedRounds := make([]*round.State, 0) @@ -43,6 +44,13 @@ func trackRounds(params Params, state *storage.NetworkState, pool *waitingPool, nodeStates := state.GetNodeMap().GetNodeStates() for _, nodeState := range nodeStates { + + if nodeState.IsBanned(){ + bStr := fmt.Sprintf("\tNode %s (AppID: %v) is banned ", nodeState.GetID(), nodeState.GetAppID()) + banned = append(banned, bStr) + continue + } + switch nodeState.GetActivity() { case current.WAITING: waitingNodes++ @@ -136,6 +144,7 @@ func trackRounds(params Params, state *storage.NetworkState, pool *waitingPool, jww.INFO.Printf("Nodes without recent poll: %v", len(noPoll)) jww.INFO.Printf("Nodes without recent update: %v", len(notUpdating)) jww.INFO.Printf("Normally operating nodes: %v", len(nodeStates)-len(noPoll)-len(notUpdating)) + jww.INFO.Printf("Banned nodes: %v", len(banned)) jww.INFO.Printf("") if len(goodNode) > 0 { @@ -169,6 +178,14 @@ func trackRounds(params Params, state *storage.NetworkState, pool *waitingPool, jww.INFO.Printf("") } + if len(banned)>0{ + jww.INFO.Printf("Banned nodes:") + for _, s := range banned{ + jww.INFO.Print(s) + } + jww.INFO.Printf("") + } + allRounds := precompRounds allRounds = append(allRounds, queuedRounds...) allRounds = append(allRounds, realtimeRounds...) diff --git a/scheduling/unstickNodes.go b/scheduling/unstickNodes.go new file mode 100644 index 0000000000000000000000000000000000000000..3c2eec73053383d3f391bff38fc7120234489784 --- /dev/null +++ b/scheduling/unstickNodes.go @@ -0,0 +1,45 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 Privategrity Corporation / +// / +// All rights reserved. / +//////////////////////////////////////////////////////////////////////////////// + +// Contains a fix for a bug where nodes get stuck in waiting +package scheduling + +import ( + "gitlab.com/elixxir/primitives/current" + "gitlab.com/elixxir/registration/storage" + "gitlab.com/elixxir/registration/storage/node" + "time" +) + +// Detect nodes that are stuck on waiting with no current round running +// Then, add them back to the active pool so they can run rounds again +func unstickNodes(state *storage.NetworkState, pool *waitingPool, roundTimeout time.Duration) { + // Check states of all nodes + nodeStates := state.GetNodeMap().GetNodeStates() + for i := range nodeStates { + thisNode := nodeStates[i] + _, thisRound := nodeStates[i].GetCurrentRound() + // add the node back to the waiting pool if certain conditions are met + if thisRound != nil && time.Since(thisRound.GetLastUpdate()) > 2*roundTimeout && thisNode.GetStatus() == node.Active && thisNode.GetActivity() == current.WAITING { + thisNode.ClearRound() + pool.Add(thisNode) + } + } +} + +// Runner that unsticks nodes periodically +// Exported methods should have only exported types for params! Right? +func UnstickNodes(state *storage.NetworkState, pool *waitingPool, roundTimeout time.Duration, quitChan chan struct{}) { + unstickNodeTicker := time.NewTicker(2 * roundTimeout) + for cont := true; cont; { + select { + case <-unstickNodeTicker.C: + unstickNodes(state, pool, roundTimeout) + case <-quitChan: + cont = false + } + } +} diff --git a/storage/registrationDb.go b/storage/clientDb.go similarity index 67% rename from storage/registrationDb.go rename to storage/clientDb.go index c527b3162b060e7400f4dc972232606a5c9b74c4..cf05621bc2f14be1ee4fbda3a25d12328bcbb08f 100644 --- a/storage/registrationDb.go +++ b/storage/clientDb.go @@ -4,7 +4,8 @@ // All rights reserved. / //////////////////////////////////////////////////////////////////////////////// -// Handles the database ORM for clients +// Handles the DatabaseImpl for client-related functionality +//+build !stateless package storage @@ -14,20 +15,20 @@ import ( ) // Inserts client registration code with given number of uses -func (m *DatabaseImpl) InsertClientRegCode(code string, uses int) error { +func (d *DatabaseImpl) InsertClientRegCode(code string, uses int) error { jww.INFO.Printf("Inserting code %s, %d uses remaining", code, uses) - return m.db.Create(&RegistrationCode{ + return d.db.Create(&RegistrationCode{ Code: code, RemainingUses: uses, }).Error } // If client registration code is valid, decrements remaining uses -func (m *DatabaseImpl) UseCode(code string) error { +func (d *DatabaseImpl) UseCode(code string) error { // Look up given registration code regCode := RegistrationCode{} jww.INFO.Printf("Attempting to use code %s...", code) - err := m.db.First(®Code, "code = ?", code).Error + err := d.db.First(®Code, "code = ?", code).Error if err != nil { // Unable to find code, return error return err @@ -40,7 +41,7 @@ func (m *DatabaseImpl) UseCode(code string) error { // Decrement remaining uses by one regCode.RemainingUses -= 1 - err = m.db.Save(®Code).Error + err = d.db.Save(®Code).Error if err != nil { return err } @@ -50,17 +51,18 @@ func (m *DatabaseImpl) UseCode(code string) error { return nil } -// Gets User from the database -func (m *DatabaseImpl) GetUser(publicKey string) (*User, error) { +// Gets User from the Database +func (d *DatabaseImpl) GetUser(publicKey string) (*User, error) { user := &User{} - result := m.db.First(&user, "public_key = ?", publicKey) + result := d.db.First(&user, "public_key = ?", publicKey) return user, result.Error } -// Inserts User into the database -func (m *DatabaseImpl) InsertUser(publicKey string) error { +// Inserts User into the Database +func (d *DatabaseImpl) InsertUser(publicKey, receptionKey string) error { user := &User{ - PublicKey: publicKey, + PublicKey: publicKey, + ReceptionKey: receptionKey, } - return m.db.Create(user).Error + return d.db.Create(user).Error } diff --git a/storage/registrationMap.go b/storage/clientMap.go similarity index 88% rename from storage/registrationMap.go rename to storage/clientMap.go index 9e89442db1c235fc40ae3e47a7d4ed1cd45dcf52..5f228a914cad2ebd46f1f615a492d60b036d653e 100644 --- a/storage/registrationMap.go +++ b/storage/clientMap.go @@ -4,7 +4,7 @@ // All rights reserved. / //////////////////////////////////////////////////////////////////////////////// -// Handles the Map backend for registration codes +// Handles the MapImpl for client-based functionality package storage @@ -58,16 +58,17 @@ func (m *MapImpl) UseCode(code string) error { // Gets User from the map func (m *MapImpl) GetUser(publicKey string) (*User, error) { - if ok := m.users[publicKey]; ok { + if rk, ok := m.users[publicKey]; ok { return &User{ - PublicKey: publicKey, + PublicKey: publicKey, + ReceptionKey: rk, }, nil } return nil, errors.New("user does not exist") } // Inserts User into the map -func (m *MapImpl) InsertUser(publicKey string) error { - m.users[publicKey] = true +func (m *MapImpl) InsertUser(publicKey, receptionKey string) error { + m.users[publicKey] = receptionKey return nil } diff --git a/storage/registrationMap_test.go b/storage/clientMap_test.go similarity index 94% rename from storage/registrationMap_test.go rename to storage/clientMap_test.go index 9f4829401a3be0ebf853dd9e40055e5f1f494368..c28e7ad8736fe9f82a3ab5a6d40db31af3f71d90 100644 --- a/storage/registrationMap_test.go +++ b/storage/clientMap_test.go @@ -101,12 +101,12 @@ func TestMapImpl_UseCode_Invalid(t *testing.T) { // Happy path func TestMapImpl_InsertUser(t *testing.T) { m := &MapImpl{ - users: make(map[string]bool), + users: make(map[string]string), } testKey := "TEST" - _ = m.InsertUser(testKey) - if !m.users[testKey] { + _ = m.InsertUser(testKey, testKey) + if _, ok := m.users[testKey]; !ok { t.Errorf("Insert failed to add the user!") } } @@ -114,11 +114,11 @@ func TestMapImpl_InsertUser(t *testing.T) { // Happy path func TestMapImpl_GetUser(t *testing.T) { m := &MapImpl{ - users: make(map[string]bool), + users: make(map[string]string), } testKey := "TEST" - m.users[testKey] = true + m.users[testKey] = testKey user, err := m.GetUser(testKey) if err != nil || user.PublicKey != testKey { @@ -129,7 +129,7 @@ func TestMapImpl_GetUser(t *testing.T) { // Get user that does not exist func TestMapImpl_GetUserNotExists(t *testing.T) { m := &MapImpl{ - users: make(map[string]bool), + users: make(map[string]string), } testKey := "TEST" diff --git a/storage/database.go b/storage/database.go index 3df7dbcdb72034dfdc315cd093c2cd3f71e7f86a..c4118038085bdf90187eeb93437569ee2681a810 100644 --- a/storage/database.go +++ b/storage/database.go @@ -4,7 +4,8 @@ // All rights reserved. / //////////////////////////////////////////////////////////////////////////////// -// Handles high level database control +// Handles Database backend functionality +//+build !stateless package storage @@ -13,223 +14,28 @@ import ( "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/postgres" jww "github.com/spf13/jwalterweatherman" - "gitlab.com/elixxir/primitives/id" - "gitlab.com/elixxir/registration/storage/node" - "sync" "time" ) // Struct implementing the Database Interface with an underlying DB type DatabaseImpl struct { - db *gorm.DB // Stored database connection + db *gorm.DB // Stored Database connection } -// Struct implementing the Database Interface with an underlying Map -type MapImpl struct { - clients map[string]*RegistrationCode - nodes map[string]*Node - users map[string]bool - applications map[uint64]*Application - nodeMetrics map[uint64]*NodeMetric - nodeMetricCounter uint64 - roundMetrics map[uint64]*RoundMetric - mut sync.Mutex -} - -// Global variable for database interaction -var PermissioningDb Storage - -type NodeRegistration interface { - // If Node registration code is valid, add Node information - RegisterNode(id *id.ID, salt []byte, code, serverAddr, serverCert, - gatewayAddress, gatewayCert string) error - // Update the Salt for a given Node ID - UpdateSalt(id *id.ID, salt []byte) error - // Get Node information for the given Node registration code - GetNode(code string) (*Node, error) - // Get Node information for the given Node ID - GetNodeById(id *id.ID) (*Node, error) - // Return all nodes in storage with the given Status - GetNodesByStatus(status node.Status) ([]*Node, error) - // Insert Application object along with associated unregistered Node - InsertApplication(application *Application, unregisteredNode *Node) error - // Insert NodeMetric object - InsertNodeMetric(metric *NodeMetric) error - // Insert RoundMetric object with associated topology - InsertRoundMetric(metric *RoundMetric, topology [][]byte) error - // Insert RoundError object - InsertRoundError(roundId id.Round, errStr string) error - UpdateNodeAddresses(id *id.ID, nodeAddr, gwAddr string) error -} - -type ClientRegistration interface { - // Inserts Client registration code with given number of uses - InsertClientRegCode(code string, uses int) error - // If Client registration code is valid, decrements remaining uses - UseCode(code string) error - // Gets User from the database - GetUser(publicKey string) (*User, error) - // Inserts User into the database - InsertUser(publicKey string) error -} - -// Interface database storage operations -type Storage struct { - ClientRegistration - NodeRegistration -} - -// Struct representing a RegistrationCode table in the database -type RegistrationCode struct { - // Registration code acts as the primary key - Code string `gorm:"primary_key"` - // Remaining uses for the RegistrationCode - RemainingUses int -} - -// Struct representing the User table in the database -type User struct { - // User TLS public certificate in PEM string format - PublicKey string `gorm:"primary_key"` -} - -// Struct representing the Node's Application table in the database -type Application struct { - // The Application's unique ID - Id uint64 `gorm:"primary_key;AUTO_INCREMENT:false"` - // Each Application has one Node - Node Node `gorm:"foreignkey:ApplicationId"` - - // Node information - Name string - Url string - Blurb string - Other string - - // Location string for the Node - Location string - // Geographic bin of the Node's location - GeoBin string - // GPS location of the Node - GpsLocation string - // Specifies the team the node was assigned - Team string - // Specifies which network the node is in - Network string - - // Social media - Forum string - Email string - Twitter string - Discord string - Instagram string - Medium string -} - -// Struct representing the Node table in the database -type Node struct { - // Registration code acts as the primary key - Code string `gorm:"primary_key"` - // Node order string, this is a tag used by the algorithm - Sequence string - - // Unique Node ID - Id []byte `gorm:"UNIQUE_INDEX;default: null"` - // Salt used for generation of Node ID - Salt []byte - // Server IP address - ServerAddress string - // Gateway IP address - GatewayAddress string - // Node TLS public certificate in PEM string format - NodeCertificate string - // Gateway TLS public certificate in PEM string format - GatewayCertificate string - - // Date/time that the node was registered - DateRegistered time.Time - // Node's network status - Status uint8 `gorm:"NOT NULL"` - - // Unique ID of the Node's Application - ApplicationId uint64 `gorm:"UNIQUE_INDEX;NOT NULL;type:bigint REFERENCES applications(id)"` - - // Each Node has many Node Metrics - NodeMetrics []NodeMetric `gorm:"foreignkey:NodeId;association_foreignkey:Id"` - - // Each Node participates in many Rounds - Topologies []Topology `gorm:"foreignkey:NodeId;association_foreignkey:Id"` -} - -// Struct representing Node Metrics table in the database -type NodeMetric struct { - // Auto-incrementing primary key (Do not set) - Id uint64 `gorm:"primary_key;AUTO_INCREMENT:true"` - // Node has many NodeMetrics - NodeId []byte `gorm:"INDEX;NOT NULL;type:bytea REFERENCES nodes(Id)"` - // Start time of monitoring period - StartTime time.Time `gorm:"NOT NULL"` - // End time of monitoring period - EndTime time.Time `gorm:"NOT NULL"` - // Number of pings responded to during monitoring period - NumPings uint64 `gorm:"NOT NULL"` -} - -// Junction table for the many-to-many relationship between Nodes & RoundMetrics -type Topology struct { - // Composite primary key - NodeId []byte `gorm:"primary_key;type:bytea REFERENCES nodes(Id)"` - RoundMetricId uint64 `gorm:"INDEX;primary_key;type:bigint REFERENCES round_metrics(Id)"` - - // Order in the topology of a Node for a given Round - Order uint8 `gorm:"NOT NULL"` -} - -// Struct representing Round Metrics table in the database -type RoundMetric struct { - // Unique ID of the round as assigned by the network - Id uint64 `gorm:"primary_key;AUTO_INCREMENT:false"` - - // Round timestamp information - PrecompStart time.Time `gorm:"NOT NULL"` - PrecompEnd time.Time `gorm:"NOT NULL"` - RealtimeStart time.Time `gorm:"NOT NULL"` - RealtimeEnd time.Time `gorm:"NOT NULL;INDEX;"` // Index for TPS calc - BatchSize uint32 `gorm:"NOT NULL"` - - // Each RoundMetric has many Nodes participating in each Round - Topologies []Topology `gorm:"foreignkey:RoundMetricId;association_foreignkey:Id"` - - // Each RoundMetric can have many Errors in each Round - RoundErrors []RoundError `gorm:"foreignkey:RoundMetricId;association_foreignkey:Id"` -} - -// Struct representing Round Errors table in the database -type RoundError struct { - // Auto-incrementing primary key (Do not set) - Id uint64 `gorm:"primary_key;AUTO_INCREMENT:true"` - - // ID of the round for a given run of the network - RoundMetricId uint64 `gorm:"INDEX;NOT NULL;type:bigint REFERENCES round_metrics(Id)"` - - // String of error that occurred during the Round - Error string `gorm:"NOT NULL"` -} - -// Initialize the Database interface with database backend +// Initialize the database interface with Database backend // Returns a Storage interface, Close function, and error func NewDatabase(username, password, database, address, port string) (Storage, func() error, error) { var err error var db *gorm.DB - //connect to the database if the correct information is provided + //connect to the Database if the correct information is provided if address != "" && port != "" { - // Create the database connection + // Create the Database connection connectString := fmt.Sprintf( "host=%s port=%s user=%s dbname=%s sslmode=disable", address, port, username, database) - // Handle empty database password + // Handle empty Database password if len(password) > 0 { connectString += fmt.Sprintf(" password=%s", password) } @@ -237,47 +43,35 @@ func NewDatabase(username, password, database, address, } // Return the map-backend interface - // in the event there is a database error or information is not provided + // in the event there is a Database error or information is not provided if (address == "" || port == "") || err != nil { if err != nil { - jww.WARN.Printf("Unable to initialize database backend: %+v", err) + jww.WARN.Printf("Unable to initialize Database backend: %+v", err) } else { jww.WARN.Printf("Database backend connection information not provided") } - defer jww.INFO.Println("Map backend initialized successfully!") - - return Storage{ - ClientRegistration: ClientRegistration(&MapImpl{ - clients: make(map[string]*RegistrationCode), - users: make(map[string]bool), - }), - NodeRegistration: NodeRegistration(&MapImpl{ - applications: make(map[uint64]*Application), - nodes: make(map[string]*Node), - nodeMetrics: make(map[uint64]*NodeMetric), - roundMetrics: make(map[uint64]*RoundMetric), - })}, func() error { return nil }, nil + return NewMap(), func() error { return nil }, nil } - // Initialize the database logger + // Initialize the Database logger db.SetLogger(jww.TRACE) db.LogMode(true) // SetMaxIdleConns sets the maximum number of connections in the idle connection pool. db.DB().SetMaxIdleConns(10) - // SetMaxOpenConns sets the maximum number of open connections to the database. + // SetMaxOpenConns sets the maximum number of open connections to the Database. db.DB().SetMaxOpenConns(100) // SetConnMaxLifetime sets the maximum amount of time a connection may be reused. db.DB().SetConnMaxLifetime(24 * time.Hour) - // Initialize the database schema - // WARNING: Order is important. Do not change without database testing + // Initialize the Database schema + // WARNING: Order is important. Do not change without Database testing models := []interface{}{ - &RegistrationCode{}, &User{}, + &RegistrationCode{}, &User{}, &State{}, &Application{}, &Node{}, &RoundMetric{}, &Topology{}, &NodeMetric{}, - &RoundError{}, + &RoundError{}, EphemeralLength{}, } for _, model := range models { err = db.AutoMigrate(model).Error @@ -286,46 +80,7 @@ func NewDatabase(username, password, database, address, } } - // Build the interface - di := &DatabaseImpl{ - db: db, - } - jww.INFO.Println("Database backend initialized successfully!") - return Storage{ - ClientRegistration: di, - NodeRegistration: di, - }, db.Close, nil + return Storage{&DatabaseImpl{db: db}}, db.Close, nil } - -// Adds Client registration codes to the database -func PopulateClientRegistrationCodes(codes []string, uses int) { - for _, code := range codes { - err := PermissioningDb.InsertClientRegCode(code, uses) - if err != nil { - jww.ERROR.Printf("Unable to populate Client registration code: %+v", - err) - } - } -} - -// Adds Node registration codes to the database -func PopulateNodeRegistrationCodes(infos []node.Info) { - // TODO: This will eventually need to be updated to intake applications too - i := 1 - for _, info := range infos { - err := PermissioningDb.InsertApplication(&Application{ - Id: uint64(i), - }, &Node{ - Code: info.RegCode, - Sequence: info.Order, - ApplicationId: uint64(i), - }) - if err != nil { - jww.ERROR.Printf("Unable to populate Node registration code: %+v", - err) - } - i++ - } -} diff --git a/storage/disabledNodes.go b/storage/disabledNodes.go index 1605c207cf91041546d1d26417bac64a34cbb573..e520a7ff33216a1f9c6d03befdc7dfbc70cd9232 100644 --- a/storage/disabledNodes.go +++ b/storage/disabledNodes.go @@ -14,9 +14,9 @@ import ( "github.com/golang-collections/collections/set" "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" - "gitlab.com/elixxir/primitives/id" - "gitlab.com/elixxir/primitives/utils" "gitlab.com/elixxir/registration/storage/node" + "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/utils" "strings" "sync" "time" diff --git a/storage/disabledNodes_test.go b/storage/disabledNodes_test.go index c4ec03c4dfefc0541beab48d942b725489f55eae..34bc05a67848571e0bf19c03ea5cdb50a1ec825f 100644 --- a/storage/disabledNodes_test.go +++ b/storage/disabledNodes_test.go @@ -9,9 +9,9 @@ package storage import ( "crypto/rand" "github.com/golang-collections/collections/set" - "gitlab.com/elixxir/primitives/id" - "gitlab.com/elixxir/primitives/utils" "gitlab.com/elixxir/registration/storage/node" + "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/utils" "os" "reflect" "strings" diff --git a/storage/interface.go b/storage/interface.go new file mode 100644 index 0000000000000000000000000000000000000000..489e7a49068c06eeb93b71e1ebf580aa092643b5 --- /dev/null +++ b/storage/interface.go @@ -0,0 +1,264 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2018 Privategrity Corporation / +// / +// All rights reserved. / +//////////////////////////////////////////////////////////////////////////////// + +// Handles low level Database structures and interfaces + +package storage + +import ( + jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/registration/storage/node" + "gitlab.com/xx_network/primitives/id" + "sync" + "time" +) + +// Interface declaration for Storage methods +type database interface { + // Permissioning methods + UpsertState(state *State) error + GetStateValue(key string) (string, error) + InsertNodeMetric(metric *NodeMetric) error + InsertRoundMetric(metric *RoundMetric, topology [][]byte) error + InsertRoundError(roundId id.Round, errStr string) error + GetLatestEphemeralLength() (*EphemeralLength, error) + GetEphemeralLengths() ([]*EphemeralLength, error) + InsertEphemeralLength(length *EphemeralLength) error + + // Node methods + InsertApplication(application *Application, unregisteredNode *Node) error + RegisterNode(id *id.ID, salt []byte, code, serverAddr, serverCert, + gatewayAddress, gatewayCert string) error + UpdateSalt(id *id.ID, salt []byte) error + GetNode(code string) (*Node, error) + GetNodeById(id *id.ID) (*Node, error) + GetNodesByStatus(status node.Status) ([]*Node, error) + UpdateNodeAddresses(id *id.ID, nodeAddr, gwAddr string) error + + // Client methods + InsertClientRegCode(code string, uses int) error + UseCode(code string) error + GetUser(publicKey string) (*User, error) + InsertUser(publicKey, receptionKey string) error +} + +// Struct implementing the Database Interface with an underlying Map +type MapImpl struct { + clients map[string]*RegistrationCode + nodes map[string]*Node + users map[string]string + applications map[uint64]*Application + nodeMetrics map[uint64]*NodeMetric + nodeMetricCounter uint64 + roundMetrics map[uint64]*RoundMetric + states map[string]string + ephemeralLengths map[uint8]*EphemeralLength + mut sync.Mutex +} + +// Key-Value store used for persisting Permissioning State information +type State struct { + Key string `gorm:"primary_key"` + Value string `gorm:"NOT NULL"` +} + +// Enumerates Keys in the State table +const ( + UpdateIdKey = "UpdateId" + RoundIdKey = "RoundId" +) + +// Struct representing a RegistrationCode table in the Database +type RegistrationCode struct { + // Registration code acts as the primary key + Code string `gorm:"primary_key"` + // Remaining uses for the RegistrationCode + RemainingUses int +} + +// Struct representing the User table in the Database +type User struct { + // User TLS public certificate in PEM string format + PublicKey string `gorm:"primary_key"` + // User reception key in PEM string format + ReceptionKey string `gorm:"NOT NULL;UNIQUE"` +} + +// Struct representing the Node's Application table in the Database +type Application struct { + // The Application's unique ID + Id uint64 `gorm:"primary_key;AUTO_INCREMENT:false"` + // Each Application has one Node + Node Node `gorm:"foreignkey:ApplicationId"` + + // Node information + Name string + Url string + Blurb string + Other string + + // Location string for the Node + Location string + // Geographic bin of the Node's location + GeoBin string + // GPS location of the Node + GpsLocation string + // Specifies the team the node was assigned + Team string + // Specifies which network the node is in + Network string + + // Social media + Forum string + Email string + Twitter string + Discord string + Instagram string + Medium string +} + +// Struct representing the Node table in the Database +type Node struct { + // Registration code acts as the primary key + Code string `gorm:"primary_key"` + // Node order string, this is a tag used by the algorithm + Sequence string + + // Unique Node ID + Id []byte `gorm:"UNIQUE_INDEX;default: null"` + // Salt used for generation of Node ID + Salt []byte + // Server IP address + ServerAddress string + // Gateway IP address + GatewayAddress string + // Node TLS public certificate in PEM string format + NodeCertificate string + // Gateway TLS public certificate in PEM string format + GatewayCertificate string + + // Date/time that the node was registered + DateRegistered time.Time + // Node's network status + Status uint8 `gorm:"NOT NULL"` + + // Unique ID of the Node's Application + ApplicationId uint64 `gorm:"UNIQUE_INDEX;NOT NULL;type:bigint REFERENCES applications(id)"` + + // Each Node has many Node Metrics + NodeMetrics []NodeMetric `gorm:"foreignkey:NodeId;association_foreignkey:Id"` + + // Each Node participates in many Rounds + Topologies []Topology `gorm:"foreignkey:NodeId;association_foreignkey:Id"` +} + +// Struct representing Node Metrics table in the Database +type NodeMetric struct { + // Auto-incrementing primary key (Do not set) + Id uint64 `gorm:"primary_key;AUTO_INCREMENT:true"` + // Node has many NodeMetrics + NodeId []byte `gorm:"INDEX;NOT NULL;type:bytea REFERENCES nodes(Id)"` + // Start time of monitoring period + StartTime time.Time `gorm:"NOT NULL"` + // End time of monitoring period + EndTime time.Time `gorm:"NOT NULL"` + // Number of pings responded to during monitoring period + NumPings uint64 `gorm:"NOT NULL"` +} + +// Junction table for the many-to-many relationship between Nodes & RoundMetrics +type Topology struct { + // Composite primary key + NodeId []byte `gorm:"primary_key;type:bytea REFERENCES nodes(Id)"` + RoundMetricId uint64 `gorm:"INDEX;primary_key;type:bigint REFERENCES round_metrics(Id)"` + + // Order in the topology of a Node for a given Round + Order uint8 `gorm:"NOT NULL"` +} + +// Struct representing Round Metrics table in the Database +type RoundMetric struct { + // Unique ID of the round as assigned by the network + Id uint64 `gorm:"primary_key;AUTO_INCREMENT:false"` + + // Round timestamp information + PrecompStart time.Time `gorm:"NOT NULL"` + PrecompEnd time.Time `gorm:"NOT NULL"` + RealtimeStart time.Time `gorm:"NOT NULL"` + RealtimeEnd time.Time `gorm:"NOT NULL;INDEX;"` // Index for TPS calc + BatchSize uint32 `gorm:"NOT NULL"` + + // Each RoundMetric has many Nodes participating in each Round + Topologies []Topology `gorm:"foreignkey:RoundMetricId;association_foreignkey:Id"` + + // Each RoundMetric can have many Errors in each Round + RoundErrors []RoundError `gorm:"foreignkey:RoundMetricId;association_foreignkey:Id"` +} + +// Struct representing Round Errors table in the Database +type RoundError struct { + // Auto-incrementing primary key (Do not set) + Id uint64 `gorm:"primary_key;AUTO_INCREMENT:true"` + + // ID of the round for a given run of the network + RoundMetricId uint64 `gorm:"INDEX;NOT NULL;type:bigint REFERENCES round_metrics(Id)"` + + // String of error that occurred during the Round + Error string `gorm:"NOT NULL"` +} + +// Struct representing the validity period of an ephemeral ID length +type EphemeralLength struct { + Length uint8 `gorm:"primary_key;AUTO_INCREMENT:false"` + Timestamp time.Time `gorm:"NOT NULL;UNIQUE"` +} + +// Initialize the database interface with Map backend +func NewMap() Storage { + defer jww.INFO.Println("Map backend initialized successfully!") + return Storage{ + &MapImpl{ + applications: make(map[uint64]*Application), + nodes: make(map[string]*Node), + nodeMetrics: make(map[uint64]*NodeMetric), + roundMetrics: make(map[uint64]*RoundMetric), + clients: make(map[string]*RegistrationCode), + users: make(map[string]string), + states: make(map[string]string), + ephemeralLengths: make(map[uint8]*EphemeralLength), + }} +} + +// Adds Client registration codes to the Database +func PopulateClientRegistrationCodes(codes []string, uses int) { + for _, code := range codes { + err := PermissioningDb.InsertClientRegCode(code, uses) + if err != nil { + jww.ERROR.Printf("Unable to populate Client registration code: %+v", + err) + } + } +} + +// Adds Node registration codes to the Database +func PopulateNodeRegistrationCodes(infos []node.Info) { + // TODO: This will eventually need to be updated to intake applications too + i := 1 + for _, info := range infos { + err := PermissioningDb.InsertApplication(&Application{ + Id: uint64(i), + }, &Node{ + Code: info.RegCode, + Sequence: info.Order, + ApplicationId: uint64(i), + }) + if err != nil { + jww.ERROR.Printf("Unable to populate Node registration code: %+v", + err) + } + i++ + } +} diff --git a/storage/map.go b/storage/map.go new file mode 100644 index 0000000000000000000000000000000000000000..ebe9cfcbd30be9c1174311fc9f3d57bd39e86fcc --- /dev/null +++ b/storage/map.go @@ -0,0 +1,17 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2018 Privategrity Corporation / +// / +// All rights reserved. / +//////////////////////////////////////////////////////////////////////////////// + +// Handles Map backend functionality +//+build stateless + +package storage + +// Initialize the Database interface with Database backend +// Returns a Storage interface, Close function, and error +func NewDatabase(username, password, database, address, + port string) (Storage, func() error, error) { + return NewMap(), func() error { return nil }, nil +} diff --git a/storage/node/map.go b/storage/node/map.go index de356cdbd366f0a0ab0a76ff8183d46a6186f90e..31e05b6093eec3d2af3210551b4c30e4ea221345 100644 --- a/storage/node/map.go +++ b/storage/node/map.go @@ -9,7 +9,7 @@ package node import ( "errors" "gitlab.com/elixxir/primitives/current" - "gitlab.com/elixxir/primitives/id" + "gitlab.com/xx_network/primitives/id" "sync" "time" ) diff --git a/storage/node/map_test.go b/storage/node/map_test.go index e806a88411787512a997387981174eade09dfa74..e082ee1151946fe9d4b10b9f09cb3abd7dbeefb7 100644 --- a/storage/node/map_test.go +++ b/storage/node/map_test.go @@ -8,8 +8,8 @@ package node import ( "gitlab.com/elixxir/primitives/current" - "gitlab.com/elixxir/primitives/id" "gitlab.com/elixxir/registration/storage/round" + "gitlab.com/xx_network/primitives/id" "math/rand" "strings" "testing" diff --git a/storage/node/registration.go b/storage/node/registration.go index 328437256de102ad2b947a324dcd757ffa35c6e0..8eafa2954c9ad495c338a5b0999bfd874a61283f 100644 --- a/storage/node/registration.go +++ b/storage/node/registration.go @@ -9,7 +9,7 @@ package node import ( "encoding/json" "github.com/pkg/errors" - "gitlab.com/elixxir/primitives/utils" + "gitlab.com/xx_network/primitives/utils" ) type Info struct { diff --git a/storage/node/registration_test.go b/storage/node/registration_test.go index 03f348cfec9674658046d9073b261052f2400c6d..2bf4ea1d1b8ba84d05dc33bd468e7b8e8a776e64 100644 --- a/storage/node/registration_test.go +++ b/storage/node/registration_test.go @@ -7,7 +7,7 @@ package node import ( - "gitlab.com/elixxir/primitives/utils" + "gitlab.com/xx_network/primitives/utils" "os" "reflect" "testing" diff --git a/storage/node/state.go b/storage/node/state.go index 6e2c4edfe3397e16b95a0218445b04846a08ca44..f08310b5fc8b5270ae0edc5d36403a5d90bd6b00 100644 --- a/storage/node/state.go +++ b/storage/node/state.go @@ -9,10 +9,10 @@ package node import ( "github.com/pkg/errors" "gitlab.com/elixxir/primitives/current" - "gitlab.com/elixxir/primitives/id" "gitlab.com/elixxir/primitives/states" "gitlab.com/elixxir/registration/storage/round" "gitlab.com/elixxir/registration/transition" + "gitlab.com/xx_network/primitives/id" "sync" "sync/atomic" "testing" diff --git a/storage/node/state_test.go b/storage/node/state_test.go index c44fb15bfe986316cfdec99d7896d97fae32027b..330ec662301d9162554d7dddce2342fc98d1063a 100644 --- a/storage/node/state_test.go +++ b/storage/node/state_test.go @@ -8,9 +8,9 @@ package node import ( "gitlab.com/elixxir/primitives/current" - "gitlab.com/elixxir/primitives/id" "gitlab.com/elixxir/primitives/states" "gitlab.com/elixxir/registration/storage/round" + "gitlab.com/xx_network/primitives/id" "math" "reflect" "strings" @@ -24,6 +24,9 @@ func TestState_GetLastUpdate(t *testing.T) { origTime := time.Now() ns := State{} + // Sleep required due to low clock resolution on Windows + time.Sleep(1 * time.Millisecond) + _, _, err := ns.Update(current.WAITING) if err != nil { t.Errorf("Updating state failed: %v", err) @@ -32,7 +35,7 @@ func TestState_GetLastUpdate(t *testing.T) { newTime := ns.GetLastUpdate() if origTime.After(newTime) || origTime.Equal(newTime) { - t.Errorf("origTime was after or euqal to newTime") + t.Errorf("origTime was after or equal to newTime") } } diff --git a/storage/node/updateNotification.go b/storage/node/updateNotification.go index 6a939df7b9b3f8fa1ca205a6bd076621d6657cdc..758151783e859d850e16eb12ef520483479f8cd7 100644 --- a/storage/node/updateNotification.go +++ b/storage/node/updateNotification.go @@ -10,7 +10,7 @@ package node import ( "gitlab.com/elixxir/comms/mixmessages" "gitlab.com/elixxir/primitives/current" - "gitlab.com/elixxir/primitives/id" + "gitlab.com/xx_network/primitives/id" ) // UpdateNotification structure used to notify the control thread that the @@ -22,4 +22,5 @@ type UpdateNotification struct { FromActivity current.Activity ToActivity current.Activity Error *mixmessages.RoundError + ClientErrors []*mixmessages.ClientError } diff --git a/storage/nodeDb.go b/storage/nodeDb.go new file mode 100644 index 0000000000000000000000000000000000000000..63c41915d8d533adc4d41b5bb396ef77adf6c271 --- /dev/null +++ b/storage/nodeDb.go @@ -0,0 +1,81 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2018 Privategrity Corporation / +// / +// All rights reserved. / +//////////////////////////////////////////////////////////////////////////////// + +// Handles the DatabaseImpl for node-related functionality +//+build !stateless + +package storage + +import ( + "gitlab.com/elixxir/registration/storage/node" + "gitlab.com/xx_network/primitives/id" + "time" +) + +// Insert Application object along with associated unregistered Node +func (d *DatabaseImpl) InsertApplication(application *Application, unregisteredNode *Node) error { + application.Node = *unregisteredNode + return d.db.Create(application).Error +} + +// Update the Salt for a given Node ID +func (d *DatabaseImpl) UpdateSalt(id *id.ID, salt []byte) error { + newNode := Node{ + Salt: salt, + } + return d.db.First(&newNode, "id = ?", id.Marshal()).Update("salt", salt).Error +} + +// If Node registration code is valid, add Node information +func (d *DatabaseImpl) RegisterNode(id *id.ID, salt []byte, code, serverAddr, serverCert, + gatewayAddress, gatewayCert string) error { + newNode := Node{ + Code: code, + Id: id.Marshal(), + Salt: salt, + ServerAddress: serverAddr, + GatewayAddress: gatewayAddress, + NodeCertificate: serverCert, + GatewayCertificate: gatewayCert, + Status: uint8(node.Active), + DateRegistered: time.Now(), + } + return d.db.Model(&newNode).Update(&newNode).Error +} + +// Get Node information for the given Node registration code +func (d *DatabaseImpl) GetNode(code string) (*Node, error) { + newNode := &Node{} + err := d.db.First(&newNode, "code = ?", code).Error + return newNode, err +} + +// Get Node information for the given Node ID +func (d *DatabaseImpl) GetNodeById(id *id.ID) (*Node, error) { + newNode := &Node{} + err := d.db.First(&newNode, "id = ?", id.Marshal()).Error + return newNode, err +} + +// Return all nodes in storage with the given Status +func (d *DatabaseImpl) GetNodesByStatus(status node.Status) ([]*Node, error) { + var nodes []*Node + err := d.db.Where("status = ?", uint8(status)).Find(&nodes).Error + return nodes, err +} + +// Update the address fields for the Node with the given id +func (d *DatabaseImpl) UpdateNodeAddresses(id *id.ID, nodeAddr, gwAddr string) error { + newNode := &Node{ + Id: id.Marshal(), + ServerAddress: nodeAddr, + GatewayAddress: gwAddr, + } + return d.db.Model(newNode).Where("id = ?", newNode.Id).Updates(map[string]interface{}{ + "server_address": nodeAddr, + "gateway_address": gwAddr, + }).Error +} diff --git a/storage/nodeMap.go b/storage/nodeMap.go new file mode 100644 index 0000000000000000000000000000000000000000..95bf30130ec9e637b292835735ef677c8619743e --- /dev/null +++ b/storage/nodeMap.go @@ -0,0 +1,156 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2018 Privategrity Corporation / +// / +// All rights reserved. / +//////////////////////////////////////////////////////////////////////////////// + +// Handles the MapImpl for node-related functionality + +package storage + +import ( + "bytes" + "github.com/pkg/errors" + jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/registration/storage/node" + "gitlab.com/xx_network/primitives/id" + "testing" + "time" +) + +// Insert Application object along with associated unregistered Node +func (m *MapImpl) InsertApplication(application *Application, unregisteredNode *Node) error { + m.mut.Lock() + defer m.mut.Unlock() + + jww.INFO.Printf("Adding application: %d", application.Id) + jww.INFO.Printf("Adding node registration code: %s with Order Info: %s", + unregisteredNode.Code, unregisteredNode.Sequence) + + // Enforce unique keys + if m.nodes[unregisteredNode.Code] != nil { + return errors.Errorf("node registration code %s already exists", + unregisteredNode.Code) + } + if m.applications[application.Id] != nil { + return errors.Errorf("application ID %d already exists", + application.Id) + } + + m.nodes[unregisteredNode.Code] = unregisteredNode + m.applications[application.Id] = application + return nil +} + +// Update the Salt for a given Node ID +func (m *MapImpl) UpdateSalt(id *id.ID, salt []byte) error { + n, err := m.GetNodeById(id) + if err != nil { + return err + } + + m.mut.Lock() + defer m.mut.Unlock() + n.Salt = salt + + return nil +} + +// If Node registration code is valid, add Node information +func (m *MapImpl) RegisterNode(id *id.ID, salt []byte, code, serverAddress, serverCert, + gatewayAddress, gatewayCert string) error { + m.mut.Lock() + defer m.mut.Unlock() + + jww.INFO.Printf("Attempting to register node with code: %s", code) + if info := m.nodes[code]; info != nil { + info.Id = id.Marshal() + info.Salt = salt + info.ServerAddress = serverAddress + info.GatewayCertificate = gatewayCert + info.GatewayAddress = gatewayAddress + info.NodeCertificate = serverCert + info.Status = uint8(node.Active) + info.DateRegistered = time.Now() + return nil + } + return errors.Errorf("unable to register node %s", code) + +} + +// Get Node information for the given Node registration code +func (m *MapImpl) GetNode(code string) (*Node, error) { + m.mut.Lock() + defer m.mut.Unlock() + + info := m.nodes[code] + if info == nil { + return nil, errors.Errorf("unable to get node %s", code) + } + return info, nil +} + +// Get Node information for the given Node ID +func (m *MapImpl) GetNodeById(id *id.ID) (*Node, error) { + m.mut.Lock() + defer m.mut.Unlock() + + for _, v := range m.nodes { + if bytes.Compare(v.Id, id.Marshal()) == 0 { + return v, nil + } + } + return nil, errors.Errorf("unable to get node %s", id.String()) +} + +// Return all nodes in storage with the given Status +func (m *MapImpl) GetNodesByStatus(status node.Status) ([]*Node, error) { + m.mut.Lock() + defer m.mut.Unlock() + + nodes := make([]*Node, 0) + for _, v := range m.nodes { + if node.Status(v.Status) == status { + nodes = append(nodes, v) + } + } + return nodes, nil +} + +// If Node registration code is valid, add Node information +func (m *MapImpl) BannedNode(id *id.ID, t interface{}) error { + // Ensure we're called from a test only + switch t.(type) { + case *testing.T: + case *testing.M: + case *testing.B: + default: + jww.FATAL.Panicf("BannedNode permissioning map function called outside testing") + } + + m.mut.Lock() + defer m.mut.Unlock() + for _, n := range m.nodes { + if bytes.Compare(n.Id, id.Bytes()) == 0 { + n.Status = uint8(node.Banned) + return nil + } + } + return errors.New("Node could not be found in map") +} + +// Update the address fields for the Node with the given id +func (m *MapImpl) UpdateNodeAddresses(id *id.ID, nodeAddr, gwAddr string) error { + m.mut.Lock() + defer m.mut.Unlock() + + for _, v := range m.nodes { + if bytes.Compare(v.Id, id.Marshal()) == 0 { + v.GatewayAddress = gwAddr + v.ServerAddress = nodeAddr + return nil + } + } + + return errors.Errorf("unable to update addresses for %s", id.String()) +} diff --git a/storage/nodeMap_test.go b/storage/nodeMap_test.go new file mode 100644 index 0000000000000000000000000000000000000000..cbb420a868385fa5fb88d1d4c877adc96c2df27b --- /dev/null +++ b/storage/nodeMap_test.go @@ -0,0 +1,298 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2018 Privategrity Corporation / +// / +// All rights reserved. / +//////////////////////////////////////////////////////////////////////////////// + +package storage + +import ( + "bytes" + "crypto/rand" + "gitlab.com/elixxir/registration/storage/node" + "gitlab.com/xx_network/primitives/id" + "testing" +) + +// Happy path +func TestMapImpl_InsertApplication(t *testing.T) { + m := &MapImpl{ + nodes: make(map[string]*Node), + applications: make(map[uint64]*Application), + } + + // Attempt to load in a valid code + applicationId := uint64(10) + newNode := &Node{ + Code: "TEST", + Sequence: "BLARG", + ApplicationId: applicationId, + } + newApplication := &Application{Id: applicationId} + err := m.InsertApplication(newApplication, newNode) + + // Verify the insert was successful + if err != nil || m.nodes[newNode.Code] == nil { + t.Errorf("Expected to successfully insert node registration code") + } + + if m.nodes[newNode.Code].Sequence != newNode.Sequence { + t.Errorf("Order string incorret; Expected: %s, Recieved: %s", + newNode.Sequence, m.nodes[newNode.Code].Sequence) + } +} + +// Error Path: Duplicate node registration code and application +func TestMapImpl_InsertApplication_Duplicate(t *testing.T) { + m := &MapImpl{ + nodes: make(map[string]*Node), + applications: make(map[uint64]*Application), + } + + // Load in a registration code + applicationId := uint64(10) + newNode := &Node{ + Code: "TEST", + Sequence: "BLARG", + ApplicationId: applicationId, + } + newApplication := &Application{Id: applicationId} + + // Attempt to load in a duplicate application + m.applications[applicationId] = newApplication + err := m.InsertApplication(newApplication, newNode) + + // Verify the insert failed + if err == nil { + t.Errorf("Expected to fail inserting duplicate application") + } + + // Attempt to load in a duplicate code + m.nodes[newNode.Code] = newNode + err = m.InsertApplication(newApplication, newNode) + + // Verify the insert failed + if err == nil { + t.Errorf("Expected to fail inserting duplicate node registration code") + } +} + +// Happy path +func TestMapImpl_UpdateSalt(t *testing.T) { + testID := id.NewIdFromString("test", id.Node, t) + key := "testKey" + newSalt := make([]byte, 8) + _, _ = rand.Read(newSalt) + + m := &MapImpl{ + nodes: map[string]*Node{key: {Id: testID.Bytes(), Salt: []byte("b")}}, + } + + err := m.UpdateSalt(testID, newSalt) + if err != nil { + t.Errorf("Received unexpected error when upadting salt."+ + "\n\terror: %v", err) + } + + // Verify that the new salt matches the passed in salt + if !bytes.Equal(newSalt, m.nodes[key].Salt) { + t.Errorf("Node in map has unexpected salt."+ + "\n\texpected: %d\n\treceived: %d", newSalt, m.nodes[key].Salt) + } +} + +// Tests that MapImpl.UpdateSalt returns an error if no Node is found in the map +// for the given ID. +func TestMapImpl_UpdateSalt_NodeNotInMap(t *testing.T) { + testID := id.NewIdFromString("test", id.Node, t) + key := "testKey" + newSalt := make([]byte, 8) + _, _ = rand.Read(newSalt) + + m := &MapImpl{ + nodes: map[string]*Node{key: {Id: id.NewIdFromString("test3", id.Node, t).Bytes(), Salt: []byte("b")}}, + } + + err := m.UpdateSalt(testID, newSalt) + if err == nil { + t.Errorf("Did not receive an error when the Node does not exist in " + + "the map.") + } +} + +// Happy path +func TestMapImpl_RegisterNode(t *testing.T) { + m := &MapImpl{ + nodes: make(map[string]*Node), + } + + // Load in a registration code + code := "TEST" + cert := "cert" + gwCert := "gwcert" + addr := "addr" + gwAddr := "gwaddr" + m.nodes[code] = &Node{Code: code} + + // Attempt to insert a node + err := m.RegisterNode(id.NewIdFromString("", id.Node, t), []byte("test"), code, addr, + cert, gwAddr, gwCert) + + // Verify the insert was successful + if info := m.nodes[code]; err != nil || info.NodeCertificate != cert || + info.GatewayCertificate != gwCert || info.ServerAddress != addr || + info.GatewayAddress != gwAddr { + t.Errorf("Expected to successfully insert node information: %+v", info) + } +} + +// Error path: Invalid registration code +func TestMapImpl_RegisterNode_Invalid(t *testing.T) { + m := &MapImpl{ + nodes: make(map[string]*Node), + } + + // Do NOT load in a registration code + code := "TEST" + + // Attempt to insert a node without an associated registration code + err := m.RegisterNode(id.NewIdFromString("", id.Node, t), []byte("test"), code, code, + code, code, code) + + // Verify the insert failed + if err == nil { + t.Errorf("Expected to fail inserting node information without the" + + " correct registration code") + } +} + +// Happy path +func TestMapImpl_GetNode(t *testing.T) { + m := &MapImpl{ + nodes: make(map[string]*Node), + } + + // Load in a registration code + code := "TEST" + m.nodes[code] = &Node{Code: code} + + // Check that the correct node is obtained + info, err := m.GetNode(code) + if err != nil || info.Code != code { + t.Errorf("Expected to be able to obtain correct node") + } +} + +// Error path: Nonexistent registration code +func TestMapImpl_GetNode_Invalid(t *testing.T) { + m := &MapImpl{ + nodes: make(map[string]*Node), + } + + // Check that no node is obtained from empty map + info, err := m.GetNode("TEST") + if err == nil || info != nil { + t.Errorf("Expected to not find the node") + } +} + +// Happy path +func TestMapImpl_GetNodeById(t *testing.T) { + m := &MapImpl{ + nodes: make(map[string]*Node), + } + + // Load in a registration code + code := "TEST" + testId := id.NewIdFromString(code, id.Node, t) + m.nodes[code] = &Node{Code: code, Id: testId.Marshal()} + + // Check that the correct node is obtained + info, err := m.GetNodeById(testId) + if err != nil || info.Code != code { + t.Errorf("Expected to be able to obtain correct node") + } +} + +// Error path: Nonexistent node id +func TestMapImpl_GetNodeById_Invalid(t *testing.T) { + m := &MapImpl{ + nodes: make(map[string]*Node), + } + + testId := id.NewIdFromString("test", id.Node, t) + + // Check that no node is obtained from empty map + info, err := m.GetNodeById(testId) + if err == nil || info != nil { + t.Errorf("Expected to not find the node") + } +} + +// Happy path +func TestMapImpl_GetNodesByStatus(t *testing.T) { + m := &MapImpl{ + nodes: make(map[string]*Node), + } + + // Should start off empty + nodes, err := m.GetNodesByStatus(node.Banned) + if err != nil { + t.Errorf("Unable to get nodes by status: %+v", err) + } + if len(nodes) > 0 { + t.Errorf("Unexpected nodes returned for status: %v", nodes) + } + + // Add a banned node + code := "TEST" + m.nodes[code] = &Node{Code: code, Status: uint8(node.Banned)} + + // Should have a result now + nodes, err = m.GetNodesByStatus(node.Banned) + if err != nil { + t.Errorf("Unable to get nodes by status: %+v", err) + } + if len(nodes) != 1 { + t.Errorf("Unexpected nodes returned for status: %v", nodes) + } + + // Unban the node + m.nodes[code].Status = uint8(node.Active) + + // Shouldn't get a result anymore + nodes, err = m.GetNodesByStatus(node.Banned) + if err != nil { + t.Errorf("Unable to get nodes by status: %+v", err) + } + if len(nodes) > 0 { + t.Errorf("Unexpected nodes returned for status: %v", nodes) + } +} + +// Happy path +func TestMapImpl_UpdateNodeAddresses(t *testing.T) { + m := &MapImpl{ + nodes: make(map[string]*Node), + } + + testString := "test" + testId := id.NewIdFromString(testString, id.Node, t) + testResult := "newAddr" + m.nodes[testString] = &Node{ + Code: testString, + Id: testId.Marshal(), + ServerAddress: testString, + GatewayAddress: testString, + } + + err := m.UpdateNodeAddresses(testId, testResult, testResult) + if err != nil { + t.Errorf(err.Error()) + } + + if result := m.nodes[testString]; result.ServerAddress != testResult || result.GatewayAddress != testResult { + t.Errorf("Field values did not update correctly, got Node %s Gateway %s", + result.ServerAddress, result.GatewayAddress) + } +} diff --git a/storage/permissioningDb.go b/storage/permissioningDb.go index fb804c0a2c25164adcaca219efe90b0866bc7319..5f7a06fa88a7a6f182f7c1151c9b0a38576ff21e 100644 --- a/storage/permissioningDb.go +++ b/storage/permissioningDb.go @@ -4,42 +4,72 @@ // All rights reserved. / //////////////////////////////////////////////////////////////////////////////// -// Handles the database ORM for nodes +// Handles the DatabaseImpl for permissioning-based functionality +//+build !stateless package storage import ( + "github.com/jinzhu/gorm" "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" - "gitlab.com/elixxir/primitives/id" - "gitlab.com/elixxir/registration/storage/node" - "time" + "gitlab.com/xx_network/primitives/id" ) -// Insert Application object along with associated unregistered Node -func (m *DatabaseImpl) InsertApplication(application *Application, unregisteredNode *Node) error { - application.Node = *unregisteredNode - return m.db.Create(application).Error +// Inserts the given State into Storage if it does not exist +// Or updates the Database State if its value does not match the given State +func (d *DatabaseImpl) UpsertState(state *State) error { + jww.TRACE.Printf("Attempting to insert State into DB: %+v", state) + + // Build a transaction to prevent race conditions + return d.db.Transaction(func(tx *gorm.DB) error { + // Make a copy of the provided state + newState := *state + + // Attempt to insert state into the Database, + // or if it already exists, replace state with the Database value + err := tx.FirstOrCreate(state, &State{Key: state.Key}).Error + if err != nil { + return err + } + + // If state is already present in the Database, overwrite it with newState + if newState.Value != state.Value { + return tx.Save(newState).Error + } + + // Commit + return nil + }) +} + +// Returns a State's value from Storage with the given key +// Or an error if a matching State does not exist +func (d *DatabaseImpl) GetStateValue(key string) (string, error) { + result := &State{Key: key} + err := d.db.Take(result).Error + jww.TRACE.Printf("Obtained State from DB: %+v", result) + return result.Value, err } -// Insert NodeMetric object -func (m *DatabaseImpl) InsertNodeMetric(metric *NodeMetric) error { - jww.TRACE.Printf("Attempting to insert node metric: %+v", metric) - return m.db.Create(metric).Error +// Insert new NodeMetric object into Storage +func (d *DatabaseImpl) InsertNodeMetric(metric *NodeMetric) error { + jww.TRACE.Printf("Attempting to insert NodeMetric into DB: %+v", metric) + return d.db.Create(metric).Error } -// Insert RoundError object -func (m *DatabaseImpl) InsertRoundError(roundId id.Round, errStr string) error { +// Insert new RoundError object into Storage +func (d *DatabaseImpl) InsertRoundError(roundId id.Round, errStr string) error { roundErr := &RoundError{ RoundMetricId: uint64(roundId), Error: errStr, } - jww.DEBUG.Printf("Attempting to insert round error: %+v", roundErr) - return m.db.Create(roundErr).Error + jww.TRACE.Printf("Attempting to insert RoundError into DB: %+v", roundErr) + return d.db.Create(roundErr).Error } -// Insert RoundMetric object with associated topology -func (m *DatabaseImpl) InsertRoundMetric(metric *RoundMetric, topology [][]byte) error { +// Insert new RoundMetric object with associated topology into Storage +func (d *DatabaseImpl) InsertRoundMetric(metric *RoundMetric, topology [][]byte) error { // Build the Topology metric.Topologies = make([]Topology, len(topology)) @@ -56,65 +86,28 @@ func (m *DatabaseImpl) InsertRoundMetric(metric *RoundMetric, topology [][]byte) } // Save the RoundMetric - jww.DEBUG.Printf("Attempting to insert round metric: %+v", metric) - return m.db.Create(metric).Error -} - -// Update the Salt for a given Node ID -func (m *DatabaseImpl) UpdateSalt(id *id.ID, salt []byte) error { - newNode := Node{ - Salt: salt, - } - return m.db.First(&newNode, "id = ?", id.Marshal()).Update("salt", salt).Error -} - -// If Node registration code is valid, add Node information -func (m *DatabaseImpl) RegisterNode(id *id.ID, salt []byte, code, serverAddr, serverCert, - gatewayAddress, gatewayCert string) error { - newNode := &Node{ - Code: code, - Id: id.Marshal(), - Salt: salt, - ServerAddress: serverAddr, - GatewayAddress: gatewayAddress, - NodeCertificate: serverCert, - GatewayCertificate: gatewayCert, - Status: uint8(node.Active), - DateRegistered: time.Now(), - } - return m.db.Model(&newNode).Update(&newNode).Error -} - -// Update the address fields for the Node with the given id -func (m *DatabaseImpl) UpdateNodeAddresses(id *id.ID, nodeAddr, gwAddr string) error { - newNode := &Node{ - Id: id.Marshal(), - ServerAddress: nodeAddr, - GatewayAddress: gwAddr, - } - return m.db.Model(newNode).Where("id = ?", newNode.Id).Updates(map[string]interface{}{ - "server_address": nodeAddr, - "gateway_address": gwAddr, - }).Error + jww.TRACE.Printf("Attempting to insert RoundMetric into DB: %+v", metric) + return d.db.Create(metric).Error } -// Get Node information for the given Node registration code -func (m *DatabaseImpl) GetNode(code string) (*Node, error) { - newNode := &Node{} - err := m.db.First(&newNode, "code = ?", code).Error - return newNode, err +// Returns newest (and largest, by implication) EphemeralLength from Storage +func (d *DatabaseImpl) GetLatestEphemeralLength() (*EphemeralLength, error) { + result := &EphemeralLength{} + err := d.db.Last(result).Error + jww.TRACE.Printf("Obtained latest EphemeralLength from DB: %+v", result) + return result, err } -// Get Node information for the given Node ID -func (m *DatabaseImpl) GetNodeById(id *id.ID) (*Node, error) { - newNode := &Node{} - err := m.db.First(&newNode, "id = ?", id.Marshal()).Error - return newNode, err +// Returns all EphemeralLength from Storage +func (d *DatabaseImpl) GetEphemeralLengths() ([]*EphemeralLength, error) { + var result []*EphemeralLength + err := d.db.Find(&result).Error + jww.TRACE.Printf("Obtained EphemeralLengths from DB: %+v", result) + return result, err } -// Return all nodes in storage with the given Status -func (m *DatabaseImpl) GetNodesByStatus(status node.Status) ([]*Node, error) { - var nodes []*Node - err := m.db.Where("status = ?", uint8(status)).Find(&nodes).Error - return nodes, err +// Insert new EphemeralLength into Storage +func (d *DatabaseImpl) InsertEphemeralLength(length *EphemeralLength) error { + jww.TRACE.Printf("Attempting to insert EphemeralLength into DB: %+v", length) + return d.db.Create(length).Error } diff --git a/storage/permissioningMap.go b/storage/permissioningMap.go index a48e3025b9aba70b2f96b6efdb76ac55d71dd16e..c2e3c0eae69c9c1c2cd13732386037c5b84df11f 100644 --- a/storage/permissioningMap.go +++ b/storage/permissioningMap.go @@ -4,45 +4,47 @@ // All rights reserved. / //////////////////////////////////////////////////////////////////////////////// -// Handles the Map backend for the permissioning server +// Handles the MapImpl for permissioning-based functionality package storage import ( - "bytes" "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" - "gitlab.com/elixxir/primitives/id" - "gitlab.com/elixxir/registration/storage/node" - "testing" + "gitlab.com/xx_network/primitives/id" ) -// Insert Application object along with associated unregistered Node -func (m *MapImpl) InsertApplication(application *Application, unregisteredNode *Node) error { +// Inserts the given State into Storage if it does not exist +// Or updates the Database State if its value does not match the given State +func (m *MapImpl) UpsertState(state *State) error { + jww.TRACE.Printf("Attempting to insert State into Map: %+v", state) + m.mut.Lock() defer m.mut.Unlock() - jww.INFO.Printf("Adding application: %d", application.Id) - jww.INFO.Printf("Adding node registration code: %s with Order Info: %s", - unregisteredNode.Code, unregisteredNode.Sequence) + m.states[state.Key] = state.Value + return nil +} - // Enforce unique keys - if m.nodes[unregisteredNode.Code] != nil { - return errors.Errorf("node registration code %s already exists", - unregisteredNode.Code) - } - if m.applications[application.Id] != nil { - return errors.Errorf("application ID %d already exists", - application.Id) +// Returns a State's value from Storage with the given key +// Or an error if a matching State does not exist +func (m *MapImpl) GetStateValue(key string) (string, error) { + m.mut.Lock() + defer m.mut.Unlock() + + if val, ok := m.states[key]; ok { + jww.TRACE.Printf("Obtained State from Map: %+v", val) + return val, nil } - m.nodes[unregisteredNode.Code] = unregisteredNode - m.applications[application.Id] = application - return nil + // NOTE: Other code depends on this error string + return "", errors.Errorf("Unable to locate state for key %s", key) } -// Insert NodeMetric object +// Insert new NodeMetric object into Storage func (m *MapImpl) InsertNodeMetric(metric *NodeMetric) error { + jww.TRACE.Printf("Attempting to insert NodeMetric into Map: %+v", metric) + m.mut.Lock() defer m.mut.Unlock() @@ -51,32 +53,28 @@ func (m *MapImpl) InsertNodeMetric(metric *NodeMetric) error { // Add to map metric.Id = m.nodeMetricCounter - jww.DEBUG.Printf("Attempting to insert node metric: %+v", metric) m.nodeMetrics[m.nodeMetricCounter] = metric return nil } -// Insert RoundError object +// Insert new RoundError object into Storage func (m *MapImpl) InsertRoundError(roundId id.Round, errStr string) error { - m.mut.Lock() - defer m.mut.Unlock() rid := uint64(roundId) + roundErr := RoundError{ + Id: 0, // Currently useless in MapImpl + RoundMetricId: rid, + Error: errStr, + } - m.roundMetrics[rid].RoundErrors = append( - m.roundMetrics[rid].RoundErrors, - RoundError{ - Id: 0, // Currently useless in MapImpl - RoundMetricId: rid, - Error: errStr, - }, - ) + jww.TRACE.Printf("Attempting to insert RoundError into Map: %+v", roundErr) + m.mut.Lock() + m.roundMetrics[rid].RoundErrors = append(m.roundMetrics[rid].RoundErrors, roundErr) + m.mut.Unlock() return nil } -// Insert RoundMetric object with associated topology +// Insert new RoundMetric object with associated topology into Storage func (m *MapImpl) InsertRoundMetric(metric *RoundMetric, topology [][]byte) error { - m.mut.Lock() - defer m.mut.Unlock() // Build Topology objects metric.Topologies = make([]Topology, len(topology)) @@ -94,119 +92,63 @@ func (m *MapImpl) InsertRoundMetric(metric *RoundMetric, topology [][]byte) erro } // Add to map - jww.DEBUG.Printf("Attempting to insert round metric: %+v", metric) - m.roundMetrics[metric.Id] = metric - return nil -} - -// Update the Salt for a given Node ID -func (m *MapImpl) UpdateSalt(id *id.ID, salt []byte) error { - n, err := m.GetNodeById(id) - if err != nil { - return err - } - + jww.TRACE.Printf("Attempting to insert RoundMetric into Map: %+v", metric) m.mut.Lock() - defer m.mut.Unlock() - n.Salt = salt - + m.roundMetrics[metric.Id] = metric + m.mut.Unlock() return nil } -// If Node registration code is valid, add Node information -func (m *MapImpl) RegisterNode(id *id.ID, salt []byte, code, serverAddress, serverCert, - gatewayAddress, gatewayCert string) error { +// Returns newest (and largest, by implication) EphemeralLength from Storage +func (m *MapImpl) GetLatestEphemeralLength() (*EphemeralLength, error) { m.mut.Lock() defer m.mut.Unlock() - jww.INFO.Printf("Attempting to register node with code: %s", code) - if info := m.nodes[code]; info != nil { - info.Id = id.Marshal() - info.Salt = salt - info.ServerAddress = serverAddress - info.GatewayCertificate = gatewayCert - info.GatewayAddress = gatewayAddress - info.NodeCertificate = serverCert - info.Status = uint8(node.Active) - return nil + if len(m.ephemeralLengths) == 0 { + return nil, errors.Errorf("Unable to locate any EphemeralLengths") } - return errors.Errorf("unable to register node %s", code) - -} - -// Update the address fields for the Node with the given id -func (m *MapImpl) UpdateNodeAddresses(id *id.ID, nodeAddr, gwAddr string) error { - m.mut.Lock() - defer m.mut.Unlock() - for _, v := range m.nodes { - if bytes.Compare(v.Id, id.Marshal()) == 0 { - v.GatewayAddress = gwAddr - v.ServerAddress = nodeAddr - return nil + largest := uint8(0) + for k := range m.ephemeralLengths { + if k > largest { + largest = k } } - - return errors.Errorf("unable to update addresses for %s", id.String()) + result := m.ephemeralLengths[largest] + jww.TRACE.Printf("Obtained latest EphemeralLength from Map: %+v", result) + return result, nil } -// Get Node information for the given Node registration code -func (m *MapImpl) GetNode(code string) (*Node, error) { +// Returns all EphemeralLength from Storage +func (m *MapImpl) GetEphemeralLengths() ([]*EphemeralLength, error) { m.mut.Lock() defer m.mut.Unlock() - info := m.nodes[code] - if info == nil { - return nil, errors.Errorf("unable to get node %s", code) + if len(m.ephemeralLengths) == 0 { + return nil, errors.Errorf("Unable to locate any EphemeralLengths") } - return info, nil -} - -// Get Node information for the given Node ID -func (m *MapImpl) GetNodeById(id *id.ID) (*Node, error) { - m.mut.Lock() - defer m.mut.Unlock() - for _, v := range m.nodes { - if bytes.Compare(v.Id, id.Marshal()) == 0 { - return v, nil - } + result := make([]*EphemeralLength, len(m.ephemeralLengths)) + i := 0 + for _, v := range m.ephemeralLengths { + result[i] = v + i++ } - return nil, errors.Errorf("unable to get node %s", id.String()) + jww.TRACE.Printf("Obtained EphemeralLengths from Map: %+v", result) + return result, nil } -// Return all nodes in storage with the given Status -func (m *MapImpl) GetNodesByStatus(status node.Status) ([]*Node, error) { +// Insert new EphemeralLength into Storage +func (m *MapImpl) InsertEphemeralLength(length *EphemeralLength) error { + jww.TRACE.Printf("Attempting to insert EphemeralLength into Map: %+v", length) + m.mut.Lock() defer m.mut.Unlock() - nodes := make([]*Node, 0) - for _, v := range m.nodes { - if node.Status(v.Status) == status { - nodes = append(nodes, v) - } - } - return nodes, nil -} - -// If Node registration code is valid, add Node information -func (m *MapImpl) BannedNode(id *id.ID, t interface{}) error { - // Ensure we're called from a test only - switch t.(type) { - case *testing.T: - case *testing.M: - case *testing.B: - default: - jww.FATAL.Panicf("BannedNode permissioning map function called outside testing") + if m.ephemeralLengths[length.Length] != nil { + return errors.Errorf("ephemeral length %d already exists", length.Length) } - m.mut.Lock() - defer m.mut.Unlock() - for _, n := range m.nodes { - if bytes.Compare(n.Id, id.Bytes()) == 0 { - n.Status = uint8(node.Banned) - return nil - } - } - return errors.New("Node could not be found in map") + m.ephemeralLengths[length.Length] = length + return nil } diff --git a/storage/permissioningMap_test.go b/storage/permissioningMap_test.go index dcdd485b28fb7040867ee44924211815f13b4765..5345977b80a1507168777dfa280638e7797f9488 100644 --- a/storage/permissioningMap_test.go +++ b/storage/permissioningMap_test.go @@ -7,22 +7,62 @@ package storage import ( - "bytes" - "crypto/rand" - "gitlab.com/elixxir/primitives/id" - "gitlab.com/elixxir/registration/storage/node" + "gitlab.com/xx_network/primitives/id" + "strings" "testing" "time" ) -// Hidden function for one-time unit testing database implementation +// Hidden function for one-time unit testing Database implementation //func TestDatabaseImpl(t *testing.T) { +// jww.SetLogThreshold(jww.LevelTrace) +// jww.SetStdoutThreshold(jww.LevelTrace) +// // db, _, err := NewDatabase("cmix", "", "cmix_server", "0.0.0.0", "5432") // if err != nil { // t.Errorf(err.Error()) // return // } // +// result, err := db.GetLatestEphemeralLength() +// if err != nil { +// t.Errorf(err.Error()) +// } +// jww.INFO.Printf("%+v", result) +// result2, err := db.GetEphemeralLengths() +// if err != nil { +// t.Errorf(err.Error()) +// } +// jww.INFO.Printf("%#v", result2) +// +// err = db.UpsertState(&State{ +// Key: RoundIdKey, +// Value: "10", +// }) +// if err != nil { +// t.Errorf(err.Error()) +// } +// +// val, err := db.GetStateValue(RoundIdKey) +// if err != nil { +// t.Errorf(err.Error()) +// } +// jww.FATAL.Printf(val) +// +// err = db.UpsertState(&State{ +// Key: RoundIdKey, +// Value: "20", +// }) +// if err != nil { +// t.Errorf(err.Error()) +// } +// +// val, err = db.GetStateValue(RoundIdKey) +// if err != nil { +// t.Errorf(err.Error()) +// } +// jww.FATAL.Printf(val) +// // testCode := "test" // testId := id.NewIdFromString(testCode, id.Node, t) // testAppId := uint64(10010) @@ -177,284 +217,120 @@ func TestMapImpl_InsertRoundError(t *testing.T) { } // Happy path -func TestMapImpl_InsertApplication(t *testing.T) { - m := &MapImpl{ - nodes: make(map[string]*Node), - applications: make(map[uint64]*Application), - } +func TestMapImpl_InsertEphemeralLength(t *testing.T) { + m := &MapImpl{ephemeralLengths: make(map[uint8]*EphemeralLength)} - // Attempt to load in a valid code - applicationId := uint64(10) - newNode := &Node{ - Code: "TEST", - Sequence: "BLARG", - ApplicationId: applicationId, + el := &EphemeralLength{ + Length: 10, + Timestamp: time.Now(), } - newApplication := &Application{Id: applicationId} - err := m.InsertApplication(newApplication, newNode) - - // Verify the insert was successful - if err != nil || m.nodes[newNode.Code] == nil { - t.Errorf("Expected to successfully insert node registration code") + err := m.InsertEphemeralLength(el) + if err != nil { + t.Errorf("Unable to insert EphLen: %+v", err) } - if m.nodes[newNode.Code].Sequence != newNode.Sequence { - t.Errorf("Order string incorret; Expected: %s, Recieved: %s", - newNode.Sequence, m.nodes[newNode.Code].Sequence) + if m.ephemeralLengths[el.Length] == nil { + t.Errorf("Expected to find inserted EphLen: %d", el.Length) } } -// Error Path: Duplicate node registration code and application -func TestMapImpl_InsertApplication_Duplicate(t *testing.T) { - m := &MapImpl{ - nodes: make(map[string]*Node), - applications: make(map[uint64]*Application), - } +// Error path +func TestMapImpl_InsertEphemeralLengthErr(t *testing.T) { + m := &MapImpl{ephemeralLengths: make(map[uint8]*EphemeralLength)} - // Load in a registration code - applicationId := uint64(10) - newNode := &Node{ - Code: "TEST", - Sequence: "BLARG", - ApplicationId: applicationId, + el := &EphemeralLength{ + Length: 10, + Timestamp: time.Now(), } - newApplication := &Application{Id: applicationId} - - // Attempt to load in a duplicate application - m.applications[applicationId] = newApplication - err := m.InsertApplication(newApplication, newNode) - - // Verify the insert failed - if err == nil { - t.Errorf("Expected to fail inserting duplicate application") - } - - // Attempt to load in a duplicate code - m.nodes[newNode.Code] = newNode - err = m.InsertApplication(newApplication, newNode) + // Manually add duplicate entry + m.ephemeralLengths[el.Length] = el - // Verify the insert failed + err := m.InsertEphemeralLength(el) if err == nil { - t.Errorf("Expected to fail inserting duplicate node registration code") + t.Errorf("Expected failure from duplicate EphLen!") } } // Happy path -func TestMapImpl_UpdateSalt(t *testing.T) { - testID := id.NewIdFromString("test", id.Node, t) - key := "testKey" - newSalt := make([]byte, 8) - _, _ = rand.Read(newSalt) +func TestMapImpl_GetEphemeralLengths(t *testing.T) { + m := &MapImpl{ephemeralLengths: make(map[uint8]*EphemeralLength)} + testLen := 64 - m := &MapImpl{ - nodes: map[string]*Node{key: {Id: testID.Bytes(), Salt: []byte("b")}}, + // Make a bunch of results to insert + for i := 0; i < testLen; i++ { + el := &EphemeralLength{ + Length: uint8(i), + Timestamp: time.Now(), + } + m.ephemeralLengths[el.Length] = el } - err := m.UpdateSalt(testID, newSalt) + result, err := m.GetEphemeralLengths() if err != nil { - t.Errorf("Received unexpected error when upadting salt."+ - "\n\terror: %v", err) + t.Errorf("Unable to get all EphLen: %+v", err) } - // Verify that the new salt matches the passed in salt - if !bytes.Equal(newSalt, m.nodes[key].Salt) { - t.Errorf("Node in map has unexpected salt."+ - "\n\texpected: %d\n\treceived: %d", newSalt, m.nodes[key].Salt) + if len(result) != testLen { + t.Errorf("Didn't get correct number of EphLen, Got %d Expected %d", len(result), testLen) } } -// Tests that MapImpl.UpdateSalt returns an error if no Node is found in the map -// for the given ID. -func TestMapImpl_UpdateSalt_NodeNotInMap(t *testing.T) { - testID := id.NewIdFromString("test", id.Node, t) - key := "testKey" - newSalt := make([]byte, 8) - _, _ = rand.Read(newSalt) - - m := &MapImpl{ - nodes: map[string]*Node{key: {Id: id.NewIdFromString("test3", id.Node, t).Bytes(), Salt: []byte("b")}}, - } - - err := m.UpdateSalt(testID, newSalt) - if err == nil { - t.Errorf("Did not receive an error when the Node does not exist in " + - "the map.") +// Error path +func TestMapImpl_GetEphemeralLengthsErr(t *testing.T) { + m := &MapImpl{ephemeralLengths: make(map[uint8]*EphemeralLength)} + result, err := m.GetEphemeralLengths() + if result != nil || err == nil { + t.Errorf("Expected error getting bad EphLens!") } } // Happy path -func TestMapImpl_RegisterNode(t *testing.T) { - m := &MapImpl{ - nodes: make(map[string]*Node), - } - - // Load in a registration code - code := "TEST" - cert := "cert" - gwCert := "gwcert" - addr := "addr" - gwAddr := "gwaddr" - m.nodes[code] = &Node{Code: code} - - // Attempt to insert a node - err := m.RegisterNode(id.NewIdFromString("", id.Node, t), []byte("test"), code, addr, - cert, gwAddr, gwCert) - - // Verify the insert was successful - if info := m.nodes[code]; err != nil || info.NodeCertificate != cert || - info.GatewayCertificate != gwCert || info.ServerAddress != addr || - info.GatewayAddress != gwAddr { - t.Errorf("Expected to successfully insert node information: %+v", info) - } -} - -// Error path: Invalid registration code -func TestMapImpl_RegisterNode_Invalid(t *testing.T) { - m := &MapImpl{ - nodes: make(map[string]*Node), - } - - // Do NOT load in a registration code - code := "TEST" +func TestMapImpl_GetLatestEphemeralLength(t *testing.T) { + m := &MapImpl{ephemeralLengths: make(map[uint8]*EphemeralLength)} - // Attempt to insert a node without an associated registration code - err := m.RegisterNode(id.NewIdFromString("", id.Node, t), []byte("test"), code, code, - code, code, code) + // Make a bunch of results to insert + maxLen := 50 + for i := 0; i <= maxLen; i += 5 { - // Verify the insert failed - if err == nil { - t.Errorf("Expected to fail inserting node information without the" + - " correct registration code") + el := &EphemeralLength{ + Length: uint8(i), + // Unlike the real world, decrease Timestamp as Length increases + // in order to ensure latest EphemeralLength is based on Length + Timestamp: time.Now().Add(time.Duration(-i) * time.Minute), + } + m.ephemeralLengths[el.Length] = el } -} -// Happy path -func TestMapImpl_UpdateNodeAddresses(t *testing.T) { - m := &MapImpl{ - nodes: make(map[string]*Node), - } - - testString := "test" - testId := id.NewIdFromString(testString, id.Node, t) - testResult := "newAddr" - m.nodes[testString] = &Node{ - Code: testString, - Id: testId.Marshal(), - ServerAddress: testString, - GatewayAddress: testString, - } - - err := m.UpdateNodeAddresses(testId, testResult, testResult) + result, err := m.GetLatestEphemeralLength() if err != nil { - t.Errorf(err.Error()) - } - - if result := m.nodes[testString]; result.ServerAddress != testResult || result.GatewayAddress != testResult { - t.Errorf("Field values did not update correctly, got Node %s Gateway %s", - result.ServerAddress, result.GatewayAddress) - } -} - -// Happy path -func TestMapImpl_GetNode(t *testing.T) { - m := &MapImpl{ - nodes: make(map[string]*Node), - } - - // Load in a registration code - code := "TEST" - m.nodes[code] = &Node{Code: code} - - // Check that the correct node is obtained - info, err := m.GetNode(code) - if err != nil || info.Code != code { - t.Errorf("Expected to be able to obtain correct node") - } -} - -// Error path: Nonexistent registration code -func TestMapImpl_GetNode_Invalid(t *testing.T) { - m := &MapImpl{ - nodes: make(map[string]*Node), - } - - // Check that no node is obtained from empty map - info, err := m.GetNode("TEST") - if err == nil || info != nil { - t.Errorf("Expected to not find the node") - } -} - -// Happy path -func TestMapImpl_GetNodeById(t *testing.T) { - m := &MapImpl{ - nodes: make(map[string]*Node), + t.Errorf("Unable to get latest EphLen: %+v", err) } - // Load in a registration code - code := "TEST" - testId := id.NewIdFromString(code, id.Node, t) - m.nodes[code] = &Node{Code: code, Id: testId.Marshal()} - - // Check that the correct node is obtained - info, err := m.GetNodeById(testId) - if err != nil || info.Code != code { - t.Errorf("Expected to be able to obtain correct node") + if result.Length != uint8(maxLen) { + t.Errorf("Latest EphLen incorrect: Got %d, expected %d", result.Length, maxLen) } } -// Error path: Nonexistent node id -func TestMapImpl_GetNodeById_Invalid(t *testing.T) { - m := &MapImpl{ - nodes: make(map[string]*Node), - } - - testId := id.NewIdFromString("test", id.Node, t) - - // Check that no node is obtained from empty map - info, err := m.GetNodeById(testId) - if err == nil || info != nil { - t.Errorf("Expected to not find the node") +// Error path +func TestMapImpl_GetLatestEphemeralLengthErr(t *testing.T) { + m := &MapImpl{ephemeralLengths: make(map[uint8]*EphemeralLength)} + result, err := m.GetLatestEphemeralLength() + if result != nil || err == nil { + t.Errorf("Expected error getting bad latest EphLen!") } } -// Happy path -func TestMapImpl_GetNodesByStatus(t *testing.T) { - m := &MapImpl{ - nodes: make(map[string]*Node), - } - - // Should start off empty - nodes, err := m.GetNodesByStatus(node.Banned) - if err != nil { - t.Errorf("Unable to get nodes by status: %+v", err) - } - if len(nodes) > 0 { - t.Errorf("Unexpected nodes returned for status: %v", nodes) - } - - // Add a banned node - code := "TEST" - m.nodes[code] = &Node{Code: code, Status: uint8(node.Banned)} +// Test error path to ensure error message stays consistent +func TestMapImpl_GetStateValue(t *testing.T) { + m := &MapImpl{states: make(map[string]string)} - // Should have a result now - nodes, err = m.GetNodesByStatus(node.Banned) - if err != nil { - t.Errorf("Unable to get nodes by status: %+v", err) - } - if len(nodes) != 1 { - t.Errorf("Unexpected nodes returned for status: %v", nodes) + _, err := m.GetStateValue("test") + if err == nil { + t.Errorf("Expected error getting bad state value!") + return } - // Unban the node - m.nodes[code].Status = uint8(node.Active) - - // Shouldn't get a result anymore - nodes, err = m.GetNodesByStatus(node.Banned) - if err != nil { - t.Errorf("Unable to get nodes by status: %+v", err) - } - if len(nodes) > 0 { - t.Errorf("Unexpected nodes returned for status: %v", nodes) + if !strings.Contains(err.Error(), "Unable to locate state for key") { + t.Errorf("Invalid error message getting bad state value: Got %s", err.Error()) } } diff --git a/storage/round/map.go b/storage/round/map.go index 010f2ceae1671e61e9bf980ce4afaeda72595a7a..bb8da291b289c571f04b98d981ffca2f40a1a46f 100644 --- a/storage/round/map.go +++ b/storage/round/map.go @@ -9,8 +9,8 @@ package round import ( "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" - "gitlab.com/elixxir/primitives/id" "gitlab.com/xx_network/comms/connect" + "gitlab.com/xx_network/primitives/id" "sync" "testing" "time" @@ -31,7 +31,7 @@ func NewStateMap() *StateMap { } // Adds a new round state to the structure. Will not overwrite an existing one. -func (rsm *StateMap) AddRound(id id.Round, batchsize uint32, resourceQueueTimeout time.Duration, +func (rsm *StateMap) AddRound(id id.Round, batchsize, addressSpaceSize uint32, resourceQueueTimeout time.Duration, topology *connect.Circuit) (*State, error) { rsm.mux.Lock() defer rsm.mux.Unlock() @@ -40,7 +40,7 @@ func (rsm *StateMap) AddRound(id id.Round, batchsize uint32, resourceQueueTimeou return nil, errors.New("cannot add a round which already exists") } - rsm.rounds[id] = newState(id, batchsize, resourceQueueTimeout, topology, time.Now()) + rsm.rounds[id] = newState(id, batchsize, addressSpaceSize, resourceQueueTimeout, topology, time.Now()) return rsm.rounds[id], nil } diff --git a/storage/round/map_test.go b/storage/round/map_test.go index b64d5175f643c207f03d7986f98c9a3f98a02ba9..d1a549e366906e6c4badbf3a9355607fc0e6c2a7 100644 --- a/storage/round/map_test.go +++ b/storage/round/map_test.go @@ -7,9 +7,9 @@ package round import ( - "gitlab.com/elixxir/primitives/id" "gitlab.com/elixxir/primitives/states" "gitlab.com/xx_network/comms/connect" + "gitlab.com/xx_network/primitives/id" "testing" "time" ) @@ -34,7 +34,7 @@ func TestStateMap_AddRound_Happy(t *testing.T) { const numNodes = 5 - rRtn, err := sm.AddRound(rid, 32, 5*time.Minute, buildMockTopology(numNodes, t)) + rRtn, err := sm.AddRound(rid, 32, 8, 5*time.Minute, buildMockTopology(numNodes, t)) if err != nil { t.Errorf("Error returned on valid addition of node: %s", err) @@ -69,7 +69,7 @@ func TestStateMap_AddNode_Invalid(t *testing.T) { sm.rounds[rid] = &State{state: states.FAILED} - rRtn, err := sm.AddRound(rid, 32, 5*time.Minute, buildMockTopology(numNodes, t)) + rRtn, err := sm.AddRound(rid, 32, 8, 5*time.Minute, buildMockTopology(numNodes, t)) if err == nil { t.Errorf("Error not returned on invalid addition of node: %s", err) diff --git a/storage/round/roundInfoCopy.go b/storage/round/roundInfoCopy.go index ad75e20fe1edd73e674bff8faff937ecdb4a04b0..6feaeedabd7907537778d62fa849c7e381ae0d5c 100644 --- a/storage/round/roundInfoCopy.go +++ b/storage/round/roundInfoCopy.go @@ -48,5 +48,6 @@ func CopyRoundInfo(ri *pb.RoundInfo) *pb.RoundInfo { Timestamps: timestampsCopy, ResourceQueueTimeoutMillis: ri.GetResourceQueueTimeoutMillis(), Errors: errorsCopy, + AddressSpaceSize: ri.GetAddressSpaceSize(), } } diff --git a/storage/round/state.go b/storage/round/state.go index 529dd2bfcc6bade1fcf46a57243d8b265eedf7f2..7adfdee9f28444a7d4839e431381de109e16d65b 100644 --- a/storage/round/state.go +++ b/storage/round/state.go @@ -10,9 +10,9 @@ import ( "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" pb "gitlab.com/elixxir/comms/mixmessages" - "gitlab.com/elixxir/primitives/id" "gitlab.com/elixxir/primitives/states" "gitlab.com/xx_network/comms/connect" + "gitlab.com/xx_network/primitives/id" "math" "sync" "testing" @@ -36,6 +36,9 @@ type State struct { // List of round errors received from nodes roundErrors []*pb.RoundError + // List of client errors received from nodes + clientErrors []*pb.ClientError + roundComplete chan struct{} lastUpdate time.Time @@ -44,7 +47,7 @@ type State struct { } //creates a round state object -func newState(id id.Round, batchsize uint32, resourceQueueTimeout time.Duration, +func newState(id id.Round, batchsize, addressSpaceSize uint32, resourceQueueTimeout time.Duration, topology *connect.Circuit, pendingTs time.Time) *State { strTopology := make([][]byte, topology.Len()) @@ -68,6 +71,7 @@ func newState(id id.Round, batchsize uint32, resourceQueueTimeout time.Duration, Topology: strTopology, Timestamps: timestamps, ResourceQueueTimeoutMillis: uint32(resourceQueueTimeout), + AddressSpaceSize: addressSpaceSize, }, topology: topology, state: states.PENDING, @@ -147,6 +151,7 @@ func (s *State) BuildRoundInfo() *pb.RoundInfo { defer s.mux.RUnlock() s.base.Errors = s.roundErrors + s.base.ClientErrors = s.clientErrors s.base.State = uint32(s.state) return CopyRoundInfo(s.base) @@ -184,6 +189,14 @@ func (s *State) AppendError(roundError *pb.RoundError) { s.roundErrors = append(s.roundErrors, roundError) } +// Append a round error to our list of stored rounderrors +func (s *State) AppendClientErrors(clientErrors []*pb.ClientError) { + s.mux.Lock() + defer s.mux.Unlock() + + s.clientErrors = append(s.clientErrors, clientErrors...) +} + //returns the channel used to stop the round timeout func (s *State) GetRoundCompletedChan() <-chan struct{} { return s.roundComplete diff --git a/storage/round/state_test.go b/storage/round/state_test.go index 85f510bff39e691fe22862a1649b7d3f4acb6bcf..63980b01ded3475391b2fab2dbeda245b0c8afea 100644 --- a/storage/round/state_test.go +++ b/storage/round/state_test.go @@ -8,8 +8,8 @@ package round import ( "bytes" - "gitlab.com/elixxir/primitives/id" "gitlab.com/elixxir/primitives/states" + "gitlab.com/xx_network/primitives/id" "math" "reflect" "strings" @@ -27,7 +27,10 @@ func TestState_GetLastUpdate(t *testing.T) { topology := buildMockTopology(numNodes, t) origTime := time.Now() - ns := newState(rid, batchSize, 5*time.Minute, topology, origTime) + ns := newState(rid, batchSize, 8, 5*time.Minute, topology, origTime) + + // Sleep required due to low clock resolution on Windows + time.Sleep(1 * time.Millisecond) err := ns.Update(states.PRECOMPUTING, time.Now()) if err != nil { @@ -37,7 +40,7 @@ func TestState_GetLastUpdate(t *testing.T) { newTime := ns.GetLastUpdate() if origTime.After(newTime) || origTime.Equal(newTime) { - t.Errorf("origTime was after or euqal to newTime") + t.Errorf("origTime was after or equal to newTime") } } @@ -53,7 +56,7 @@ func TestNewState(t *testing.T) { ts := time.Now() - ns := newState(rid, batchSize, 5*time.Minute, topology, ts) + ns := newState(rid, batchSize, 8, 5*time.Minute, topology, ts) if len(ns.base.Timestamps) != int(states.NUM_STATES) { t.Errorf("Length of timestamps list is incorrect: "+ @@ -130,7 +133,7 @@ func TestState_NodeIsReadyForTransition(t *testing.T) { ts := time.Now() - ns := newState(rid, batchSize, 5*time.Minute, topology, ts) + ns := newState(rid, batchSize, 8, 5*time.Minute, topology, ts) if ns.readyForTransition != 0 { t.Errorf("readyForTransmission is incorrect; "+ @@ -166,7 +169,7 @@ func TestState_Update_Forward(t *testing.T) { ts := time.Now() - ns := newState(rid, batchSize, 5*time.Minute, topology, ts) + ns := newState(rid, batchSize, 8, 5*time.Minute, topology, ts) for i := states.PRECOMPUTING; i < states.NUM_STATES; i++ { time.Sleep(1 * time.Millisecond) @@ -201,7 +204,7 @@ func TestState_Update_Same(t *testing.T) { ts := time.Now() - ns := newState(rid, batchSize, 5*time.Minute, topology, ts) + ns := newState(rid, batchSize, 8, 5*time.Minute, topology, ts) for i := states.PENDING; i < states.NUM_STATES; i++ { ns.state = i @@ -245,7 +248,7 @@ func TestState_Update_Reverse(t *testing.T) { ts := time.Now() - ns := newState(rid, batchSize, 5*time.Minute, topology, ts) + ns := newState(rid, batchSize, 8, 5*time.Minute, topology, ts) for i := states.PRECOMPUTING; i < states.NUM_STATES; i++ { ns.state = i @@ -287,7 +290,7 @@ func TestState_BuildRoundInfo(t *testing.T) { ts := time.Now() - ns := newState(rid, batchSize, 5*time.Minute, topology, ts) + ns := newState(rid, batchSize, 8, 5*time.Minute, topology, ts) ns.state = states.FAILED diff --git a/storage/state.go b/storage/state.go index ffa96c7d60501c1be148607214b878bc61d193c9..7e4102eb6fe59ba1c0cecdb391ca7a9de74188e2 100644 --- a/storage/state.go +++ b/storage/state.go @@ -10,17 +10,20 @@ package storage import ( "github.com/golang-collections/collections/set" + "github.com/jinzhu/gorm" "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" pb "gitlab.com/elixxir/comms/mixmessages" "gitlab.com/elixxir/comms/network/dataStructures" - "gitlab.com/elixxir/crypto/signature" - "gitlab.com/elixxir/crypto/signature/rsa" - "gitlab.com/elixxir/primitives/id" - "gitlab.com/elixxir/primitives/ndf" "gitlab.com/elixxir/primitives/states" "gitlab.com/elixxir/registration/storage/node" "gitlab.com/elixxir/registration/storage/round" + "gitlab.com/xx_network/comms/signature" + "gitlab.com/xx_network/crypto/signature/rsa" + "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/ndf" + "strconv" + "strings" "sync" "time" ) @@ -32,19 +35,15 @@ type NetworkState struct { // NetworkState parameters privateKey *rsa.PrivateKey - // The ID of the current round - roundID *stateID - // Round state - rounds *round.StateMap - roundUpdates *dataStructures.Updates - roundUpdateID *stateID - roundUpdateLock sync.Mutex - roundData *dataStructures.Data - update chan node.UpdateNotification // For triggering updates to top level + rounds *round.StateMap + roundUpdates *dataStructures.Updates + roundData *dataStructures.Data + update chan node.UpdateNotification // For triggering updates to top level // Node NetworkState - nodes *node.StateMap + nodes *node.StateMap + updateMux sync.Mutex // List of states of Nodes to be disabled disabledNodesStates *disabledNodes @@ -52,10 +51,13 @@ type NetworkState struct { // NDF state partialNdf *dataStructures.Ndf fullNdf *dataStructures.Ndf + + // Address space size + addressSpaceSize uint32 } // NewState returns a new NetworkState object. -func NewState(pk *rsa.PrivateKey, roundIdPath, updateIdPath string) (*NetworkState, error) { +func NewState(pk *rsa.PrivateKey, addressSpaceSize uint32) (*NetworkState, error) { fullNdf, err := dataStructures.NewNdf(&ndf.NetworkDefinition{}) if err != nil { return nil, err @@ -65,40 +67,54 @@ func NewState(pk *rsa.PrivateKey, roundIdPath, updateIdPath string) (*NetworkSta return nil, err } - // Create round ID - roundID, err := loadOrCreateStateID(roundIdPath, 1) - if err != nil { - return nil, errors.Errorf("Failed to load round ID from path: %+v", err) + state := &NetworkState{ + rounds: round.NewStateMap(), + roundUpdates: dataStructures.NewUpdates(), + update: make(chan node.UpdateNotification, updateBufferLength), + nodes: node.NewStateMap(), + fullNdf: fullNdf, + partialNdf: partialNdf, + privateKey: pk, + addressSpaceSize: addressSpaceSize, } - // Create increment ID - updateRoundID, err := loadOrCreateStateID(updateIdPath, 0) - if err != nil { - return nil, errors.Errorf("Failed to load update ID from path: %+v", err) + // Obtain round & update Id from Storage + // Ignore not found in Storage errors, zero-value will be handled below + updateId, err := state.GetUpdateID() + if err != nil && + !strings.Contains(err.Error(), gorm.ErrRecordNotFound.Error()) && + !strings.Contains(err.Error(), "Unable to locate state for key") { + return nil, err } - - state := &NetworkState{ - roundID: roundID, - rounds: round.NewStateMap(), - roundUpdates: dataStructures.NewUpdates(), - update: make(chan node.UpdateNotification, updateBufferLength), - nodes: node.NewStateMap(), - fullNdf: fullNdf, - partialNdf: partialNdf, - privateKey: pk, - roundUpdateID: updateRoundID, + roundId, err := state.GetRoundID() + if err != nil && + !strings.Contains(err.Error(), gorm.ErrRecordNotFound.Error()) && + !strings.Contains(err.Error(), "Unable to locate state for key") { + return nil, err } // Updates are handled in the uint space, as a result, the designator for // update 0 also designates that no updates are known by the server. To // avoid this collision, permissioning will skip this update as well. - if updateRoundID.get() == 0 { - // Insert dummy update + if updateId == 0 { + // Set update Id to start at 0 + err = state.setId(UpdateIdKey, 0) + if err != nil { + return nil, err + } + // Then insert a dummy and increment to 1 err = state.AddRoundUpdate(&pb.RoundInfo{}) if err != nil { return nil, err } } + if roundId == 0 { + // Set round Id to start at 1 + err = state.setId(RoundIdKey, 1) + if err != nil { + return nil, err + } + } return state, nil } @@ -121,12 +137,11 @@ func (s *NetworkState) GetUpdates(id int) ([]*pb.RoundInfo, error) { // AddRoundUpdate creates a copy of the round before inserting it into // roundUpdates. func (s *NetworkState) AddRoundUpdate(r *pb.RoundInfo) error { - s.roundUpdateLock.Lock() - defer s.roundUpdateLock.Unlock() + s.updateMux.Lock() + defer s.updateMux.Unlock() roundCopy := round.CopyRoundInfo(r) - - updateID, err := s.roundUpdateID.increment() + updateID, err := s.IncrementUpdateID() if err != nil { return err } @@ -194,6 +209,11 @@ func (s *NetworkState) GetNodeMap() *node.StateMap { return s.nodes } +// GetAddressSpaceSize returns the address space size +func (s *NetworkState) GetAddressSpaceSize() uint32 { + return s.addressSpaceSize +} + // NodeUpdateNotification sends a notification to the control thread of an // update to a nodes state. func (s *NetworkState) SendUpdateNotification(nun node.UpdateNotification) error { @@ -210,16 +230,68 @@ func (s *NetworkState) GetNodeUpdateChannel() <-chan node.UpdateNotification { return s.update } -// IncrementRoundID increments the round ID in a thread safe manner. If an error -// occurs while updating the ID file, then it is returned. +// Helper to increment the RoundId or UpdateId depending on the given key +// FIXME: Get and set should be coupled to avoid race conditions +func (s *NetworkState) increment(key string) (uint64, error) { + oldIdStr, err := PermissioningDb.GetStateValue(key) + if err != nil { + return 0, errors.Errorf("Unable to obtain current %s: %+v", key, err) + } + + oldId, err := strconv.ParseUint(oldIdStr, 10, 64) + if err != nil { + return 0, errors.Errorf("Unable to parse current %s: %+v", key, err) + } + + return oldId, s.setId(key, oldId+1) +} + +// Helper to set the roundId or updateId value +func (s *NetworkState) setId(key string, newVal uint64) error { + err := PermissioningDb.UpsertState(&State{ + Key: key, + Value: strconv.FormatUint(newVal, 10), + }) + if err != nil { + return errors.Errorf("Unable to update current round ID: %+v", err) + } + return nil +} + +// Helper to return the RoundId or UpdateId depending on the given key +func (s *NetworkState) get(key string) (uint64, error) { + roundIdStr, err := PermissioningDb.GetStateValue(key) + if err != nil { + return 0, errors.Errorf("Unable to obtain current %s: %+v", key, err) + } + + roundId, err := strconv.ParseUint(roundIdStr, 10, 64) + if err != nil { + return 0, errors.Errorf("Unable to parse current %s: %+v", key, err) + } + return roundId, nil +} + +// IncrementRoundID increments the round ID func (s *NetworkState) IncrementRoundID() (id.Round, error) { - roundID, err := s.roundID.increment() - return id.Round(roundID), err + roundId, err := s.increment(RoundIdKey) + return id.Round(roundId), err +} + +// IncrementUpdateID increments the update ID +func (s *NetworkState) IncrementUpdateID() (uint64, error) { + return s.increment(UpdateIdKey) +} + +// GetRoundID returns the round ID +func (s *NetworkState) GetRoundID() (id.Round, error) { + roundId, err := s.get(RoundIdKey) + return id.Round(roundId), err } -// GetRoundID returns the round ID in a thread safe manner. -func (s *NetworkState) GetRoundID() id.Round { - return id.Round(s.roundID.get()) +// GetRoundID returns the update ID +func (s *NetworkState) GetUpdateID() (uint64, error) { + return s.get(UpdateIdKey) } // CreateDisabledNodes generates and sets a disabledNodes object that will track diff --git a/storage/stateID.go b/storage/stateID.go deleted file mode 100644 index a7a315fadb660021da1c8e96da5d2edcdc7bb669..0000000000000000000000000000000000000000 --- a/storage/stateID.go +++ /dev/null @@ -1,89 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Copyright © 2020 Privategrity Corporation / -// / -// All rights reserved. / -//////////////////////////////////////////////////////////////////////////////// - -// Package storage defines the structure which creates and tracks the RoundID. -// It only allows itself to incremented forward by 1. -package storage - -import ( - "github.com/pkg/errors" - jww "github.com/spf13/jwalterweatherman" - "gitlab.com/elixxir/primitives/utils" - "strconv" - "strings" - "sync" -) - -// roundID structure contains the current ID and the file path to store it. -type stateID struct { - id uint64 - path string - sync.RWMutex -} - -// loadOrCreateStateID loads a new round ID from the specified file path and -// returns a new roundID with that ID. If no path is provided or the file does -// not exist, then the ID is set to startId. -func loadOrCreateStateID(path string, startId uint64) (*stateID, error) { - // Skip reading from the file if no path is provided or file does not exist - if path != "" && utils.FileExists(path) { - roundIdBytes, err := utils.ReadFile(path) - if err != nil { - return nil, errors.Errorf("Could not load ID from file: %+v", err) - } - roundIdString := strings.TrimSpace(string(roundIdBytes)) - startId, err = strconv.ParseUint(roundIdString, 10, 64) - if err != nil { - return nil, errors.Errorf("Could not convert ID to uint: %+v", err) - } - } else { - jww.WARN.Printf("Could not open state ID path %s because file does "+ - "not exist, reading ID from file skipped. state ID set to %d.", - path, startId) - } - - return &stateID{ - id: startId, - path: path, - }, nil -} - -// increment increments the ID by one, saves the new ID to file, and returns the -// previous ID. This function is thread safe. The internal value is updated only -// after the file write succeeds. If no path is provided, then only the ID in -// memory is updated. -func (rid *stateID) increment() (uint64, error) { - rid.Lock() - defer rid.Unlock() - - oldID := rid.id - newID := rid.id + 1 - - // Skip updating the file if no path is provided - if rid.path != "" { - // Convert the incremented ID to a string and write to file - idBytes := []byte(strconv.FormatUint(newID, 10)) - err := utils.WriteFile(rid.path, idBytes, utils.FilePerms, utils.DirPerms) - if err != nil { - return 0, errors.WithMessagef(err, "can't update to %d", newID) - } - } else { - jww.WARN.Printf("The state ID path is empty, updating ID file skipped.") - } - - // Update the ID in memory - rid.id = newID - - return oldID, nil -} - -// get returns the current ID. -func (rid *stateID) get() uint64 { - rid.RLock() - defer rid.RUnlock() - - return rid.id -} diff --git a/storage/stateID_test.go b/storage/stateID_test.go deleted file mode 100644 index 92a5cb7470fcbe6a1f2d1add013337302833250e..0000000000000000000000000000000000000000 --- a/storage/stateID_test.go +++ /dev/null @@ -1,376 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Copyright © 2020 Privategrity Corporation / -// / -// All rights reserved. / -//////////////////////////////////////////////////////////////////////////////// -package storage - -import ( - "gitlab.com/elixxir/primitives/utils" - "os" - "strconv" - "testing" - "time" -) - -// Tests that loadOrCreateStateID() correctly reads the ID from file and -// constructs the roundID with the correct values. -func TestLoadRoundID(t *testing.T) { - expectedPath := "testRoundID.txt" - expectedID := uint64(9843) - idString := []byte(strconv.FormatUint(expectedID, 10)) - - defer func() { - err := os.RemoveAll(expectedPath) - if err != nil { - t.Fatalf("%+v", err) - } - }() - - err := utils.WriteFile(expectedPath, idString, utils.FilePerms, utils.DirPerms) - if err != nil { - t.Fatalf("Failed to write test file: %+v", err) - } - - testSID, err := loadOrCreateStateID(expectedPath, 0) - if err != nil { - t.Errorf("loadOrCreateStateID() produced an unexpected error: %+v", err) - } - - if expectedID != testSID.id { - t.Errorf("loadOrCreateStateID() returned a roundID with an incorrect ID."+ - "\n\t expected: %+v\n\treceived: %+v", expectedID, testSID.id) - } - - if expectedPath != testSID.path { - t.Errorf("loadOrCreateStateID() returned a roundID with an incorrect path."+ - "\n\t expected: %+v\n\treceived: %+v", expectedPath, testSID.path) - } -} - -// Tests that loadOrCreateStateID() sets the ID to 0 when no path is provided. -func TestLoadRoundID_EmptyPath(t *testing.T) { - expectedPath := "" - expectedID := uint64(0) - - testSID, err := loadOrCreateStateID(expectedPath, 0) - if err != nil { - t.Errorf("loadOrCreateStateID() produced an unexpected error: %+v", err) - } - - if expectedID != testSID.id { - t.Errorf("loadOrCreateStateID() returned a roundID with an incorrect ID."+ - "\n\t expected: %+v\n\treceived: %+v", expectedID, testSID.id) - } - - if expectedPath != testSID.path { - t.Errorf("loadOrCreateStateID() returned a roundID with an incorrect path."+ - "\n\t expected: %+v\n\treceived: %+v", expectedPath, testSID.path) - } -} - -// Tests that loadOrCreateStateID() returns an error when the file does not contain a -// uint64. -func TestLoadRoundID_FileContentError(t *testing.T) { - expectedPath := "testRoundID.txt" - expectedError := "Could not convert ID to uint: strconv.ParseUint: " + - "parsing \"test\": invalid syntax" - - defer func() { - err := os.RemoveAll(expectedPath) - if err != nil { - t.Fatalf("%+v", err) - } - }() - - err := utils.WriteFile(expectedPath, []byte("test"), utils.FilePerms, utils.DirPerms) - if err != nil { - t.Fatalf("Failed to write test file: %+v", err) - } - - _, err = loadOrCreateStateID(expectedPath, 0) - if err == nil { - t.Errorf("loadOrCreateStateID() did not produce the expected error."+ - "\n\texpected: %+v\n\treceived: %+v", expectedError, err) - } -} - -// Tests that increment() increments the ID the correct number of times and -// saves the value to file. -func TestRoundID_Increment(t *testing.T) { - testID := uint64(9843) - testPath := "testRoundID.txt" - incrementAmount := uint64(10) - testSID := stateID{ - id: testID, - path: testPath, - } - - defer func() { - err := os.RemoveAll(testPath) - if err != nil { - t.Fatalf("%+v", err) - } - }() - - for i := uint64(0); i < incrementAmount; i++ { - oldID, err := testSID.increment() - if err != nil { - t.Errorf("increment() produced an unexpected error on index %d: "+ - "%+v", i, err) - } - - // Test that the correct old ID was returned - if oldID != testID+i { - t.Errorf("increment() did not return the correct old ID."+ - "\n\texpected: %+v\n\treceived: %+v", testID+i, oldID) - } - - // Test that the ID in memory was correctly incremented - if testSID.id != testID+i+1 { - t.Errorf("increment() did not increment the ID in memory correctly."+ - "\n\texpected: %+v\n\treceived: %+v", testID+i+1, testSID.id) - } - - // Test that the ID on disk was correctly incremented - sidBytes, err := utils.ReadFile(testPath) - if err != nil { - t.Fatalf("%+v", err) - } - idUint, err := strconv.ParseUint(string(sidBytes), 10, 64) - if err != nil { - t.Fatalf("%+v", err) - } - if idUint != testID+i+1 { - t.Errorf("increment() did not increment the ID on disk "+ - "correctly.\n\texpected: %+v\n\treceived: %+v", - testID+i+1, idUint) - } - } -} - -// Tests that increment() increments the internal ID the correct number of times -// but skips writing to file when an empty path is provided. -func TestRoundID_Increment_EmptyPath(t *testing.T) { - testID := uint64(9843) - testPath := "" - incrementAmount := uint64(10) - testSID := stateID{ - id: testID, - path: testPath, - } - - for i := uint64(0); i < incrementAmount; i++ { - oldID, err := testSID.increment() - if err != nil { - t.Errorf("increment() produced an unexpected error on "+ - "index %d: %+v", i, err) - } - - // Test that the correct old ID was returned - if oldID != testID+i { - t.Errorf("increment() did not return the correct old ID."+ - "\n\texpected: %+v\n\treceived: %+v", testID+i, oldID) - } - - // Test that the ID in memory was correctly incremented - if testSID.id != testID+i+1 { - t.Errorf("increment() did not increment the ID in memory correctly."+ - "\n\texpected: %+v\n\treceived: %+v", - testID+i+1, testSID.id) - } - } -} - -// Tests that increment() returns an error for an invalid file path and that the ID -// is not updated on error. -func TestRoundID_Increment_FileError(t *testing.T) { - testID := uint64(9843) - testPath := "~a/testRoundID.txt" - testSID := stateID{ - id: testID, - path: testPath, - } - - defer func() { - err := os.RemoveAll(testPath) - if err != nil { - t.Fatalf("%+v", err) - } - }() - - _, err := testSID.increment() - if err == nil { - t.Errorf("increment() did not produce an error on an invalid path.") - } - - if testSID.id != testID { - t.Errorf("increment() unexpectedly incremented the ID on error."+ - "\n\texpected: %+v\n\treceived: %+v", testID, testSID.id) - } -} - -// Tests that increment() blocks when the thread is locked. -func TestRoundID_Increment_Lock(t *testing.T) { - expectedID := uint64(9843) - testSID := stateID{ - id: expectedID, - path: "testRoundID.txt", - } - - result := make(chan bool) - - testSID.Lock() - - go func() { - _, _ = testSID.increment() - result <- true - }() - - select { - case <-result: - t.Errorf("increment() did not correctly lock the thread.") - case <-time.After(time.Second): - return - } -} - -// Tests that get() returns the correct value. -func TestRoundID_Get(t *testing.T) { - expectedID := uint64(9843) - testSID := stateID{ - id: expectedID, - path: "testRoundID.txt", - } - - testID := testSID.get() - - if expectedID != testID { - t.Errorf("get() returned an incorrect ID."+ - "\n\texpected: %+v\n\treceived: %+v", expectedID, testID) - } -} - -// Tests that get() blocks when the thread is locked. -func TestRoundID_Get_Lock(t *testing.T) { - expectedID := uint64(9843) - testSID := stateID{ - id: expectedID, - path: "testRoundID.txt", - } - - result := make(chan bool) - - testSID.Lock() - - go func() { - _ = testSID.get() - result <- true - }() - - select { - case <-result: - t.Errorf("get() did not correctly lock the thread.") - case <-time.After(time.Second): - return - } -} - -// Tests that calling loadOrCreateStateID() multiple times on a previously -// incremented ID files results in the correct ID. This simulates what the ID -// file will do in integration. -func TestRoundID_IntegrationSim(t *testing.T) { - testID := uint64(9843) - testPath := "testRoundID.txt" - idString := []byte(strconv.FormatUint(testID, 10)) - incrementAmount := uint64(10) - idTracker := testID - - defer func() { - err := os.RemoveAll(testPath) - if err != nil { - t.Fatalf("%+v", err) - } - }() - - err := utils.WriteFile(testPath, idString, utils.FilePerms, utils.DirPerms) - if err != nil { - t.Fatalf("Failed to write test file: %+v", err) - } - - for i := 0; i < 5; i++ { - testSID, err2 := loadOrCreateStateID(testPath, 0) - if err2 != nil { - t.Errorf("loadOrCreateStateID() produced an unexpected error at "+ - "index %d: %+v", i, err2) - } - - if testSID.id != idTracker { - t.Errorf("loadOrCreateStateID() produced a state ID with an "+ - "incorrect ID at index %d.\n\texpected: %+v\n\treceived: %+v", - i, idTracker, testSID.id) - } - - for j := uint64(0); j < incrementAmount; j++ { - _, err = testSID.increment() - if err != nil { - t.Errorf("increment() produced an unexpected error on index "+ - "%d: %+v", j, err) - } - } - - idTracker += incrementAmount - - if testSID.id != idTracker { - t.Errorf("increment() did not increment the id correctly at index "+ - "%d.\n\texpected: %+v\n\treceived: %+v", - i, idTracker, testSID.id) - } - } -} - -// Tests that calling loadOrCreateStateID() multiple times on a previously -// incremented ID files results in the correct ID. This simulates what the ID -// file will do in integration. -func TestRoundID_IntegrationSim_NoFile(t *testing.T) { - testPath := "testRoundID.txt" - incrementAmount := uint64(10) - idTracker := uint64(0) - - defer func() { - err := os.RemoveAll(testPath) - if err != nil { - t.Fatalf("%+v", err) - } - }() - - for i := 0; i < 5; i++ { - testSID, err2 := loadOrCreateStateID(testPath, 0) - if err2 != nil { - t.Errorf("loadOrCreateStateID() produced an unexpected error at "+ - "index %d: %+v", i, err2) - } - - if testSID.id != idTracker { - t.Errorf("loadOrCreateStateID() produced a state ID with an "+ - "incorrect ID at index %d.\n\texpected: %+v\n\treceived: %+v", - i, idTracker, testSID.id) - } - - for j := uint64(0); j < incrementAmount; j++ { - _, err := testSID.increment() - if err != nil { - t.Errorf("increment() produced an unexpected error on index "+ - "%d: %+v", j, err) - } - } - - idTracker += incrementAmount - - if testSID.id != idTracker { - t.Errorf("increment() did not increment the id correctly at index "+ - "%d.\n\texpected: %+v\n\treceived: %+v", - i, idTracker, testSID.id) - } - } -} diff --git a/storage/state_test.go b/storage/state_test.go index 4d3b80379419450c4470c0fc49f45e68eeda9eff..9eb840f617f8cdb1065a8b914b338c1c012ac272 100644 --- a/storage/state_test.go +++ b/storage/state_test.go @@ -12,14 +12,14 @@ import ( "github.com/pkg/errors" pb "gitlab.com/elixxir/comms/mixmessages" "gitlab.com/elixxir/comms/network/dataStructures" - "gitlab.com/elixxir/crypto/signature" - "gitlab.com/elixxir/crypto/signature/rsa" "gitlab.com/elixxir/primitives/current" - "gitlab.com/elixxir/primitives/id" - "gitlab.com/elixxir/primitives/ndf" - "gitlab.com/elixxir/primitives/utils" "gitlab.com/elixxir/registration/storage/node" "gitlab.com/elixxir/registration/storage/round" + "gitlab.com/xx_network/comms/signature" + "gitlab.com/xx_network/crypto/signature/rsa" + "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/ndf" + "gitlab.com/xx_network/primitives/utils" mrand "math/rand" "os" "reflect" @@ -44,6 +44,11 @@ func TestNewState(t *testing.T) { t.Fatalf("Failed to generate new NDF:\n%v", err) } + PermissioningDb, _, err = NewDatabase("", "", "", "", "") + if err != nil { + t.Errorf(err.Error()) + } + // Generate private RSA key privateKey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { @@ -51,7 +56,7 @@ func TestNewState(t *testing.T) { } // Generate new NetworkState - state, err := NewState(privateKey, "", "") + state, err := NewState(privateKey, 8) if err != nil { t.Errorf("NewState() produced an unexpected error:\n%v", err) } @@ -104,6 +109,12 @@ func TestNewState_PrivateKeyError(t *testing.T) { "signature: Unable to sign message: crypto/rsa: key size too small " + "for PSS signature" + var err error + PermissioningDb, _, err = NewDatabase("", "", "", "", "") + if err != nil { + t.Errorf(err.Error()) + } + // Generate private RSA key privateKey, err := rsa.GenerateKey(rand.Reader, 128) if err != nil { @@ -111,12 +122,12 @@ func TestNewState_PrivateKeyError(t *testing.T) { } // Generate new NetworkState - state, err := NewState(privateKey, "", "") + state, err := NewState(privateKey, 8) // Test NewState() output if err == nil || err.Error() != expectedErr { t.Errorf("NewState() did not produce an error when expected."+ - "\n\texpected: %s\n\treceived: %s", expectedErr, err.Error()) + "\n\texpected: %s\n\treceived: %s", expectedErr, err) } if state != nil { t.Errorf("NewState() unexpedly produced a non-nil NetworkState when an error was produced."+ @@ -225,8 +236,6 @@ func TestNetworkState_AddRoundUpdate(t *testing.T) { t.Fatalf("%+v", err) } - state.roundUpdateID.id = 1 - // Call AddRoundUpdate() err = state.AddRoundUpdate(testRoundInfo) if err != nil { @@ -274,6 +283,11 @@ func TestNetworkState_AddRoundUpdate_Error(t *testing.T) { expectedErr := "Could not add round update 1 for round 0 due to failed " + "signature: Unable to sign message: crypto/rsa: key size too small for " + "PSS signature" + var err error + PermissioningDb, _, err = NewDatabase("", "", "", "", "") + if err != nil { + t.Errorf(err.Error()) + } // Generate new NetworkState state, _, err := generateTestNetworkState() @@ -491,7 +505,7 @@ func generateTestNetworkState() (*NetworkState, *rsa.PrivateKey, error) { } // Generate new NetworkState using the private key - state, err := NewState(privateKey, "", "") + state, err := NewState(privateKey, 8) if err != nil { return state, privateKey, fmt.Errorf("NewState() produced an unexpected error:\n+%v", err) } @@ -502,14 +516,23 @@ func generateTestNetworkState() (*NetworkState, *rsa.PrivateKey, error) { // Tests that IncrementRoundID() increments the ID correctly. func TestNetworkState_IncrementRoundID(t *testing.T) { testID := uint64(9843) + var err error + PermissioningDb, _, err = NewDatabase("", "", "", "", "") + if err != nil { + t.Errorf(err.Error()) + t.FailNow() + } + err = PermissioningDb.UpsertState(&State{ + Key: RoundIdKey, + Value: fmt.Sprintf("%d", testID), + }) + if err != nil { + t.Errorf(err.Error()) + } + testPath := "testRoundID.txt" incrementAmount := uint64(10) - testState := NetworkState{ - roundID: &stateID{ - id: testID, - path: testPath, - }, - } + testState := NetworkState{} defer func() { err := os.RemoveAll(testPath) @@ -536,14 +559,27 @@ func TestNetworkState_IncrementRoundID(t *testing.T) { // Tests that GetRoundID() returns the correct value. func TestNetworkState_GetRoundID(t *testing.T) { expectedID := id.Round(9843) - testState := NetworkState{ - roundID: &stateID{ - id: uint64(expectedID), - path: "testRoundID.txt", - }, + + var err error + PermissioningDb, _, err = NewDatabase("", "", "", "", "") + if err != nil { + t.Errorf(err.Error()) + t.FailNow() } + err = PermissioningDb.UpsertState(&State{ + Key: RoundIdKey, + Value: fmt.Sprintf("%d", expectedID), + }) + if err != nil { + t.Errorf(err.Error()) + } + + testState := NetworkState{} - testID := testState.GetRoundID() + testID, err := testState.GetRoundID() + if err != nil { + t.Errorf(err.Error()) + } if expectedID != testID { t.Errorf("GetRoundID() returned an incorrect ID."+ diff --git a/storage/storage.go b/storage/storage.go new file mode 100644 index 0000000000000000000000000000000000000000..040b32293b2026a11d719564f2f705a0df74ff6b --- /dev/null +++ b/storage/storage.go @@ -0,0 +1,26 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 Privategrity Corporation / +// / +// All rights reserved. / +//////////////////////////////////////////////////////////////////////////////// + +// Handles the high level storage API. +// This layer merges the business logic layer and the database layer + +package storage + +import "testing" + +// Global variable for Database interaction +var PermissioningDb Storage + +// API for the storage layer +type Storage struct { + // Stored Database interface + database +} + +// Test use only function for exposing MapImpl +func (s *Storage) GetMapImpl(t *testing.T) *MapImpl { + return s.database.(*MapImpl) +} diff --git a/testkeys/clientNDF.json b/testkeys/clientNDF.json index 2b6a3f9e8daaf8b0a8ec2a29df15c1eef468f08a..5bdc6807f3710f2d36d6f1b4b58f35595e0d2d4c 100644 --- a/testkeys/clientNDF.json +++ b/testkeys/clientNDF.json @@ -82,4 +82,3 @@ "Generator":"2" } } -jhwpnRhQlA9L4oWlndWbbYgESFx6d/ZHCiLVs/k83S5zkk1WJejMI3mK/CG5lYg7lgO411OzM9rCic36l5EeVoZW7xVG8Qyh834rj+Q6QWxxgrW9UQoAIrEQ3Mm4LuyhBRolTEUNSx+1jwKLja1IaZGV2F06qq8LeaqbqFFlanuTc+5ns1OD2nvtoqL+OTRAM6e3+0LYA9wR5RmA7KG10ZnIdG9v3TV0frD/A1n2QLNoFus8XTy54U5vt3+UiVbMsGGoUDWCg8xHaGiaHj5j30eQDxU17GUiAurorRMUnH/1UMD3NLUtw2NfhuWqikR/a8KG4xqyJ3RgqpOWCa9luX0QzSyo/lANbpTjFHPXi1JNXPfT7808d74KXr/lGBEYfxLXoABmS8GyUwb6sScnNAnbnWUL+9J++rPZ3OVlfTjUFRbGPZi1alVdzX3qevTKIJ+bUUhjSHBA3wOrhalIaquFOZHI7o/jaGdtmdVILRqga+7B04vy0K7guEp+xcRZqXT5VV878r92pJEv9+nAlLAohhn0OFEXxyB+47mVkmCqoC0UJb2s+ZjVU4Pwf/x6/Qu71IVKQLnoipsY7cUV2IDdBwlGjocg5ImMijVw+lthbs8bk0G6m9owmTAOEI4ux1M/4ykKM1lzm/JCYq0o4ULig0nVy52m9Y/snmGCV/8= \ No newline at end of file diff --git a/testkeys/keypath.go b/testkeys/keypath.go index 194a8f7b5d934607a48252fe1ab86e4f885ee07f..a343cc7536f498fd9bb146c0a5b8ef5e8dddfeae 100644 --- a/testkeys/keypath.go +++ b/testkeys/keypath.go @@ -55,6 +55,10 @@ func GetNDFPath() string { return filepath.Join(getDirForFile(), "ndf.json") } +func GetUdbCertPath() string { + return filepath.Join(getDirForFile(), "udb.crt") +} + func GetClientPublicKey() string { return filepath.Join(getDirForFile(), "cmix.public_key.pem") } diff --git a/testkeys/udb.crt b/testkeys/udb.crt new file mode 100644 index 0000000000000000000000000000000000000000..4b622ae0f74ee4c5ec47a74fa28d29193b3f3361 --- /dev/null +++ b/testkeys/udb.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDOTCCAiGgAwIBAgIUC0PgcjZfa9ApbtSoRzr0y3vx4vAwDQYJKoZIhvcNAQEL +BQAwLDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRAwDgYDVQQKDAdFbGl4eGly +MB4XDTIwMTAyNzE4MjMwMVoXDTMwMTAyNTE4MjMwMVowLDELMAkGA1UEBhMCVVMx +CzAJBgNVBAgMAkNBMRAwDgYDVQQKDAdFbGl4eGlyMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAoVdpRzLj/+zexaf2+1ZvY8uOwvdhyYAGvvi//znlL3jv +ZpTD8ltQ3v50LIBrcmyTvsEE//iKrL0nd1PHZB/jD/PGRzC5ssnYfXL+YwsHQrbk +mgWQdmbAfiLE5ZBK89zoV39ojNqWj47pQkQ1gsZnnS8pqCOtv9rCZIkKATQx1/hA +gUxMrkO1M61zuKtwJFnRfct/UaQSZtIs37fpvKJzNy4Ok8kkplMHg2VWI7+99Cqb +UexnNbOq7pPSnewt+wC4pcxeR3Bi6JBkHWpntsOdzIfSLNqhXvnnzurMwscjsfnj +KJjsx0UZClZHP3AO2i3qvqz7ytGGzthAuEfWqH1vdwIDAQABo1MwUTAdBgNVHQ4E +FgQUcxnSaGW3Vn1Xd13g1SBfYMPFSVIwHwYDVR0jBBgwFoAUcxnSaGW3Vn1Xd13g +1SBfYMPFSVIwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAFkpc +dp3hXNOt+szMXbEryCARrpvy1VUMjAEZd/sqAA/eXgq8h2cNe0Hkjm25iMPTNiJQ +6+nOng3HJixqxPjZDw/GgvwYPRGDulwyOe2p9gHa3Ib8PvPL9ZET6BqqXF0VZ5S2 +rvbjBOHTAhx0ydJVPmW4vTaoK7vOqaoqEgMw14IUjGYBzIcqdW78Aj5HFlgI8g+w +wceLuCmH2idoh55fEq1jczSbq1S4GEtoM0Yx7zBNXnBb30YThIJ652smkdrTsIxV +jrKOtzZ/EMp7qE96S0HRz0S4EjPUHC7d/v1WB3pVv5XSoFbn2N7ZUMRFgDlvSeyu +rnLb1hgS8uEk5jJzLA== +-----END CERTIFICATE----- \ No newline at end of file