void SetWindowIcon(photon_window &window, const std::string &filename){ if(PHYSFS_exists(filename.c_str())){ auto fp = PHYSFS_openRead(filename.c_str()); intmax_t length = PHYSFS_fileLength(fp); if(length > 0){ uint8_t *buffer = new uint8_t[length]; PHYSFS_read(fp, buffer, 1, length); PHYSFS_close(fp); SDL_RWops *rw = SDL_RWFromMem(buffer, length); SDL_Surface *icon = IMG_Load_RW(rw, 1); if(icon == nullptr){ PrintToLog("ERROR: icon loading failed! %s", IMG_GetError()); } SDL_SetWindowIcon(window.window_SDL, icon); SDL_FreeSurface(icon); delete[] buffer; }else{ PrintToLog("ERROR: Unable to open image file \"%s\"!"); } }else{ PrintToLog("ERROR: Image file \"%s\" does not exist!", filename.c_str()); } }
photon_window CreateSDLWindow(const photon_settings &settings){ PrintToLog("INFO: Initializing SDL."); photon_window window; SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) == -1) { PrintToLog("ERROR: Unable to init SDL! \"%s\"", SDL_GetError()); // TODO - error handling. abort(); } SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, 32); if(settings.multisamples > 0){ SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, settings.multisamples); } SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, settings.doublebuffer); window.window_SDL = SDL_CreateWindow("Photon", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, PHOTON_WINDOW_FLAGS); if (!window.window_SDL){ PrintToLog("ERROR: Unable to create window!"); // TODO - error handling. abort(); } window.context_SDL = SDL_GL_CreateContext(window.window_SDL); SDL_GL_SetSwapInterval(settings.vsync); SDL_ShowCursor(SDL_ENABLE); if(settings.fullscreen){ ToggleFullscreen(window); } SetWindowIcon(window, "/textures/gui/window_icon.png"); return window; }
/** * Updates the number of tokens for the given tally type. * * Negative balances are only permitted for pending balances. * * @param propertyId The identifier of the tally to update * @param amount The amount to add * @param ttype The tally type * @return True, if the update was successful */ bool CMPTally::updateMoney(uint32_t propertyId, int64_t amount, TallyType ttype) { if (TALLY_TYPE_COUNT <= ttype || amount == 0) { return false; } bool fUpdated = false; int64_t now64 = mp_token[propertyId].balance[ttype]; if (isOverflow(now64, amount)) { PrintToLog("%s(): ERROR: arithmetic overflow [%d + %d]\n", __func__, now64, amount); return false; } if (PENDING != ttype && (now64 + amount) < 0) { // NOTE: // Negative balances are only permitted for pending balances } else { now64 += amount; mp_token[propertyId].balance[ttype] = now64; fUpdated = true; } return fUpdated; }
/** Obtains a hash of the balances for a specific property. */ uint256 GetBalancesHash(const uint32_t hashPropertyId) { SHA256_CTX shaCtx; SHA256_Init(&shaCtx); LOCK(cs_tally); std::map<std::string, CMPTally> tallyMapSorted; for (std::unordered_map<string, CMPTally>::iterator uoit = mp_tally_map.begin(); uoit != mp_tally_map.end(); ++uoit) { tallyMapSorted.insert(std::make_pair(uoit->first,uoit->second)); } for (std::map<string, CMPTally>::iterator my_it = tallyMapSorted.begin(); my_it != tallyMapSorted.end(); ++my_it) { const std::string& address = my_it->first; CMPTally& tally = my_it->second; tally.init(); uint32_t propertyId = 0; while (0 != (propertyId = (tally.next()))) { if (propertyId != hashPropertyId) continue; std::string dataStr = GenerateConsensusString(tally, address, propertyId); if (dataStr.empty()) continue; if (msc_debug_consensus_hash) PrintToLog("Adding data to balances hash: %s\n", dataStr); SHA256_Update(&shaCtx, dataStr.c_str(), dataStr.length()); } } uint256 balancesHash; SHA256_Final((unsigned char*)&balancesHash, &shaCtx); return balancesHash; }
photon_input InitInput(){ PrintToLog("INFO: Initializing Input System."); photon_input input; SDL_GameControllerEventState(SDL_ENABLE); SDL_JoystickEventState(SDL_ENABLE); for(int i = 0; i < SDL_NumJoysticks(); i++){ if(SDL_IsGameController(i)){ input.open_controllers.push_back(SDL_GameControllerOpen(i)); }else{ input.open_joysticks.push_back(SDL_JoystickOpen(i)); } } PrintToLog("INFO: Listening for input, press a button on the device you want to use."); return input; }
void GarbageCollect(photon_window &window, bool quitSDL){ SDL_GL_DeleteContext(window.context_SDL); SDL_DestroyWindow(window.window_SDL); if(quitSDL){ SDL_Quit(); } PrintToLog("INFO: SDL garbage collection complete."); }
void ToggleFullscreen(photon_window &window){ window.fullscreen = !window.fullscreen; if(window.fullscreen){ SDL_DisplayMode mode; SDL_GetDesktopDisplayMode(0, &mode); SDL_SetWindowDisplayMode(window.window_SDL, &mode); } SDL_SetWindowFullscreen(window.window_SDL, SDL_bool(window.fullscreen)); PrintToLog("INFO: Window toggled fullscreen."); }
void GarbageCollect(photon_input &input){ for(auto controller : input.open_controllers){ if(controller == input.controller){ input.controller = nullptr; } SDL_GameControllerClose(controller); } for(auto joystick : input.open_joysticks){ if(joystick == input.joystick){ input.joystick = nullptr; } SDL_JoystickClose(joystick); } PrintToLog("INFO: Input System garbage collection complete."); }
void AdvanceFrame(photon_level &level, photon_player &player, float time){ level.beams.clear(); for(auto &block : level.grid){ blocks::OnFrame(glm::uvec2(block.first.first, block.first.second), level, time); } for(photon_laserbeam &beam : level.beams){ tracer::TraceBeam(beam, level, time); } level.time += time; lua::AdvanceFrame(); // if victory_state is 0 before checking but not after then defeat occured. (keeps it from repeating message over and over) if(level.victory_state == 0 && level::CheckVictory(level, player) < 0){ PrintToLog("INFO: DEFEAT! (game is unwinnable)"); } }
void DoEvents(photon_instance &instance){ SDL_Event event; while(SDL_PollEvent(&event)){ switch (event.type) { case SDL_TEXTINPUT: if(instance.gui.load_save_menu.loading || instance.gui.load_save_menu.saving){ instance.gui.load_save_menu.filename.insert(instance.gui.load_save_menu.cursor, event.text.text); for(char* c = event.text.text; *c != '\0'; c++){ instance.gui.load_save_menu.cursor++; } instance.gui.load_save_menu.current_file_index = -1; }else{ // if we don't need text input anymore... SDL_StopTextInput(); } break; case SDL_TEXTEDITING: if(instance.gui.load_save_menu.loading || instance.gui.load_save_menu.saving){ instance.gui.load_save_menu.filename.insert(event.edit.start, event.edit.text); }else{ // if we don't need text input anymore... SDL_StopTextInput(); } break; case SDL_KEYDOWN: if(!instance.input.is_valid){ input::LoadConfig("/config/keyboard.xml", instance.input); PrintToLog("INFO: Using keyboard input."); } if(event.key.keysym.sym == SDLK_f && event.key.keysym.mod & KMOD_CTRL){ window_managment::ToggleFullscreen(instance.window); }else if(event.key.keysym.sym == SDLK_i && event.key.keysym.mod & KMOD_CTRL){ // re-detect input. by doing a garbage collect and an init over again it will detect newly connected controllers. PrintToLog("INFO: Reinitilizing input system to redetect available devices..."); GarbageCollect(instance.input); instance.input = InitInput(); }else if((instance.gui.load_save_menu.loading || instance.gui.load_save_menu.saving)){ photon_gui_load_save_menu &load_save_menu = instance.gui.load_save_menu; if(event.key.keysym.sym == SDLK_BACKSPACE){ if(load_save_menu.cursor > 0 && load_save_menu.cursor <= load_save_menu.filename.length()){ load_save_menu.filename.erase(--load_save_menu.cursor, 1); load_save_menu.current_file_index = -1; } }else if(event.key.keysym.sym == SDLK_DELETE){ if(load_save_menu.cursor >= 0 && load_save_menu.cursor < load_save_menu.filename.length()){ load_save_menu.filename.erase(load_save_menu.cursor, 1); load_save_menu.current_file_index = -1; } }else if(event.key.keysym.sym == SDLK_LEFT && instance.input.left.type != photon_input_state::keyboard){ if(--load_save_menu.cursor < 0){ load_save_menu.cursor = 0; } }else if(event.key.keysym.sym == SDLK_RIGHT && instance.input.right.type != photon_input_state::keyboard){ if(++load_save_menu.cursor > load_save_menu.filename.length()){ load_save_menu.cursor = load_save_menu.filename.length(); } }else if(event.key.keysym.sym == SDLK_HOME){ if(load_save_menu.current_file_index == -1){ load_save_menu.cursor = 0; }else{ load_save_menu.current_file_index = 0; load_save_menu.filename = load_save_menu.file_list[load_save_menu.current_file_index]; } }else if(event.key.keysym.sym == SDLK_END){ if(load_save_menu.current_file_index == -1){ load_save_menu.cursor = load_save_menu.filename.length(); }else{ load_save_menu.current_file_index = load_save_menu.file_list.size() - 1; load_save_menu.filename = load_save_menu.file_list[load_save_menu.current_file_index]; } }else if(event.key.keysym.sym == SDLK_ESCAPE){ gui::CancelLoadSave(instance); } }else if(event.key.keysym.sym == SDLK_ESCAPE){ instance.paused = !instance.paused; }else if(event.key.keysym.sym == SDLK_s && event.key.keysym.mod & KMOD_CTRL){ instance.paused = true; gui::StartSavingGUI(instance); } break; case SDL_QUIT: Close(instance); break; case SDL_WINDOWEVENT: switch(event.window.event){ case SDL_WINDOWEVENT_RESIZED: // case SDL_WINDOWEVENT_SIZE_CHANGED: if(event.window.data1 < 1){ // prevent window dissapearing if some weirdo decides to resize the window to a width of 0... SDL_SetWindowSize(instance.window.window_SDL, 1, event.window.data2); break; } if(event.window.data2 < 1){ // prevent window dissapearing if some weirdo decides to resize the window to a height of 0... SDL_SetWindowSize(instance.window.window_SDL, event.window.data1, 1); break; } opengl::OnResize(event.window.data1,event.window.data2, instance.window); break; } break; case SDL_MOUSEWHEEL: if(!instance.paused){ instance.camera_offset.z -= event.wheel.y * instance.camera_offset.z * 0.02f; } break; case SDL_MOUSEBUTTONUP: if(!instance.input.is_valid){ input::LoadConfig("/config/keyboard.xml", instance.input); PrintToLog("INFO: Using mouse & keyboard input."); }else if(event.button.button == SDL_BUTTON_LEFT){ if(!gui::HandleMouseClick(instance, event.button.x, event.button.y)){ if(glm::length(instance.player.location - WindowToWorldCoord(instance, event.button.x, event.button.y)) < 0.5f){ blocks::OnPhotonInteract(glm::uvec2(instance.player.location + 0.5f), instance.level, instance.player); } } } break; case SDL_CONTROLLERBUTTONUP: if(!instance.input.is_valid){ SDL_GameController *controller = SDL_GameControllerOpen(event.cbutton.which); if(controller != nullptr){ input::LoadConfig("/config/controller.xml", instance.input); instance.input.controller = controller; PrintToLog("INFO: Using Game Controller (device index %i)", event.cbutton.which); } } break; case SDL_CONTROLLERAXISMOTION: if(!instance.input.is_valid){ SDL_GameController *controller = SDL_GameControllerOpen(event.caxis.which); if(controller != nullptr){ input::LoadConfig("/config/controller.xml", instance.input); instance.input.controller = controller; PrintToLog("INFO: Using Game Controller (device index %i)", event.caxis.which); } } break; } } }
void InitFreeType(){ PrintToLog("INFO: Initializing FreeType."); FT_Library ft; FT_Face face; if(FT_Init_FreeType(&ft)) { PrintToLog("ERROR: Could not init FreeType library!"); return; } // TODO - load from some configuration file... const char *font_filename = "/Orbitron Bold.otf"; PHYSFS_File *file; intmax_t length; uint8_t *font_buffer; file = PHYSFS_openRead(font_filename); if(!file){ PrintToLog("ERROR: unable to open Font File: \"%s\"", font_filename); return; } length = PHYSFS_fileLength(file); font_buffer = new uint8_t[length]; PHYSFS_read(file, font_buffer, 1, length); PHYSFS_close(file); FT_Error err = FT_New_Memory_Face(ft, font_buffer, length, 0, &face); if(err){ PrintToLog("ERROR: Could not load font \"%s\"! error code %x!", font_filename, err); delete[] font_buffer; return; } FT_Set_Pixel_Sizes(face, 0, FONT_SIZE); FT_GlyphSlot g = face->glyph; int width = 0; int height = 0; for(unsigned char c = FONT_FIRST_CHAR; c <= FONT_LAST_CHAR; c++) { if(FT_Load_Char(face, c, FT_LOAD_RENDER)) { PrintToLog("WARNING: Loading character %c failed!", c); continue; } width += g->bitmap.width + 1; height = std::max(height, int(g->bitmap.rows)); } main_atlas.width = width; glGenTextures(1, &main_atlas.texture); glBindTexture(GL_TEXTURE_2D, main_atlas.texture); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); std::vector<GLubyte> empty_image(width * height, 0); glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, &empty_image[0]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); uint32_t x = 0; for(unsigned char i = FONT_FIRST_CHAR; i <= FONT_LAST_CHAR; i++) { if(FT_Load_Char(face, i, FT_LOAD_RENDER)) continue; glTexSubImage2D(GL_TEXTURE_2D, 0, x, 0, g->bitmap.width, g->bitmap.rows, GL_ALPHA, GL_UNSIGNED_BYTE, g->bitmap.buffer); main_atlas.characters[i].advance.x = g->advance.x >> 6; main_atlas.characters[i].advance.y = g->advance.y >> 6; main_atlas.characters[i].width = g->bitmap.width; main_atlas.characters[i].rows = g->bitmap.rows; main_atlas.characters[i].left = g->bitmap_left; main_atlas.characters[i].top = g->bitmap_top; main_atlas.characters[i].x = (float)x / (float)width; x += g->bitmap.width + 1; } PrintToLog("INFO: Created character atlas size %ix%i", width,height); FT_Done_Face(face); delete[] font_buffer; // TODO - perhaps leave this open for loading other fonts and close in a GarbageCollect() function? FT_Done_FreeType(ft); }
/** * Obtains a hash of the active state to use for consensus verification and checkpointing. * * For increased flexibility, so other implementations like OmniWallet and OmniChest can * also apply this methodology without necessarily using the same exact data types (which * would be needed to hash the data bytes directly), create a string in the following * format for each entry to use for hashing: * * ---STAGE 1 - BALANCES--- * Format specifiers & placeholders: * "%s|%d|%d|%d|%d|%d" - "address|propertyid|balance|selloffer_reserve|accept_reserve|metadex_reserve" * * Note: empty balance records and the pending tally are ignored. Addresses are sorted based * on lexicographical order, and balance records are sorted by the property identifiers. * * ---STAGE 2 - DEX SELL OFFERS--- * Format specifiers & placeholders: * "%s|%s|%d|%d|%d|%d|%d" - "txid|address|propertyid|offeramount|btcdesired|minfee|timelimit" * * Note: ordered ascending by txid. * * ---STAGE 3 - DEX ACCEPTS--- * Format specifiers & placeholders: * "%s|%s|%d|%d|%d" - "matchedselloffertxid|buyer|acceptamount|acceptamountremaining|acceptblock" * * Note: ordered ascending by matchedselloffertxid followed by buyer. * * ---STAGE 4 - METADEX TRADES--- * Format specifiers & placeholders: * "%s|%s|%d|%d|%d|%d|%d" - "txid|address|propertyidforsale|amountforsale|propertyiddesired|amountdesired|amountremaining" * * Note: ordered ascending by txid. * * ---STAGE 5 - CROWDSALES--- * Format specifiers & placeholders: * "%d|%d|%d|%d|%d" - "propertyid|propertyiddesired|deadline|usertokens|issuertokens" * * Note: ordered by property ID. * * ---STAGE 6 - PROPERTIES--- * Format specifiers & placeholders: * "%d|%s" - "propertyid|issueraddress" * * Note: ordered by property ID. * * The byte order is important, and we assume: * SHA256("abc") = "ad1500f261ff10b49c7a1796a36103b02322ae5dde404141eacf018fbf1678ba" * */ uint256 GetConsensusHash() { // allocate and init a SHA256_CTX SHA256_CTX shaCtx; SHA256_Init(&shaCtx); LOCK(cs_tally); if (msc_debug_consensus_hash) PrintToLog("Beginning generation of current consensus hash...\n"); // Balances - loop through the tally map, updating the sha context with the data from each balance and tally type // Placeholders: "address|propertyid|balance|selloffer_reserve|accept_reserve|metadex_reserve" // Sort alphabetically first std::map<std::string, CMPTally> tallyMapSorted; for (std::unordered_map<string, CMPTally>::iterator uoit = mp_tally_map.begin(); uoit != mp_tally_map.end(); ++uoit) { tallyMapSorted.insert(std::make_pair(uoit->first,uoit->second)); } for (std::map<string, CMPTally>::iterator my_it = tallyMapSorted.begin(); my_it != tallyMapSorted.end(); ++my_it) { const std::string& address = my_it->first; CMPTally& tally = my_it->second; tally.init(); uint32_t propertyId = 0; while (0 != (propertyId = (tally.next()))) { std::string dataStr = GenerateConsensusString(tally, address, propertyId); if (dataStr.empty()) continue; // skip empty balances if (msc_debug_consensus_hash) PrintToLog("Adding balance data to consensus hash: %s\n", dataStr); SHA256_Update(&shaCtx, dataStr.c_str(), dataStr.length()); } } // DEx sell offers - loop through the DEx and add each sell offer to the consensus hash (ordered by txid) // Placeholders: "txid|address|propertyid|offeramount|btcdesired|minfee|timelimit" std::vector<std::pair<arith_uint256, std::string> > vecDExOffers; for (OfferMap::iterator it = my_offers.begin(); it != my_offers.end(); ++it) { const CMPOffer& selloffer = it->second; const std::string& sellCombo = it->first; std::string seller = sellCombo.substr(0, sellCombo.size() - 2); std::string dataStr = GenerateConsensusString(selloffer, seller); vecDExOffers.push_back(std::make_pair(arith_uint256(selloffer.getHash().ToString()), dataStr)); } std::sort (vecDExOffers.begin(), vecDExOffers.end()); for (std::vector<std::pair<arith_uint256, std::string> >::iterator it = vecDExOffers.begin(); it != vecDExOffers.end(); ++it) { const std::string& dataStr = it->second; if (msc_debug_consensus_hash) PrintToLog("Adding DEx offer data to consensus hash: %s\n", dataStr); SHA256_Update(&shaCtx, dataStr.c_str(), dataStr.length()); } // DEx accepts - loop through the accepts map and add each accept to the consensus hash (ordered by matchedtxid then buyer) // Placeholders: "matchedselloffertxid|buyer|acceptamount|acceptamountremaining|acceptblock" std::vector<std::pair<std::string, std::string> > vecAccepts; for (AcceptMap::const_iterator it = my_accepts.begin(); it != my_accepts.end(); ++it) { const CMPAccept& accept = it->second; const std::string& acceptCombo = it->first; std::string buyer = acceptCombo.substr((acceptCombo.find("+") + 1), (acceptCombo.size()-(acceptCombo.find("+") + 1))); std::string dataStr = GenerateConsensusString(accept, buyer); std::string sortKey = strprintf("%s-%s", accept.getHash().GetHex(), buyer); vecAccepts.push_back(std::make_pair(sortKey, dataStr)); } std::sort (vecAccepts.begin(), vecAccepts.end()); for (std::vector<std::pair<std::string, std::string> >::iterator it = vecAccepts.begin(); it != vecAccepts.end(); ++it) { const std::string& dataStr = it->second; if (msc_debug_consensus_hash) PrintToLog("Adding DEx accept to consensus hash: %s\n", dataStr); SHA256_Update(&shaCtx, dataStr.c_str(), dataStr.length()); } // MetaDEx trades - loop through the MetaDEx maps and add each open trade to the consensus hash (ordered by txid) // Placeholders: "txid|address|propertyidforsale|amountforsale|propertyiddesired|amountdesired|amountremaining" std::vector<std::pair<arith_uint256, std::string> > vecMetaDExTrades; for (md_PropertiesMap::const_iterator my_it = metadex.begin(); my_it != metadex.end(); ++my_it) { const md_PricesMap& prices = my_it->second; for (md_PricesMap::const_iterator it = prices.begin(); it != prices.end(); ++it) { const md_Set& indexes = it->second; for (md_Set::const_iterator it = indexes.begin(); it != indexes.end(); ++it) { const CMPMetaDEx& obj = *it; std::string dataStr = GenerateConsensusString(obj); vecMetaDExTrades.push_back(std::make_pair(arith_uint256(obj.getHash().ToString()), dataStr)); } } } std::sort (vecMetaDExTrades.begin(), vecMetaDExTrades.end()); for (std::vector<std::pair<arith_uint256, std::string> >::iterator it = vecMetaDExTrades.begin(); it != vecMetaDExTrades.end(); ++it) { const std::string& dataStr = it->second; if (msc_debug_consensus_hash) PrintToLog("Adding MetaDEx trade data to consensus hash: %s\n", dataStr); SHA256_Update(&shaCtx, dataStr.c_str(), dataStr.length()); } // Crowdsales - loop through open crowdsales and add to the consensus hash (ordered by property ID) // Note: the variables of the crowdsale (amount, bonus etc) are not part of the crowdsale map and not included here to // avoid additionalal loading of SP entries from the database // Placeholders: "propertyid|propertyiddesired|deadline|usertokens|issuertokens" std::vector<std::pair<uint32_t, std::string> > vecCrowds; for (CrowdMap::const_iterator it = my_crowds.begin(); it != my_crowds.end(); ++it) { const CMPCrowd& crowd = it->second; uint32_t propertyId = crowd.getPropertyId(); std::string dataStr = GenerateConsensusString(crowd); vecCrowds.push_back(std::make_pair(propertyId, dataStr)); } std::sort (vecCrowds.begin(), vecCrowds.end()); for (std::vector<std::pair<uint32_t, std::string> >::iterator it = vecCrowds.begin(); it != vecCrowds.end(); ++it) { std::string dataStr = (*it).second; if (msc_debug_consensus_hash) PrintToLog("Adding Crowdsale entry to consensus hash: %s\n", dataStr); SHA256_Update(&shaCtx, dataStr.c_str(), dataStr.length()); } // Properties - loop through each property and store the issuer (to capture state changes via change issuer transactions) // Note: we are loading every SP from the DB to check the issuer, if using consensus_hash_every_block debug option this // will slow things down dramatically. Not an issue to do it once every 10,000 blocks for checkpoint verification. // Placeholders: "propertyid|issueraddress" for (uint8_t ecosystem = 1; ecosystem <= 2; ecosystem++) { uint32_t startPropertyId = (ecosystem == 1) ? 1 : TEST_ECO_PROPERTY_1; for (uint32_t propertyId = startPropertyId; propertyId < _my_sps->peekNextSPID(ecosystem); propertyId++) { CMPSPInfo::Entry sp; if (!_my_sps->getSP(propertyId, sp)) { PrintToLog("Error loading property ID %d for consensus hashing, hash should not be trusted!\n"); continue; } std::string dataStr = GenerateConsensusString(propertyId, sp.issuer); if (msc_debug_consensus_hash) PrintToLog("Adding property to consensus hash: %s\n", dataStr); SHA256_Update(&shaCtx, dataStr.c_str(), dataStr.length()); } } // extract the final result and return the hash uint256 consensusHash; SHA256_Final((unsigned char*)&consensusHash, &shaCtx); if (msc_debug_consensus_hash) PrintToLog("Finished generation of consensus hash. Result: %s\n", consensusHash.GetHex()); return consensusHash; }