Skip to content
Snippets Groups Projects
Unverified Commit 9b192c57 authored by Sydney Anne Erickson's avatar Sydney Anne Erickson :chipmunk:
Browse files

Initial push

parents
Branches
Tags
No related merge requests found
*~
# Ignore glide files/folders
glide.lock
vendor/
# Ignore Jetbrains IDE folder
.idea/*
# Ignore vim .swp buffers for open files
.*.swp
.*.swo
# Ignore local development scripts
localdev_*
# Ignore logs
*.log
# Android things
*.iml
/android/.gradle
/android/local.properties
/android/.idea/workspace.xml
/android/.idea/libraries
/android/.DS_Store
/android/build
/android/captures
/android/.externalNativeBuild
*.apk
*.ap_
*.dex
*.class
*.aar
*.jar
# Ignore genered version file
cmd/version_vars.go
# From: https://about.gitlab.com/2017/09/21/how-to-create-ci-cd-pipeline-with-autodeploy-to-kubernetes-using-gitlab-and-helm/
cache:
untracked: true
key: "$CI_BUILD_REF_NAME"
paths:
- vendor/
variables:
REPO_DIR: gitlab.com/elixxir
REPO_NAME: primitives
DOCKER_IMAGE: elixxirlabs/cuda-go:latest
MIN_CODE_COVERAGE: "89.0"
before_script:
- go version || echo "Go executable not found."
- echo $CI_BUILD_REF
- echo $CI_PROJECT_DIR
- echo $PWD
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- ssh-keyscan -t rsa gitlab.com > ~/.ssh/known_hosts
- git config --global url."git@gitlab.com:".insteadOf "https://gitlab.com/"
- export PATH=$HOME/go/bin:$PATH
- export GOPRIVATE=gitlab.com/elixxir/*
stages:
- build
- trigger_integration
build:
stage: build
image: $DOCKER_IMAGE
script:
- git clean -ffdx
- go mod vendor -v
- go build ./...
- go mod tidy
- mkdir -p testdata
- go-acc --covermode atomic --output testdata/coverage.out ./... -- -v
- cat testdata/coverage.out | grep -v pb[.]go > testdata/coverage-real.out
- go tool cover -func=testdata/coverage-real.out
- go tool cover -html=testdata/coverage-real.out -o testdata/coverage.html
# Test Coverage Check
- go tool cover -func=testdata/coverage-real.out | grep "total:" | awk '{print $3}' | sed 's/\%//g' > testdata/coverage-percentage.txt
- export CODE_CHECK=$(echo "$(cat testdata/coverage-percentage.txt) >= $MIN_CODE_COVERAGE" | bc -l)
- (if [ "$CODE_CHECK" == "1" ]; then echo "Minimum coverage of $MIN_CODE_COVERAGE succeeded"; else echo "Minimum coverage of $MIN_CODE_COVERAGE failed"; exit 1; fi);
- mkdir -p release
- GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags '-w -s' ./...
- cd release
artifacts:
paths:
- vendor/
- testdata/
- release/
trigger:
stage: trigger_integration
script:
# client
- "curl -X POST -F token=$CLIENT_TRIGGER_KEY -F ref=master https://gitlab.com/api/v4/projects/5454785/trigger/pipeline"
# server
- "curl -X POST -F token=$SERVER_TRIGGER_KEY -F ref=master https://gitlab.com/api/v4/projects/5014439/trigger/pipeline"
only:
- master
LICENSE 0 → 100644
To whom it may concern,
You can download, modify, compile and deploy this source code for the purpose of participating in the xx network as a
beta node, in accordance with your beta node participation agreement.
You can also download, modify, compile and deploy the source code for non-commercial testing and verification
(i.e., security and bug review) purposes. You can repost aspects of the source code on both the BetaNet forum
(forum.xx.network) and the official Discord (https://discord.gg/D4NHmv4) consistent with these purposes.
To release a bug or security report outside the BetaNet forum or official Discord, you must notify bugs@xx.network at
least three business days in advance Pacific time.
The xx network SEZC hereby grants you a non-transferable license under its legal rights limited to the purposes above.
This Agreement and the license that it grants you expires the earlier of March 1st 2021 or the launch of the xx network
MainNet.
THE SOURCE CODE IS PROVIDED TO YOU ON AN “AS IS” BASIS WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED,
INCLUDING ANY WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR USE, OR ANY WARRANTY THAT THE SOURCE CODE
DOES NOT INFRINGE THE RIGHTS OF OTHERS (WHETHER PATENT RIGHTS, COPYRIGHTS OR OTHERWISE).
THE XX NETWORK SEZC WILL NOT BE LIABLE TO YOU FOR ANY DAMAGES OF ANY KIND, WHETHER DIRECT, SPECIAL, CONSEQUENTIAL,
INCIDENTAL, INDIRECT OR OTHERWISE, EVEN IF THE XX NETWORK SEZC HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES,
WHICH ARISE OUT OF THIS AGREEMENT OR THE USE OR PERFORMANCE OF THE SOURCE CODE.
The xx network SEZC
Makefile 0 → 100644
.PHONY: update master release setup update_master update_release build clean
setup:
git config --global --add url."git@gitlab.com:".insteadOf "https://gitlab.com/"
clean:
rm -rf vendor/
go mod vendor
update:
-GOFLAGS="" go get -u all
build:
go build ./...
go mod tidy
update_release:
update_master:
master: clean update_master build
release: clean update_release build
go.mod 0 → 100644
module gitlab.com/xx_network/primitives
require (
github.com/pkg/errors v0.9.1
github.com/spf13/jwalterweatherman v1.1.0
gitlab.com/elixxir/primitives v0.0.0-20200731184040-494269b53b4d
)
go 1.13
go.sum 0 → 100644
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
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/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
gitlab.com/elixxir/primitives v0.0.0-20200731184040-494269b53b4d h1:OKWTmYN5q8XVHo8JXThIH0TCuvl/fLXR7MGVacpqfRg=
gitlab.com/elixxir/primitives v0.0.0-20200731184040-494269b53b4d/go.mod h1:OQgUZq7SjnE0b+8+iIAT2eqQF+2IFHn73tOo+aV11mg=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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/yaml.v2 v2.2.2/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=
////////////////////////////////////////////////////////////////////////////////////////////
// Copyright © 2020 xx network SEZC //
// //
// Use of this source code is governed by a license that can be found in the LICENSE file //
////////////////////////////////////////////////////////////////////////////////////////////
package id
// Stored here are global hard coded IDs. The last byte should be set to the
// correct ID type.
// Note: When adding or removing a hard coded ID, make sure to update
// GetHardCodedIDs() with the changes.
// ID for permissioning (ID data is the string "Permissioning")
var Permissioning = ID{80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 105,
110, 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, byte(Generic)}
// ID for notification bot (ID data is the string "notification-bot")
var NotificationBot = ID{110, 111, 116, 105, 102, 105, 99, 97, 116, 105, 111,
110, 45, 98, 111, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, byte(Generic)}
// ID for a temporary gateway (ID data is the string "tmp")
var TempGateway = ID{116, 109, 112, 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, byte(Gateway)}
// ID for a user with the ID data all zeroes
var ZeroUser = ID{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, 0, byte(User)}
// ID for a dummy user (ID data is the string "dummy")
var DummyUser = ID{100, 117, 109, 109, 121, 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, byte(User)}
// ID for UDB (ID data is in the range of dummy IDs)
var UDB = ID{0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, byte(User)}
// GetHardCodedIDs returns an array of all the hard coded IDs.
func GetHardCodedIDs() (ids []*ID) {
ids = append(ids, &Permissioning)
ids = append(ids, &NotificationBot)
ids = append(ids, &TempGateway)
ids = append(ids, &ZeroUser)
ids = append(ids, &DummyUser)
ids = append(ids, &UDB)
return
}
// CollidesWithHardCodedID searches if the given ID collides with any hard coded
// IDs. If it collides, then the function returns true. Otherwise, it returns
// false.
func CollidesWithHardCodedID(testID *ID) bool {
for _, hardCodedID := range GetHardCodedIDs() {
if testID.Cmp(hardCodedID) {
return true
}
}
return false
}
////////////////////////////////////////////////////////////////////////////////
// Copyright © 2020 Privategrity Corporation /
// /
// All rights reserved. /
////////////////////////////////////////////////////////////////////////////////
package id
import (
"testing"
)
// Tests that GetHardCodedIDs() returns all the hard coded IDs in the order that
// they were added.
func TestGetHardCodedIDs(t *testing.T) {
expectedIDs := []*ID{&Permissioning, &NotificationBot, &TempGateway,
&ZeroUser, &DummyUser, &UDB}
for i, testID := range GetHardCodedIDs() {
if !expectedIDs[i].Cmp(testID) {
t.Errorf("GetHardCodedIDs() did not return the expected ID at "+
"index %d.\n\texepcted: %v\n\trecieved: %v",
i, expectedIDs[i], testID)
}
}
}
// Tests that CollidesWithHardCodedID() returns false when none of the test IDs
// collide with the hard coded IDs.
func TestCollidesWithHardCodedID_HappyPath(t *testing.T) {
testIDs := []*ID{
NewIdFromString("Test1", Generic, t),
NewIdFromString("Test2", Gateway, t),
NewIdFromString("Test3", Node, t),
NewIdFromString("Test4", User, t),
}
for _, testID := range testIDs {
if CollidesWithHardCodedID(testID) {
t.Errorf("CollidesWithHardCodedID() found collision when none "+
"should exist.\n\tcolliding ID: %v", testID)
}
}
}
// Tests that CollidesWithHardCodedID() returns true when checking if hard coded
// IDs collide.
func TestCollidesWithHardCodedID_True(t *testing.T) {
testIDs := []*ID{&Permissioning, &NotificationBot, &TempGateway,
&ZeroUser, &DummyUser, &UDB}
for _, testID := range testIDs {
if !CollidesWithHardCodedID(testID) {
t.Errorf("CollidesWithHardCodedID() did not find a collision when "+
"one should exist.\n\tcolliding ID: %v", testID)
}
}
}
id/id.go 0 → 100644
////////////////////////////////////////////////////////////////////////////////////////////
// Copyright © 2020 xx network SEZC //
// //
// Use of this source code is governed by a license that can be found in the LICENSE file //
////////////////////////////////////////////////////////////////////////////////////////////
// Contains the generic ID type, which is a byte array that represents an entity
// ID. The first bytes in the array contain the actual ID data while the last
// byte contains the ID type, which is either generic, gateway, node, or user.
// IDs can be hard coded or generated using a cryptographic function found in
// crypto.
package id
import (
"encoding/base64"
"encoding/binary"
"github.com/pkg/errors"
jww "github.com/spf13/jwalterweatherman"
"testing"
)
const (
// Length of the ID data
dataLen = 32
// Length of the full ID array
ArrIDLen = dataLen + 1
)
// ID is an array holding the generic identifier. The first 32 bytes hold the ID
// data and the last byte holds the ID type.
type ID [ArrIDLen]byte
// Marshal takes an ID and copies the data into the wire format.
func (i *ID) Marshal() []byte {
return i.Bytes()
}
// Unmarshal takes in the ID wire format and copies the data into an ID.
func Unmarshal(data []byte) (*ID, error) {
// Return an error if the length of data is incorrect
if len(data) != ArrIDLen {
return nil, errors.Errorf("Could not unmarshal byte slice to ID: "+
"length of data must be %d, length received was %d",
ArrIDLen, len(data))
}
newID := new(ID)
copy(newID[:], data)
return newID, nil
}
// Bytes returns a copy of an ID as a byte slice. Note that Bytes() is used by
// Marshal() and any changes made here will affect how Marshal() functions.
func (i *ID) Bytes() []byte {
if i == nil {
jww.FATAL.Panicf("cannot get bytes of a nil ID")
}
newBytes := make([]byte, ArrIDLen)
copy(newBytes, i[:])
return newBytes
}
// Cmp determines whether two IDs are the same. Returns true if they are equal
// and false otherwise.
func (i *ID) Cmp(y *ID) bool {
if i == nil || y == nil {
jww.FATAL.Panicf("cannot compare nil IDs")
}
return *i == *y
}
// DeepCopy creates a new copy of an ID.
func (i *ID) DeepCopy() *ID {
if i == nil {
jww.FATAL.Panicf("cannot deep copy a nil ID")
}
newID := new(ID)
copy(newID[:], i[:])
return newID
}
// String converts an ID to a string via base64 encoding.
func (i *ID) String() string {
return base64.StdEncoding.EncodeToString(i.Bytes())
}
// GetType returns the ID's type. It is the last byte of the array.
func (i *ID) GetType() Type {
if i == nil {
jww.FATAL.Panicf("cannot get the type of a nil ID")
}
return Type(i[ArrIDLen-1])
}
// SetType changes the ID type by setting the last byte to the specified type.
func (i *ID) SetType(idType Type) {
if i == nil {
jww.FATAL.Panicf("cannot set the type of a nil ID")
}
i[ArrIDLen-1] = byte(idType)
}
// NewIdFromBytes creates a new ID from a copy of the data. It is similar to
// Unmarshal() but does not do any error checking. If the data is longer than
// ArrIDLen, then it is truncated. If it is shorter, then the remaining bytes
// are filled with zeroes. This function is for testing purposes only.
func NewIdFromBytes(data []byte, x interface{}) *ID {
// Ensure that this function is only run in testing environments
switch x.(type) {
case *testing.T, *testing.M:
break
default:
panic("NewIdFromBytes() can only be used for testing.")
}
newID := new(ID)
copy(newID[:], data[:])
return newID
}
// NewIdFromString creates a new ID from the given string and type. If the
// string is longer than dataLen, then it is truncated. If it is shorter, then
// the remaining bytes are filled with zeroes. This function is for testing
// purposes only.
func NewIdFromString(idString string, idType Type, x interface{}) *ID {
// Ensure that this function is only run in testing environments
switch x.(type) {
case *testing.T, *testing.M:
break
default:
panic("NewIdFromString() can only be used for testing.")
}
// Convert the string to bytes and create new ID from it
newID := NewIdFromBytes([]byte(idString), x)
// Set the ID type
newID.SetType(idType)
return newID
}
// NewIdFromUInt converts the specified uint64 into bytes and returns a new ID
// based off it with the specified ID type. The remaining space of the array is
// filled with zeros. This function is for testing purposes only.
func NewIdFromUInt(idUInt uint64, idType Type, x interface{}) *ID {
// Ensure that this function is only run in testing environments
switch x.(type) {
case *testing.T, *testing.M:
break
default:
panic("NewIdFromUInt() can only be used for testing.")
}
// Create the new ID
newID := new(ID)
// Convert the uints to bytes
binary.BigEndian.PutUint64(newID[:], idUInt)
// Set the ID's type
newID.SetType(idType)
return newID
}
// NewIdFromUInt converts the specified uint64 array into bytes and returns a
// new ID based off it with the specified ID type. Unlike NewIdFromUInt(), the
// four uint64s provided fill the entire ID array. This function is for testing
// purposes only.
func NewIdFromUInts(idUInts [4]uint64, idType Type, x interface{}) *ID {
// Ensure that this function is only run in testing environments
switch x.(type) {
case *testing.T, *testing.M:
break
default:
panic("NewIdFromUInt() can only be used for testing.")
}
// Create the new ID
newID := new(ID)
// Convert the uints to bytes
for i, idUint := range idUInts {
binary.BigEndian.PutUint64(newID[i*8:], idUint)
}
// Set the ID's type
newID.SetType(idType)
return newID
}
////////////////////////////////////////////////////////////////////////////////////////////
// Copyright © 2020 xx network SEZC //
// //
// Use of this source code is governed by a license that can be found in the LICENSE file //
////////////////////////////////////////////////////////////////////////////////////////////
package id
import "github.com/pkg/errors"
// idList.go handles operations that create a list of id.ID objects.
// NewIDListFromBytes creates a list of IDs from a list of byte slices. On
// success, it returns a new list. On failure, it returns a nil list and an
// error.
func NewIDListFromBytes(topology [][]byte) ([]*ID, error) {
list := make([]*ID, len(topology))
for index, id := range topology {
newId, err := Unmarshal(id)
if err != nil {
return nil, errors.Errorf("Unable to marshal ID for index %d: %+v",
index, err)
}
list[index] = newId
}
return list, nil
}
////////////////////////////////////////////////////////////////////////////////////////////
// Copyright © 2020 xx network SEZC //
// //
// Use of this source code is governed by a license that can be found in the LICENSE file //
////////////////////////////////////////////////////////////////////////////////////////////
package id
import (
"bytes"
"math/rand"
"testing"
)
func TestNewIDListFromBytes(t *testing.T) {
// Topology list will contain the list of strings to be passed to
// NewIDListFromBytes()
var topologyList [][]byte
// ExpectedNodes will contain the constructed IDs, to be compared one-by-one
// to the output of NewIDListFromBytes
var expectedIDs []*ID
// Construct a topology list
for i := 0; i < 10; i++ {
// Construct an ID
idBytes := newRandomBytes(ArrIDLen, t)
expectedId := NewIdFromBytes(idBytes, t)
// Append to the slices
expectedIDs = append(expectedIDs, expectedId)
topologyList = append(topologyList, expectedId.Bytes())
}
// Pass topologyList into NewIDListFromBytes()
receivedIDs, err := NewIDListFromBytes(topologyList)
if err != nil {
t.Errorf("Failed to create ID list: %+v", err)
}
// Iterate through the list and comparing receivedIDs to expectedIDs every
// iteration
for index, receivedID := range receivedIDs {
expectedNode := expectedIDs[index]
// Check the outputted list to the expected values
if !bytes.Equal(receivedID.Bytes(), expectedNode.Bytes()) {
t.Errorf("ID of index %d was not converted correctly."+
"\n\treceived: %v\n\texpected: %v", index, receivedID.Bytes(),
expectedNode.Bytes())
}
}
}
// Error path: construct a list with a bad topology
func TestNewIDListFromBytes_Error(t *testing.T) {
// Topology list will contain the list of strings to be passed to
// NewIDListFromBytes
var topologyList [][]byte
// ExpectedNodes will contain the constructed nodes, to be compared
// one-by-one to the output of NewIDListFromBytes
var expectedNodes []*ID
// Construct a topology list
for i := 0; i < 10; i++ {
// construct id
idBytes := newRandomBytes(ArrIDLen, t)
expectedId := NewIdFromBytes(idBytes, t)
expectedNodes = append(expectedNodes, expectedId)
// Inject a bad byte slices into the list of slices at an arbitrary point
if i == rand.Int()%5 {
expectedIdBytes := []byte{1, 2, 3}
topologyList = append(topologyList, expectedIdBytes)
}
}
// Attempt to convert the topologyList
_, err := NewIDListFromBytes(topologyList)
if err != nil {
return
}
t.Errorf("Expected error case, should not successfully create a list" +
"of nodes due to a bad topology")
}
////////////////////////////////////////////////////////////////////////////////////////////
// Copyright © 2020 xx network SEZC //
// //
// Use of this source code is governed by a license that can be found in the LICENSE file //
////////////////////////////////////////////////////////////////////////////////////////////
package id
import (
"bytes"
"encoding/base64"
"encoding/binary"
"fmt"
"math/rand"
"reflect"
"testing"
)
// Tests that Bytes() returns the correct byte slice of an ID and that it is
// a copy.
func TestID_Bytes(t *testing.T) {
// Test values
expectedBytes := newRandomBytes(ArrIDLen, t)
testID := NewIdFromBytes(expectedBytes, t)
// Check for the correct values
testVal := testID.Bytes()
if !bytes.Equal(expectedBytes, testVal) {
t.Errorf("Bytes() returned the incorrect byte slice of the ID"+
"\n\texpected: %+v\n\treceived: %+v", expectedBytes, testVal)
}
// Test if the returned bytes are copies
if &testID[0] == &testVal[0] {
t.Errorf("Bytes() did not return a copy when it should have."+
"\n\texpected: any value except %+v\n\treceived: %+v",
&testID[0], &testVal[0])
}
}
// Tests that Cmp() returns the correct value when comparing equal and unequal
// IDs.
func TestID_Cmp(t *testing.T) {
// Test values
randomBytes1 := newRandomBytes(ArrIDLen, t)
randomBytes2 := newRandomBytes(ArrIDLen, t)
testID1 := NewIdFromBytes(randomBytes1, t)
testID2 := NewIdFromBytes(randomBytes1, t)
testID3 := NewIdFromBytes(randomBytes2, t)
// Compare two equal IDs
testVal := testID1.Cmp(testID2)
if !testVal {
t.Errorf("Cmp() incorrectly determined the two IDs are not equal."+
"\n\texpected: %+v\n\treceived: %+v", true, testVal)
}
// Compare two unequal IDs
testVal = testID1.Cmp(testID3)
if testVal {
t.Errorf("Cmp() incorrectly determined the two IDs are equal."+
"\n\texpected: %+v\n\treceived: %+v", false, testVal)
}
}
// Test that DeepCopy() returns a copy with the same contents as the original
// and where the pointers are different.
func TestID_DeepCopy(t *testing.T) {
// Test values
expectedID := NewIdFromBytes(newRandomBytes(ArrIDLen, t), t)
// Test if the contents are equal
testVal := expectedID.DeepCopy()
if !reflect.DeepEqual(expectedID, testVal) {
t.Errorf("DeepCopy() returned a copy with the wrong contents."+
"\n\texpected: %+v\n\treceived: %+v", expectedID, testVal)
}
// Test if the returned bytes are copies
if &expectedID[0] == &testVal[0] {
t.Errorf("DeepCopy() did not return a copy when it should have."+
"\n\texpected: any value except %+v\n\treceived: %+v",
&expectedID[0], &testVal[0])
}
}
// Tests that the base64 encoded string returned by String() can be decoded into
// the original ID.
func TestID_String(t *testing.T) {
// Expected values
expectedBytes := newRandomBytes(ArrIDLen, t)
expectedID := NewIdFromBytes(expectedBytes, t)
// Encode into string
stringID := expectedID.String()
// Decode the string and check
newID, err := base64.StdEncoding.DecodeString(stringID)
if err != nil {
t.Fatalf("Failed to decode string returned by String():\n%v", err)
}
if !bytes.Equal(expectedBytes, newID) {
t.Errorf("String() did not encode the string correctly."+
"The decoded strings differ.\n\texpected: %v\n\treceived: %v",
expectedBytes, newID)
}
}
// Tests that GetType() returns the correct type for each ID type.
func TestID_GetType(t *testing.T) {
// Test values
testTypes := []Type{Generic, Gateway, Node, User, NumTypes, 6}
randomBytes := [][]byte{
newRandomBytes(ArrIDLen-1, t), newRandomBytes(ArrIDLen-1, t),
newRandomBytes(ArrIDLen-1, t), newRandomBytes(ArrIDLen-1, t),
newRandomBytes(ArrIDLen-1, t), newRandomBytes(ArrIDLen-1, t),
}
testIDs := []*ID{
NewIdFromBytes(append(randomBytes[0], byte(testTypes[0])), t),
NewIdFromBytes(append(randomBytes[1], byte(testTypes[1])), t),
NewIdFromBytes(append(randomBytes[2], byte(testTypes[2])), t),
NewIdFromBytes(append(randomBytes[3], byte(testTypes[3])), t),
NewIdFromBytes(append(randomBytes[4], byte(testTypes[4])), t),
NewIdFromBytes(append(randomBytes[5], byte(testTypes[5])), t),
}
for i, testID := range testIDs {
testVal := testID.GetType()
if testTypes[i] != testVal {
t.Errorf("GetType() returned the incorrect type."+
"\n\texpected: %v\n\treceived: %v", testTypes[i], testVal)
}
}
}
// Tests that SetType() sets the type of the ID correctly by checking if the
// ID's type changed after calling SetType().
func TestID_SetType(t *testing.T) {
// Test values
expectedType := Node
testType := Generic
testBytes := newRandomBytes(dataLen, t)
testID := NewIdFromBytes(append(testBytes, byte(testType)), t)
// Change the ID
testID.SetType(expectedType)
// Check the ID
testVal := testID.GetType()
if expectedType != testVal {
t.Errorf("SetType() did not set the ID type correctly."+
"\n\texpected: %v\n\treceived: %v", expectedType, testVal)
}
}
// Tests that NewIdFromBytes() creates a new ID with the correct contents.
func TestNewIdFromBytes(t *testing.T) {
// Expected values
expectedBytes := newRandomBytes(ArrIDLen, t)
// Create the ID and check its contents
newID := NewIdFromBytes(expectedBytes, t)
if !bytes.Equal(expectedBytes, newID[:]) {
t.Errorf("NewIdFromBytes() produced an ID with the incorrect bytes."+
"\n\texpected: %v\n\treceived: %v", expectedBytes, newID[:])
}
}
// Tests that NewIdFromBytes() creates a new ID from bytes with a length smaller
// than 33. The resulting ID should have the bytes and the rest should be 0.
func TestNewIdFromBytes_Underflow(t *testing.T) {
// Expected values
expectedBytes := newRandomBytes(ArrIDLen/2, t)
expectedArr := [ArrIDLen]byte{}
copy(expectedArr[:], expectedBytes)
// Create the ID and check its contents
newID := NewIdFromBytes(expectedBytes, t)
if !bytes.Equal(expectedArr[:], newID[:]) {
t.Errorf("NewIdFromBytes() produced an ID with the incorrect bytes."+
"\n\texpected: %v\n\treceived: %v", expectedArr, newID[:])
}
}
// Tests that NewIdFromBytes() creates a new ID from bytes with a length larger
// than 33. The resulting ID should the original bytes truncated to 33 bytes.
func TestNewIdFromBytes_Overflow(t *testing.T) {
// Expected values
expectedBytes := newRandomBytes(ArrIDLen*2, t)
expectedArr := [ArrIDLen]byte{}
copy(expectedArr[:], expectedBytes)
// Create the ID and check its contents
newID := NewIdFromBytes(expectedBytes, t)
if !bytes.Equal(expectedArr[:], newID[:]) {
t.Errorf("NewIdFromBytes() produced an ID with the incorrect bytes."+
"\n\texpected: %v\n\treceived: %v", expectedArr, newID[:])
}
}
// Tests that NewIdFromBytes() panics when given a nil testing object.
func TestNewIdFromBytes_TestError(t *testing.T) {
defer func() {
if err := recover(); err == nil {
t.Errorf("NewIdFromBytes() did not panic when it received a " +
"nil testing object when it should have.")
}
}()
// Call function with nil testing object
_ = NewIdFromBytes(newRandomBytes(ArrIDLen, t), nil)
}
// Tests that NewIdFromString() creates a new ID from string correctly. The new
// ID is created from a string that is 32 bytes long so that no truncation or
// padding is required. The test checks that the original string is still
// present in the data.
func TestNewIdFromString(t *testing.T) {
// Test values
expectedIdString := "Test ID string of correct length"
expectedType := Generic
expectedID := new(ID)
copy(expectedID[:], append([]byte(expectedIdString), byte(expectedType)))
// Create the ID and check its contents
newID := NewIdFromString(expectedIdString, expectedType, t)
// Check if the new ID matches the expected ID
if !expectedID.Cmp(newID) {
t.Errorf("NewIdFromString() produced an ID with the incorrect data."+
"\n\texpected: %v\n\treceived: %v", expectedID[:], newID[:])
}
// Check if the original string is still in the first 32 bytes
newIdString := string(newID.Bytes()[:ArrIDLen-1])
if expectedIdString != newIdString {
t.Errorf("NewIdFromString() did not correctly convert the original "+
"string to bytes.\n\texpected string: %#v\n\treceived string: %#v"+
"\n\texpected bytes: %v\n\treceived bytes: %v",
expectedIdString, newIdString,
[]byte(expectedIdString), newID.Bytes()[:ArrIDLen-1])
}
}
// Tests that NewIdFromString() panics when given a nil testing object.
func TestNewIdFromString_TestError(t *testing.T) {
defer func() {
if err := recover(); err == nil {
t.Errorf("NewIdFromString() did not panic when it received a " +
"nil testing object when it should have.")
}
}()
// Call function with nil testing object
_ = NewIdFromString("test", Generic, nil)
}
// Tests that NewIdFromUInt() creates a new ID with the correct contents by
// converting the ID back into a uint and comparing it to the original.
func TestNewIdFromUInt(t *testing.T) {
// Expected values
expectedUint := rand.Uint64()
// Create the ID and check its contents
newID := NewIdFromUInt(expectedUint, Generic, t)
idUint := binary.BigEndian.Uint64(newID[:ArrIDLen-1])
if expectedUint != idUint {
t.Errorf("NewIdFromUInt() produced an ID from uint incorrectly."+
"\n\texpected: %v\n\treceived: %v", expectedUint, idUint)
}
}
// Tests that NewIdFromUInt() panics when given a nil testing object.
func TestNewIdFromUInt_TestError(t *testing.T) {
defer func() {
if err := recover(); err == nil {
t.Errorf("NewIdFromUInt() did not panic when it received a " +
"nil testing object when it should have.")
}
}()
// Call function with nil testing object
_ = NewIdFromUInt(rand.Uint64(), Generic, nil)
}
// Tests that NewIdFromUInts() creates a new ID with the correct contents by
// converting the ID back into a slice of uints and comparing it to the
// original.
func TestNewIdFromUInts(t *testing.T) {
// Expected values
expectedUints := [4]uint64{rand.Uint64(), rand.Uint64(),
rand.Uint64(), rand.Uint64()}
// Create the ID and check its contents
newID := NewIdFromUInts(expectedUints, Generic, t)
idUints := [4]uint64{}
for i := range idUints {
idUints[i] = binary.BigEndian.Uint64(newID[i*8 : (i+1)*8])
}
if !reflect.DeepEqual(expectedUints, idUints) {
t.Errorf("NewIdFromUInts() produced an ID from uints incorrectly."+
"\n\texpected: %#v\n\treceived: %#v", expectedUints, idUints)
}
}
// Tests that NewIdFromUInts() panics when given a nil testing object.
func TestNewIdFromUInts_TestError(t *testing.T) {
defer func() {
if err := recover(); err == nil {
t.Errorf("NewIdFromUInts() did not panic when it received a " +
"nil testing object when it should have.")
}
}()
// Call function with nil testing object
newUint64s := [4]uint64{rand.Uint64(), rand.Uint64(),
rand.Uint64(), rand.Uint64()}
_ = NewIdFromUInts(newUint64s, Generic, nil)
}
// Tests that Marshal() returns the correct byte slice of an ID and that it is a
// copy. This test ensures duplicates the test for Bytes() to ensure that
// Marshal() uses Bytes().
func TestID_Marshal(t *testing.T) {
// Test values
expectedBytes := newRandomBytes(ArrIDLen, t)
testID := NewIdFromBytes(expectedBytes, t)
// Check for the correct values
testVal := testID.Marshal()
if !bytes.Equal(expectedBytes, testVal) {
t.Errorf("Marshal() returned the incorrect byte slice of the ID"+
"\n\texpected: %+v\n\treceived: %+v", expectedBytes, testVal)
}
// Test if the returned bytes are copies
if &testID[0] == &testVal[0] {
t.Errorf("Marshal() did not return a copy when it should have."+
"\n\texpected: any value except %+v\n\treceived: %+v",
&testID[0], &testVal[0])
}
}
// Tests that Unmarshal() creates a new ID with the correct contents and does
// not return an error.
func TestUnmarshal(t *testing.T) {
// Test values
expectedBytes := newRandomBytes(ArrIDLen, t)
// Unmarshal the bytes into an ID
newID, err := Unmarshal(expectedBytes)
// Make sure no error occurred
if err != nil {
t.Errorf("Unmarshal() produced an unexpected error."+
"\n\texpected: %v\n\treceived: %v", nil, err)
}
// Make sure the ID contents are correct
if !bytes.Equal(expectedBytes, newID[:]) {
t.Errorf("Unmarshal() produced an ID with the incorrect bytes."+
"\n\texpected: %v\n\treceived: %v", expectedBytes, newID[:])
}
}
// Tests that Unmarshal() produces an error when the given data length is not
// equal to the length of an ID and that the ID returned is nil.
func TestUnmarshal_DataLengthError(t *testing.T) {
// Test values
expectedBytes := newRandomBytes(ArrIDLen+10, t)
expectedError := fmt.Errorf("could not marshal byte slice to ID: "+
"length of data must be %d, length received was %d",
ArrIDLen, len(expectedBytes))
// Unmarshal the bytes into an ID
newID, err := Unmarshal(expectedBytes)
// Make sure an error occurs
if err == nil {
t.Errorf("Unmarshal() did not product an expected error."+
"\n\texpected: %v\n\treceived: %v", expectedError, err)
}
// Make sure the returned ID is nil
if newID != nil {
t.Errorf("Unmarshal() produced a non-nil ID on error."+
"\n\texpected: %v\n\treceived: %v", nil, newID)
}
}
// Generates a byte slice of the specified length containing random numbers.
func newRandomBytes(length int, t *testing.T) []byte {
// Create new byte slice of the correct size
idBytes := make([]byte, length)
// Create random bytes
_, err := rand.Read(idBytes)
if err != nil {
t.Fatalf("Failed to generate random bytes: %v", err)
}
return idBytes
}
////////////////////////////////////////////////////////////////////////////////////////////
// Copyright © 2020 xx network SEZC //
// //
// Use of this source code is governed by a license that can be found in the LICENSE file //
////////////////////////////////////////////////////////////////////////////////////////////
// The package idf contains the structure of the ID File. It holds a generated
// ID and a salt (a 256-bit random number) in a JSON file. This file is used
// by different entities to save their ID and salt to file. The file path is
// usually stored and referenced from each entity's configuration files.
package idf
import (
"encoding/json"
"github.com/pkg/errors"
"gitlab.com/elixxir/primitives/id"
"gitlab.com/elixxir/primitives/utils"
)
// The length of the salt byte array
const saltLen = 32
// IdFile structure matches the JSON structure used to save IDs and salts. The
// ID and ID type are saved as strings to make the file easy to read; they are
// never used or processed.
type IdFile struct {
ID string `json:"id"`
Type string `json:"type"`
Salt [saltLen]byte `json:"salt"`
IdBytes [id.ArrIDLen]byte `json:"idBytes"`
}
// UnloadIDF reads the contents of the IDF at the given path and returns the
// salt and ID stored in it. It does so by unmarshalling the JSON in the file
// into an IdFile object. The ID bytes from the object are unmarshalled into an
// ID object and it is returned along with the salt.
//
// Errors are returned when there is a failure to read the IDF, unmarshall the
// JSON, or unmarshalling the ID.
func UnloadIDF(filePath string) ([]byte, *id.ID, error) {
// Read the contents from the file
jsonBytes, err := utils.ReadFile(filePath)
if err != nil {
return nil, nil, errors.Errorf("Could not read IDF file %s: %v",
filePath, err)
}
// Create empty IdFile and unmarshal the data into it
idf, err := newIdfFromJSON(jsonBytes)
if err != nil {
return nil, nil, err
}
// Unmarshal ID bytes into ID
newID, err := id.Unmarshal(idf.IdBytes[:])
return idf.Salt[:], newID, err
}
// Create empty IdFile, unmarshal the data into it, and return it.
func newIdfFromJSON(jsonBytes []byte) (*IdFile, error) {
// Create new and empty IdFile object
var idf *IdFile
// Unmarshal JSON bytes into IdFile
err := json.Unmarshal(jsonBytes, &idf)
if err != nil {
return nil, errors.Errorf("Failed to unmarshal IDF JSON: %v", err)
}
return idf, nil
}
// MarshalIdfToJSON creates an IdFile object with the provided values and
// marshals it into JSON bytes ready to be written to a file.
func LoadIDF(filePath string, salt []byte, genID *id.ID) error {
// Generate new IdFile object
idf, err := newIDF(salt, genID)
if err != nil {
return errors.Errorf("Failed to create new IDF: %v", err)
}
// Marshal the IDF into JSON bytes
idfJSON, err := json.Marshal(idf)
if err != nil {
return errors.Errorf("Failed to marshal the IDF: %v", err)
}
// Create new ID file
err = writeIDF(filePath, idfJSON)
return err
}
// newIDF creates a pointer to a new IdFile object using the given ID and salt.
// The salt and marshaled ID are copied into the IdFile and the type is set from
// the ID. An error is returned if the salt is of the incorrect length.
func newIDF(salt []byte, genID *id.ID) (*IdFile, error) {
// Check that the salt is of the correct length
if len(salt) != saltLen {
return nil, errors.Errorf("Salt length must be %d, length "+
"received was %d", saltLen, len(salt))
}
// Create the new, empty IDF
newIDF := &IdFile{}
// Copy salt byte slice into IDF salt array
copy(newIDF.Salt[:], salt)
// Copy marshaled ID byte slice into IDF ID array
copy(newIDF.IdBytes[:], genID.Marshal())
// Set the IDF type
newIDF.Type = genID.GetType().String()
// Set the ID string
newIDF.ID = genID.String()
return newIDF, nil
}
// writeIDF creates an ID file (IDF) at the given path with the given JSON data.
// Errors are returned if an error occurs making directories or files.
func writeIDF(filePath string, jsonData []byte) error {
// Create new ID file
err := utils.WriteFile(filePath, jsonData, utils.FilePerms, utils.DirPerms)
if err != nil {
return errors.Errorf("Failed to create IDF: %v", err)
}
return nil
}
////////////////////////////////////////////////////////////////////////////////////////////
// Copyright © 2020 xx network SEZC //
// //
// Use of this source code is governed by a license that can be found in the LICENSE file //
////////////////////////////////////////////////////////////////////////////////////////////
package idf
import (
"bytes"
"gitlab.com/elixxir/primitives/id"
"gitlab.com/elixxir/primitives/utils"
"os"
"reflect"
"testing"
)
// Random test values
var randomIdfJson = "{\"id\":\"Uv38ByGCZU8WP18PmmIdcpVmx00QA3xNe7sEB9HixkkC\"," +
"\"type\":\"node\",\"salt\":[133,90,216,104,29,13,134,209,233,30,0,22," +
"121,57,203,102,148,210,196,34,172,210,8,160,7,41,57,72,127,105,153,235]," +
"\"idBytes\":[82,253,252,7,33,130,101,79,22,63,95,15,154,98,29,114,149," +
"102,199,77,16,3,124,77,123,187,4,7,209,226,198,73,2]}"
var randomIDBytes = [id.ArrIDLen]byte{82, 253, 252, 7, 33, 130, 101, 79, 22,
63, 95, 15, 154, 98, 29, 114, 149, 102, 199, 77, 16, 3, 124, 77, 123, 187,
4, 7, 209, 226, 198, 73, 2}
var randomSaltBytes = [saltLen]byte{133, 90, 216, 104, 29, 13, 134, 209, 233,
30, 0, 22, 121, 57, 203, 102, 148, 210, 196, 34, 172, 210, 8, 160, 7, 41,
57, 72, 127, 105, 153, 235}
var randomType = "node"
var randomIDF = IdFile{
ID: "Uv38ByGCZU8WP18PmmIdcpVmx00QA3xNe7sEB9HixkkC",
Type: randomType,
IdBytes: randomIDBytes,
Salt: randomSaltBytes,
}
// Tests that newIdfFromJSON() creates the correct IdFile object form the given
// JSON bytes.
func TestNewIdfFromJSON(t *testing.T) {
// Expected values
expectedIDF := &randomIDF
testIdfJSON := []byte(randomIdfJson)
// Create the new IDF from the known JSON
newIDF, err := newIdfFromJSON(testIdfJSON)
// Check that no error occurred
if err != nil {
t.Fatalf("newIdfFromJSON() produced an unexpected error:\n%+v", err)
}
// Check that the new IDF matches the expected IDF
if !reflect.DeepEqual(expectedIDF, newIDF) {
t.Errorf("newIdfFromJSON() produced an incorrect IDF."+
"\n\texpected: %#v\n\treceived: %#v", expectedIDF, newIDF)
}
}
// Tests that newIdfFromJSON() creates the correct IdFile object form the given
// JSON bytes.
func TestNewIdfFromJSON_JsonError(t *testing.T) {
// Expected values
expectedError := "Failed to unmarshal IDF JSON: ..."
testIdfJSON := []byte("invalidJSON")
// Create the new IDF from the known JSON
newIDF, err := newIdfFromJSON(testIdfJSON)
// Check that the expected error occurred
if err == nil {
t.Fatalf("newIdfFromJSON() did not produce the expected error."+
"\n\texpected: %v\n\treceived: %v", expectedError, err)
}
// Check that the new IDF is nil
if newIDF != nil {
t.Errorf("newIdfFromJSON() returned a non-nil IDF on error."+
"\n\texpected: %#v\n\treceived: %#v", nil, newIDF)
}
}
// Tests that newIDF() creates the correct IdFile object.
func TestNewIDF(t *testing.T) {
// Expected values
expectedID := id.NewIdFromBytes(randomIDBytes[:], t)
expectedSalt := randomSaltBytes
expectedIDF := &randomIDF
// Create the new IDF from the expected values
newIDF, err := newIDF(expectedSalt[:], expectedID)
// Check that no error occurred
if err != nil {
t.Fatalf("newIDF() produced an unexpected error:\n%v", err)
}
// Check that the new IDF matches the expected IDF
if !reflect.DeepEqual(expectedIDF, newIDF) {
t.Errorf("newIDF() produced an incorrect IDF."+
"\n\texpected: %#v\n\treceived: %#v", expectedIDF, newIDF)
}
}
// Tests that newIDF() returns and error when the provided salt is of the
// incorrect length.
func TestNewIDF_SaltLengthError(t *testing.T) {
// Expected values
expectedID := id.NewIdFromBytes(randomIDBytes[:], t)
expectedSalt := []byte{1, 2, 3}
expectedError := "Salt length must be 32, length received was %d"
// Create the new IDF from the expected values
newIDF, err := newIDF(expectedSalt, expectedID)
// Check that the expected error occurred
if err == nil {
t.Fatalf("newIDF() did not produce the expected error."+
"\n\texpected: %v\n\treceived: %v", expectedError, err)
}
// Check that the new IDF is nil
if newIDF != nil {
t.Errorf("newIDF() returned a non-nil IDF on error."+
"\n\texpected: %#v\n\treceived: %#v", nil, newIDF)
}
}
// Tests that writeIDF() write the IDF data to file correctly by reading the
// file back and comparing the contents to the original JSON.
func TestWriteIDF(t *testing.T) {
// Expected values
expectedJSON := []byte(randomIdfJson)
filePath := "test_ID.json"
// Delete the test file at the end
defer func() {
err := os.RemoveAll(filePath)
if err != nil {
t.Fatalf("Error deleting test IDF file %#v:\n%v", filePath, err)
}
}()
// Write the IDF
err := writeIDF(filePath, expectedJSON)
// Check that no error occurred
if err != nil {
t.Fatalf("writeIDF() produced an unexpected error:\n%v", err)
}
// Read the contents from the file
jsonBytes, err := utils.ReadFile(filePath)
if err != nil {
t.Fatalf("Error reading test file %#v:\n%v", filePath, err)
}
// Check that the IDF file contents match the expected IDF bytes
if !bytes.Equal(expectedJSON, jsonBytes) {
t.Errorf("writeIDF() wrote the IDF incorrectly."+
"\n\texpected: %#v\n\treceived: %#v", expectedJSON, jsonBytes)
}
}
// Tests that writeIDF() returns an error and does not create a new file when
// provided a bad path.
func TestWriteIDF_BadPathError(t *testing.T) {
// Expected values
expectedJSON := []byte(randomIdfJson)
filePath := "~a/test_ID.json"
expectedError := "Failed to create IDF: ..."
// Delete the test file at the end
defer func() {
err := os.RemoveAll(filePath)
if err != nil {
t.Fatalf("Error deleting test IDF file %#v:\n%v", filePath, err)
}
}()
// Write the IDF
err := writeIDF(filePath, expectedJSON)
// Check that the expected error occurred
if err == nil {
t.Fatalf("writeIDF() did not produce the expected error."+
"\n\texpected: %v\n\treceived: %v", expectedError, err)
}
// Check that the IDF was not created
if utils.Exists(filePath) {
t.Errorf("writeIDF() created the test file %#v when given a bad path.",
filePath)
}
}
// Tests that UnloadIDF() returns the expected salt and IdBytes.
func TestUnloadIDF(t *testing.T) {
// Test values
filePath := "test_ID.json"
testJSON := []byte(randomIdfJson)
// Expected values
expectedSalt := randomSaltBytes[:]
expectedID := id.NewIdFromBytes(randomIDBytes[:], t)
// Create IDF at path
err := utils.WriteFile(filePath, testJSON, utils.FilePerms, utils.DirPerms)
if err != nil {
t.Fatalf("Error creating test IDF %#v:\n%v", filePath, err)
}
// Delete the test file at the end
defer func() {
err := os.RemoveAll(filePath)
if err != nil {
t.Fatalf("Error deleting test IDF file %#v:\n%v", filePath, err)
}
}()
// Load the IDF from file
newSalt, newID, err := UnloadIDF(filePath)
// Check that no error occurred
if err != nil {
t.Fatalf("UnloadIDF() produced an unexpected error:\n%v", err)
}
// Check if returned salt is correct
if !bytes.Equal(expectedSalt, newSalt) {
t.Errorf("UnloadIDF() returned incorrect salt."+
"\n\texpected: %v\n\treceived: %v", expectedSalt, newSalt)
}
// Check if returned IdBytes is correct
if !expectedID.Cmp(newID) {
t.Errorf("UnloadIDF() returned incorrect IdBytes."+
"\n\texpected: %v\n\treceived: %v",
expectedID.Bytes(), newID.Bytes())
}
}
// Tests that UnloadIDF() returns an error when provided an invalid path.
func TestUnloadIDF_FilePathError(t *testing.T) {
// Test values
filePath := "~a/test_ID.json"
expectedError := "Could not read IDF file " + filePath + ": ..."
// Load the IDF from file
newSalt, newID, err := UnloadIDF(filePath)
// Check that the expected error occurred
if err == nil {
t.Errorf("UnloadIDF() did not produce the expected error."+
"\n\texpected: %v\n\treceived: %v", expectedError, err)
}
// Check that the returned salt is nil
if newSalt != nil {
t.Errorf("UnloadIDF() returned non-nil salt on error."+
"\n\texpected: %v\n\treceived: %v", nil, newSalt)
}
// Check that the returned IdBytes is nil
if newID != nil {
t.Errorf("UnloadIDF() returned non-nil IdBytes on error."+
"\n\texpected: %v\n\treceived: %v", nil, newID)
}
}
// Tests that UnloadIDF() returns an error when the provided IDF contains
// invalid JSON.
func TestUnloadIDF_InvalidJsonError(t *testing.T) {
// Test values
filePath := "test_ID.json"
testJSON := []byte("invalidJSON")
// Expected values
expectedError := "Failed to unmarshal IDF JSON: ..."
// Create IDF at path
err := utils.WriteFile(filePath, testJSON, utils.FilePerms, utils.DirPerms)
if err != nil {
t.Fatalf("Error creating test IDF %#v:\n%v", filePath, err)
}
// Delete the test file at the end
defer func() {
err := os.RemoveAll(filePath)
if err != nil {
t.Fatalf("Error deleting test IDF file %#v:\n%v", filePath, err)
}
}()
// Load the IDF from file
newSalt, newID, err := UnloadIDF(filePath)
// Check that the expected error occurred
if err == nil {
t.Errorf("UnloadIDF() did not produce the expected error."+
"\n\texpected: %v\n\treceived: %v", expectedError, err)
}
// Check that the returned salt is nil
if newSalt != nil {
t.Errorf("UnloadIDF() returned non-nil salt on error."+
"\n\texpected: %v\n\treceived: %v", nil, newSalt)
}
// Check that the returned IdBytes is nil
if newID != nil {
t.Errorf("UnloadIDF() returned non-nil IdBytes on error."+
"\n\texpected: %v\n\treceived: %v", nil, newID)
}
}
// Tests that LoadIDF() writes the correct data to file.
func TestLoadIDF(t *testing.T) {
// Test values
filePath := "test_ID.json"
testSalt := randomSaltBytes[:]
testID := id.NewIdFromBytes(randomIDBytes[:], t)
// Expected values
expectedIdfJSON := []byte(randomIdfJson)
// Delete the test file at the end
defer func() {
err := os.RemoveAll(filePath)
if err != nil {
t.Fatalf("Error deleting test IDF file %#v:\n%v", filePath, err)
}
}()
// Load IDF into a file
err := LoadIDF(filePath, testSalt, testID)
// Check that no error occurred
if err != nil {
t.Fatalf("LoadIDF() produced an unexpected error:\n%v", err)
}
// Get NDF contents
testIdfJSON, err := utils.ReadFile(filePath)
if err != nil {
t.Fatalf("Error reading test IDF file %#v:\n%v", filePath, err)
}
// Check if returned IDF JSON is correct
if !bytes.Equal(expectedIdfJSON, testIdfJSON) {
t.Errorf("LoadIDF() created incorrect IDF."+
"\n\texpected: %v\n\treceived: %v", expectedIdfJSON, testIdfJSON)
}
}
// Tests that LoadIDF() returns an error when given a salt with incorrect
// length.
func TestLoadIDF_IdfError(t *testing.T) {
// Test values
filePath := "test_ID.json"
testSalt := []byte{1, 2, 3}
testID := id.NewIdFromBytes(randomIDBytes[:], t)
// Expected values
expectedError := "Failed to create new IDF: ..."
// Delete the test file at the end
defer func() {
err := os.RemoveAll(filePath)
if err != nil {
t.Fatalf("Error deleting test IDF file %#v:\n%v", filePath, err)
}
}()
// Load IDF into a file
err := LoadIDF(filePath, testSalt, testID)
// Check that the expected error occurred
if err == nil {
t.Errorf("LoadIDF() did not produce the expected error."+
"\n\texpected: %v\n\treceived: %v", expectedError, err)
}
// Check that the IDF was not created
if utils.Exists(filePath) {
t.Errorf("LoadIDF() created the test file %#v when given a bad path.",
filePath)
}
}
func TestIDF_LoadUnload(t *testing.T) {
// Test values
filePath := "test_ID.json"
// Expected values
expectedSalt := randomSaltBytes[:]
expectedID := id.NewIdFromBytes(randomIDBytes[:], t)
// Delete the test file at the end
defer func() {
err := os.RemoveAll(filePath)
if err != nil {
t.Fatalf("Error deleting test IDF file %#v:\n%v", filePath, err)
}
}()
// Load IDF into a file
err := LoadIDF(filePath, expectedSalt, expectedID)
// Check that no error occurred
if err != nil {
t.Fatalf("LoadIDF() produced an unexpected error:\n%v", err)
}
// Unload the IDF
newSalt, newID, err := UnloadIDF(filePath)
// Check that no error occurred
if err != nil {
t.Fatalf("UnloadIDF() produced an unexpected error:\n%v", err)
}
// Check if returned salt is correct
if !bytes.Equal(expectedSalt, newSalt) {
t.Errorf("UnloadIDF() returned incorrect salt."+
"\n\texpected: %v\n\treceived: %v", expectedSalt, newSalt)
}
// Check if returned IdBytes is correct
if !expectedID.Cmp(newID) {
t.Errorf("UnloadIDF() returned incorrect IdBytes."+
"\n\texpected: %v\n\treceived: %v",
expectedID.Bytes(), newID.Bytes())
}
}
////////////////////////////////////////////////////////////////////////////////////////////
// Copyright © 2020 xx network SEZC //
// //
// Use of this source code is governed by a license that can be found in the LICENSE file //
////////////////////////////////////////////////////////////////////////////////////////////
package id
type Round uint64
////////////////////////////////////////////////////////////////////////////////////////////
// Copyright © 2020 xx network SEZC //
// //
// Use of this source code is governed by a license that can be found in the LICENSE file //
////////////////////////////////////////////////////////////////////////////////////////////
package id
import (
"fmt"
"strconv"
)
// Type holds the numerical representation of the ID type.
type Type byte
// List of ID types
const (
Generic = Type(iota)
Gateway
Node
User
NumTypes // Gives number of ID types
)
// String is a stringer to get the name of the ID type.
func (t Type) String() string {
switch t {
case Generic:
return "generic"
case Gateway:
return "gateway"
case Node:
return "node"
case User:
return "user"
case NumTypes:
return strconv.Itoa(int(NumTypes))
default:
return fmt.Sprintf("UNKNOWN ID TYPE: %d", t)
}
}
////////////////////////////////////////////////////////////////////////////////////////////
// Copyright © 2020 xx network SEZC //
// //
// Use of this source code is governed by a license that can be found in the LICENSE file //
////////////////////////////////////////////////////////////////////////////////////////////
package id
import (
"testing"
)
// Tests that String() returns the correct string for each Type.
func TestType_String(t *testing.T) {
// Expected values
expectedGeneric := "generic"
expectedGateway := "gateway"
expectedNode := "node"
expectedUser := "user"
expectedNumTypes := "4"
// Test Generic stringer
testVal := Generic.String()
if expectedGeneric != testVal {
t.Errorf("String() returned incorrect string for Generic type."+
"\n\texpected: %s\n\treceived: %s", expectedGeneric, testVal)
}
// Test Gateway stringer
testVal = Gateway.String()
if expectedGateway != testVal {
t.Errorf("String() returned incorrect string for Gateway type."+
"\n\texpected: %s\n\treceived: %s", expectedGateway, testVal)
}
// Test Node stringer
testVal = Node.String()
if expectedNode != testVal {
t.Errorf("String() returned incorrect string for Node type."+
"\n\texpected: %s\n\treceived: %s", expectedNode, testVal)
}
// Test User stringer
testVal = User.String()
if expectedUser != testVal {
t.Errorf("String() returned incorrect string for User type."+
"\n\texpected: %s\n\treceived: %s", expectedUser, testVal)
}
// Test NumTypes stringer
testVal = NumTypes.String()
if expectedNumTypes != testVal {
t.Errorf("String() returned incorrect string for NumTypes type."+
"\n\texpected: %s\n\treceived: %s", expectedNumTypes, testVal)
}
}
// Tests that String() returns an error when given a Type that has not been
// defined.
func TestType_String_Error(t *testing.T) {
// Expected/test values
expectedError := "UNKNOWN ID TYPE: 5"
testType := Type(5)
// Test stringer error
testVal := testType.String()
if expectedError != testVal {
t.Errorf("String() did not return an error when it should have."+
"\n\texpected: %s\n\treceived: %s", expectedError, testVal)
}
}
////////////////////////////////////////////////////////////////////////////////////////////
// Copyright © 2020 xx network SEZC //
// //
// Use of this source code is governed by a license that can be found in the LICENSE file //
////////////////////////////////////////////////////////////////////////////////////////////
// Package ndf contains the structure for our network definition file. This object is used by
// various users, including our cMix nodes and clients of the xx Messenger, among others.
// It also includes functions to unmarshal an NDF from a JSON file, separate the signature
// from the actual NDF content, and serialize the NDF structure into a byte slice
package ndf
import (
"encoding/base64"
"encoding/json"
"github.com/pkg/errors"
jww "github.com/spf13/jwalterweatherman"
"gitlab.com/xx_network/primitives/id"
"strings"
"time"
)
// This constant string is to be used by our users that request NDFs from permissioning.
// Those that request include cMix nodes, gateways, notification bot and clients.
// Permissioning builds and provides the ndf to those that request it. However, depending on
// the status of the cMix network, it might not have the the ndf ready upon request.
// Permissioning in this case tells the requester that it is not ready with an error message.
// The requester checks if the error message contains this string, and thus knows it needs to ask
// again.
const NO_NDF = "Permissioning server does not have an ndf to give"
// NetworkDefinition structure matches the JSON structure generated in
// Terraform, which allows it to be decoded to Go.
type NetworkDefinition struct {
Timestamp time.Time
Gateways []Gateway
Nodes []Node
Registration Registration
Notification Notification
UDB UDB `json:"Udb"`
E2E Group `json:"E2e"`
CMIX Group `json:"Cmix"`
}
// Gateway is the structure for the gateways object in the JSON file.
type Gateway struct {
ID []byte `json:"Id"`
Address string
TlsCertificate string `json:"Tls_certificate"`
}
// Node is the structure for the nodes object in the JSON file.
type Node struct {
ID []byte `json:"Id"`
Address string
TlsCertificate string `json:"Tls_certificate"`
}
// Registration is the structure for the registration object in the JSON file.
type Registration struct {
Address string
TlsCertificate string `json:"Tls_certificate"`
}
// Notifications is the structure for the registration object in the JSON file.
type Notification struct {
Address string
TlsCertificate string `json:"Tls_certificate"`
}
// UDB is the structure for the UDB object in the JSON file.
type UDB struct {
ID []byte `json:"Id"`
}
// Group is the structure for a group in the JSON file; it is used for the E2E
// and CMIX objects.
type Group struct {
Prime string
SmallPrime string `json:"Small_prime"`
Generator string
}
func (g *Group) String() (string, error) {
data, err := json.Marshal(g)
if err != nil {
return "", errors.Errorf("Unable to marshal group: %+v", err)
}
return string(data), nil
}
// DecodeNDF decodes the given JSON string into the NetworkDefinition structure
// and decodes the base 64 signature to a byte slice. The NDF string is expected
// to have the JSON data on line 1 and its signature on line 2. Returns an error
// if separating the lines fails or if the JSON unmarshal fails.
func DecodeNDF(ndf string) (*NetworkDefinition, []byte, error) {
// Get JSON data and check if the separating failed
jsonData, signature := separate(ndf)
// Decode the signature form base 64 and check for errors
signatureBytes, err := base64.StdEncoding.DecodeString(signature)
if err != nil {
return nil, nil, err
}
// Unmarshal the JSON string into a structure
networkDefinition := &NetworkDefinition{}
err = json.Unmarshal([]byte(jsonData), networkDefinition)
if err != nil {
return nil, nil, err
}
return networkDefinition, signatureBytes, nil
}
// Returns a stripped down copy of the NDF object to be used by Clients
func (ndf *NetworkDefinition) StripNdf() *NetworkDefinition {
// Strip down nodes slice of addresses and certs
var strippedNodes []Node
for _, node := range ndf.Nodes {
newNode := Node{
ID: node.ID,
}
strippedNodes = append(strippedNodes, newNode)
}
// Create a new Ndf with the stripped information
return &NetworkDefinition{
Timestamp: ndf.Timestamp,
Gateways: ndf.Gateways,
Nodes: strippedNodes,
Registration: ndf.Registration,
Notification: ndf.Notification,
UDB: ndf.UDB,
E2E: ndf.E2E,
CMIX: ndf.CMIX,
}
}
// separate splits the JSON data from the signature. The NDF string is expected
// to have the JSON data starting on line 1 and its signature on the last line.
// Returns JSON data and signature as separate strings. If the signature is not
// present, it is returned as an empty string.
func separate(ndf string) (string, string) {
var jsonLineEnd int
var signature string
lines := strings.Split(ndf, "\n")
// Determine which line the JSON ends and which line the signature is on
for i := len(lines) - 1; i >= 0; i-- {
line := strings.TrimSpace(lines[i])
if line != "" {
if strings.HasSuffix(line, "}") {
jsonLineEnd = i
break
} else {
signature = line
}
}
}
return strings.Join(lines[0:jsonLineEnd+1], "\n"), signature
}
// Serialize converts the NetworkDefinition into a byte slice.
func (ndf *NetworkDefinition) Serialize() []byte {
b := make([]byte, 0)
// Convert timestamp to a byte slice
timeBytes, err := ndf.Timestamp.MarshalBinary()
if err != nil {
jww.FATAL.Panicf("Failed to marshal NetworkDefinition timestamp: %v", err)
}
b = append(b, timeBytes...)
// Convert Gateways slice to byte slice
for _, val := range ndf.Gateways {
b = append(b, val.ID...)
b = append(b, []byte(val.Address)...)
b = append(b, []byte(val.TlsCertificate)...)
}
// Convert Nodes slice to byte slice
for _, val := range ndf.Nodes {
b = append(b, val.ID...)
b = append(b, []byte(val.Address)...)
b = append(b, []byte(val.TlsCertificate)...)
}
// Convert Registration to byte slice
b = append(b, []byte(ndf.Registration.Address)...)
b = append(b, []byte(ndf.Registration.TlsCertificate)...)
// Convert UDB to byte slice
b = append(b, ndf.UDB.ID...)
// Convert E2E to byte slice
b = append(b, []byte(ndf.E2E.Prime)...)
b = append(b, []byte(ndf.E2E.Generator)...)
b = append(b, []byte(ndf.E2E.SmallPrime)...)
// Convert CMIX to byte slice
b = append(b, []byte(ndf.CMIX.Prime)...)
b = append(b, []byte(ndf.CMIX.Generator)...)
b = append(b, []byte(ndf.CMIX.SmallPrime)...)
return b
}
// Marshal returns a json marshal of the ndf
func (ndf *NetworkDefinition) Marshal() ([]byte, error) {
ndfBytes, err := json.Marshal(ndf)
if err != nil {
return nil, err
}
return ndfBytes, nil
}
// GetNodeId marshals the node id into the ID type. Returns an error if Marshal
// fails.
func (n *Node) GetNodeId() (*id.ID, error) {
newID, err := id.Unmarshal(n.ID)
if err != nil {
return nil, err
}
return newID, nil
}
// GetGatewayId formats the gateway id into the id format specified in the id package of this repo
func (n *Node) GetGatewayId() (*id.ID, error) {
newID, err := id.Unmarshal(n.ID)
if err != nil {
return nil, err
}
return newID, nil
}
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment