// 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, parametersCreationTime, _, 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, parametersCreationTime, 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, _, _, existingParametersCreationTime, err := GetAuthorizedParameters(parametersType, parametersNetworkType) if (err != nil) { return false, false, err } if (parametersFound == true){ if (existingParametersCreationTime >= parametersCreationTime){ // 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 creation 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, parametersCreationTime, 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, parametersCreationTime, 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, parametersCreationTime, nil } //Outputs: // -bool: Admin permissions found // -bool: Parameters are authorized // -error func verifyParametersAreAuthorized(parametersType string, networkType byte, parametersCreationTime 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") } minimumCreationTime, exists := adminPermissionsMap[parametersType + "_MinimumCreationTime"] if (exists == false) { return false, false, errors.New("Parameters storage is corrupt: AdminPermissions missing minimumCreationTime but contains AuthorizedAdmins for parameterType.") } minimumCreationTimeInt64, err := helpers.ConvertStringToInt64(minimumCreationTime) if (err != nil) { return false, false, errors.New("Parameters storage is corrupt: AdminPermissions contains invalid minimumCreationTime: " + minimumCreationTime) } if (parametersCreationTime < minimumCreationTimeInt64){ 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 }