seekia/internal/parameters/parametersStorage/parametersStorage.go

267 lines
10 KiB
Go

// parametersStorage provides functions to store and retrieve network parameters
package parametersStorage
//TODO: Create a job to prune old parameters
import "seekia/internal/parameters/readParameters"
import "seekia/internal/helpers"
import "seekia/internal/localFilesystem"
import "seekia/internal/encoding"
import "bytes"
import goFilepath "path/filepath"
import "strings"
import "errors"
import "slices"
//TODO: The key of the master admin who can change the admin permissions
const masterAdminPublicKey string = "TODO"
//Outputs:
// -bool: Parameters to add are well formed
// -bool: Admin permissions found (we cannot add parameters without these)
// -error
func AddParameters(inputParameters []byte)(bool, bool, error){
ableToRead, _, parametersNetworkType, listOfAdmins, parametersType, parametersBroadcastTime, _, err := readParameters.ReadParameters(true, inputParameters)
if (err != nil) { return false, false, err }
if (ableToRead == false){
return false, false, nil
}
adminParametersFound, parametersAreAuthorized, err := verifyParametersAreAuthorized(parametersType, parametersNetworkType, parametersBroadcastTime, listOfAdmins)
if (err != nil) { return false, false, err }
if (adminParametersFound == false){
// We do not have admin parameters. We cannot determine if parameters to add are authorized
return false, false, nil
}
if (parametersAreAuthorized == false){
// Parameters are not authorized.
// Could be malicious host who sent parameters, or admin permissions were updated and we/they do not have them yet
return true, true, nil
}
// We check if parameters already exist that are newer
parametersFound, _, _, existingParametersBroadcastTime, err := GetAuthorizedParameters(parametersType, parametersNetworkType)
if (err != nil) { return false, false, err }
if (parametersFound == true){
if (existingParametersBroadcastTime >= parametersBroadcastTime){
// These parameters are older than our existing stored authorized parameters.
// We will not add them.
return true, true, nil
}
}
seekiaDirectory, err := localFilesystem.GetSeekiaDataFolderPath()
if (err != nil) { return false, false, err }
networkTypeString := helpers.ConvertByteToString(parametersNetworkType)
networkTypeFoldername := "Network" + networkTypeString
parametersFolderpath := goFilepath.Join(seekiaDirectory, "Parameters", networkTypeFoldername)
parametersFilename := parametersType + ".messagepack"
err = localFilesystem.CreateOrOverwriteFile(inputParameters, parametersFolderpath, parametersFilename)
if (err != nil) { return false, false, err }
return true, true, nil
}
// This function will return parameters that are signed by known admins
//Outputs:
// -bool: Requested parameters found and signed by valid admins
// -[]byte: Parameters bytes
// -map[string]string: Parameters map
// -int64: Parameters broadcast time
// -error
func GetAuthorizedParameters(parametersType string, networkType byte)(bool, []byte, map[string]string, int64, error){
isValid := readParameters.VerifyParametersType(parametersType)
if (isValid == false) {
return false, nil, nil, 0, errors.New("GetAuthorizedParameters called with invalid parametersType: " + parametersType)
}
isValid = helpers.VerifyNetworkType(networkType)
if (isValid == false){
networkTypeString := helpers.ConvertByteToString(networkType)
return false, nil, nil, 0, errors.New("GetAuthorizedParameters called with invalid networkType: " + networkTypeString)
}
parametersExist, storedParametersBytes, err := getStoredParameters(parametersType, networkType)
if (err != nil) { return false, nil, nil, 0, err }
if (parametersExist == false){
return false, nil, nil, 0, nil
}
ableToRead, _, parametersNetworkType, listOfSigners, currentParametersType, parametersBroadcastTime, parametersMap, err := readParameters.ReadParameters(true, storedParametersBytes)
if (err != nil) { return false, nil, nil, 0, err }
if (ableToRead == false) {
return false, nil, nil, 0, errors.New("Stored parameters are corrupt: storing corrupt " + parametersType + "parameter.")
}
if (parametersNetworkType != networkType){
return false, nil, nil, 0, errors.New("getStoredParameters returning different networkType parameters")
}
if (currentParametersType != parametersType){
return false, nil, nil, 0, errors.New("getStoredParameters returning different parametersType parameters")
}
adminPermissionsFound, parametersAreAuthorized, err := verifyParametersAreAuthorized(parametersType, networkType, parametersBroadcastTime, listOfSigners)
if (err != nil) { return false, nil, nil, 0, err }
if (adminPermissionsFound == false || parametersAreAuthorized == false){
return false, nil, nil, 0, nil
}
return true, storedParametersBytes, parametersMap, parametersBroadcastTime, nil
}
//Outputs:
// -bool: Admin permissions found
// -bool: Parameters are authorized
// -error
func verifyParametersAreAuthorized(parametersType string, networkType byte, parametersBroadcastTime int64, parametersSigners [][32]byte)(bool, bool, error){
containsDuplicates, _ := helpers.CheckIfListContainsDuplicates(parametersSigners)
if (containsDuplicates == true){
return false, false, errors.New("verifyParametersAreAuthorized called with parametersSigners list containing duplicates.")
}
parametersExist, adminPermissionsParameters, err := getStoredParameters("AdminPermissions", networkType)
if (err != nil) { return false, false, err }
if (parametersExist == false){
return false, false, nil
}
ableToRead, _, parametersNetworkType, listOfAdmins, currentParametersType, _, adminPermissionsMap, err := readParameters.ReadParameters(true, adminPermissionsParameters)
if (err != nil) { return false, false, err }
if (ableToRead == false){
return false, false, errors.New("Parameters storage corrupt: Storing invalid AdminPermissions.")
}
if (parametersNetworkType != networkType){
return false, false, errors.New("getStoredParameters returning parameters of a different networkType")
}
if (currentParametersType != "AdminPermissions"){
return false, false, errors.New("getStoredParameters returning parameters of a different parametersType")
}
if (len(listOfAdmins) != 1){
return false, false, errors.New("Parameters storage is corrupt: storing AdminPermissions with more than 1 signer.")
}
masterAdminPublicKeyBytes, err := encoding.DecodeHexStringToBytes(masterAdminPublicKey)
if (err != nil){
return false, false, errors.New("Master admin public key is malformed: " + masterAdminPublicKey + ". Reason: " + err.Error())
}
areEqual := bytes.Equal(listOfAdmins[0][:], masterAdminPublicKeyBytes)
if (areEqual == false){
return false, false, errors.New("Parameters storage is corrupt: storing AdminPermissions with different admin key.")
}
authorizedAdminsListString, exists := adminPermissionsMap[parametersType + "_AuthorizedAdmins"]
if (exists == false) {
return false, false, errors.New("Stored AdminPermissions parameters missing " + parametersType + "_AuthorizedAdmins")
}
authorizedAdminsList := strings.Split(authorizedAdminsListString, "+")
if (len(authorizedAdminsList) == 0){
return false, false, errors.New("Stored AdminPermissions parameters contains empty authorizedAdmins list")
}
authorizedAdminKeysList := make([][32]byte, 0, len(authorizedAdminsList))
for _, adminPublicKeyHex := range authorizedAdminsList{
adminPublicKey, err := encoding.DecodeHexStringToBytes(adminPublicKeyHex)
if (err != nil){
return false, false, errors.New("Stored AdminPermissions parameters contains empty authorizedAdmins list: Key not hex.")
}
if (len(adminPublicKey) != 32){
return false, false, errors.New("Stored AdminPermissions parameters contains empty authorizedAdmins list: Key is invalid length.")
}
adminPublicKeyArray := [32]byte(adminPublicKey)
authorizedAdminKeysList = append(authorizedAdminKeysList, adminPublicKeyArray)
}
minimumSigners, exists := adminPermissionsMap[parametersType + "_MinimumSigners"]
if (exists == false) {
return false, false, errors.New("Parameters storage is corrupt: AdminPermissions missing MinimumSigners but contains AuthorizedAdmins")
}
minimumNumberOfSigners, err := helpers.ConvertStringToInt(minimumSigners)
if (err != nil) {
return false, false, errors.New("Parameters storage is corrupt: AdminPermissions contains invalid minimumSigners: " + minimumSigners)
}
if (len(authorizedAdminKeysList) < minimumNumberOfSigners){
return false, false, errors.New("Parameters storage is corrupt: AdminPermissions contains less authorizedAdmins than minimumSigners")
}
minimumBroadcastTime, exists := adminPermissionsMap[parametersType + "_MinimumBroadcastTime"]
if (exists == false) {
return false, false, errors.New("Parameters storage is corrupt: AdminPermissions missing minimumBroadcastTime but contains AuthorizedAdmins for parameterType.")
}
minimumBroadcastTimeInt64, err := helpers.ConvertStringToInt64(minimumBroadcastTime)
if (err != nil) {
return false, false, errors.New("Parameters storage is corrupt: AdminPermissions contains invalid minimumBroadcastTime: " + minimumBroadcastTime)
}
if (parametersBroadcastTime < minimumBroadcastTimeInt64){
return false, false, nil
}
verifiedAdminsCount := 0
for _, adminPublicKey := range parametersSigners{
isInList := slices.Contains(authorizedAdminKeysList, adminPublicKey)
if (isInList == true){
verifiedAdminsCount += 1
}
}
if (verifiedAdminsCount < minimumNumberOfSigners){
// This parameters file does not have enough authorized signers
// Either the provider of the parameters file is malicious, or the permissions file has updated
// and the provider/we have not received the newest file
return true, false, nil
}
return true, true, nil
}
//Outputs:
// -bool: Parameters exist
// -[]byte: Parameters bytes
// -error
func getStoredParameters(parametersType string, networkType byte)(bool, []byte, error){
seekiaDirectory, err := localFilesystem.GetSeekiaDataFolderPath()
if (err != nil) { return false, nil, err }
networkTypeString := helpers.ConvertByteToString(networkType)
networkTypeFoldername := "Network" + networkTypeString
parametersFilename := parametersType + ".messagepack"
parametersFilepath := goFilepath.Join(seekiaDirectory, "Parameters", networkTypeFoldername, parametersFilename)
exists, fileBytes, err := localFilesystem.GetFileContents(parametersFilepath)
if (err != nil) { return false, nil, err }
if (exists == false){
return false, nil, nil
}
return true, fileBytes, nil
}