diff --git a/src/Qubic.vcxproj b/src/Qubic.vcxproj index 89a1041b..6e264c60 100644 --- a/src/Qubic.vcxproj +++ b/src/Qubic.vcxproj @@ -39,7 +39,8 @@ - + + @@ -59,6 +60,7 @@ + @@ -286,4 +288,4 @@ - + \ No newline at end of file diff --git a/src/Qubic.vcxproj.filters b/src/Qubic.vcxproj.filters index bdbf6b63..2fb5a67f 100644 --- a/src/Qubic.vcxproj.filters +++ b/src/Qubic.vcxproj.filters @@ -42,7 +42,6 @@ - network_messages @@ -174,6 +173,15 @@ mining + + logging + + + logging + + + platform + @@ -200,6 +208,9 @@ {df525479-7504-470c-a25a-de4af8be0e5d} + + {d334594b-f24d-440e-949a-c791aa13f867} + diff --git a/src/assets.h b/src/assets.h index 7609f96d..64bc655b 100644 --- a/src/assets.h +++ b/src/assets.h @@ -9,7 +9,7 @@ #include "network_messages/assets.h" #include "public_settings.h" -#include "logging.h" +#include "logging/logging.h" #include "kangaroo_twelve.h" #include "four_q.h" #include "common_buffers.h" diff --git a/src/contract_core/contract_def.h b/src/contract_core/contract_def.h index 7fe7602f..68a7fed6 100644 --- a/src/contract_core/contract_def.h +++ b/src/contract_core/contract_def.h @@ -133,13 +133,11 @@ struct __FunctionOrProcedureBeginEndGuard #undef CONTRACT_STATE_TYPE #undef CONTRACT_STATE2_TYPE -#if IPO_OF_CCF #define CCF_CONTRACT_INDEX 8 #define CONTRACT_INDEX CCF_CONTRACT_INDEX #define CONTRACT_STATE_TYPE CCF #define CONTRACT_STATE2_TYPE CCF2 #include "contracts/ComputorControlledFund.h" -#endif #define MAX_CONTRACT_ITERATION_DURATION 0 // In milliseconds, must be above 0; for now set to 0 to disable timeout, because a rollback mechanism needs to be implemented to properly handle timeout @@ -190,9 +188,7 @@ constexpr struct ContractDescription {"MLM", 112, 10000, sizeof(IPO)}, {"GQMPROP", 123, 10000, sizeof(GQMPROP)}, {"SWATCH", 123, 10000, sizeof(IPO)}, -#if IPO_OF_CCF {"CCF", 127, 10000, sizeof(CCF)}, // proposal in epoch 125, IPO in 126, construction and first use in 127 -#endif }; constexpr unsigned int contractCount = sizeof(contractDescriptions) / sizeof(contractDescriptions[0]); @@ -276,7 +272,5 @@ static void initializeContracts() REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(MLM); REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(GQMPROP); REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(SWATCH); -#if IPO_OF_CCF REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(CCF); -#endif } diff --git a/src/contract_core/qpi_proposal_voting.h b/src/contract_core/qpi_proposal_voting.h index ad3f9308..46bafdb4 100644 --- a/src/contract_core/qpi_proposal_voting.h +++ b/src/contract_core/qpi_proposal_voting.h @@ -174,7 +174,7 @@ namespace QPI if (!supportScalarVotes) { // option voting only (1 byte per voter) - // TODO: ASSERT that proposal type does not require sint64 (internal logic error) + ASSERT(proposal.type != ProposalTypes::VariableScalarMean); constexpr uint8 noVoteValue = 0xff; setMemory(votes, noVoteValue); } @@ -206,7 +206,7 @@ namespace QPI // scalar vote if (supportScalarVotes) { - // TODO: add ASSERT checking that storage type is sint64 + ASSERT(sizeof(votes[0]) == 8); if ((voteValue >= this->variableScalar.minValue && voteValue <= this->variableScalar.maxValue)) { // (cast should not be needed but is to get rid of warning) @@ -365,8 +365,7 @@ namespace QPI // remove oldest proposal clearProposal(proposalIndex); - // call voters interface again in case it needs to register the proposer - // TODO: add ASSERT, because this should always return the same value if the interface is implemented correctly + // call voters interface again in case it needs to register the proposer (should always return the same value) proposalIndex = pv.proposersAndVoters.getNewProposalIndex(qpi, proposer); } @@ -463,7 +462,6 @@ namespace QPI return false; // scalar voting -> compute mean value of votes - // TODO: ASSERT(optionCount) == 0 sint64 value; sint64 accumulation = 0; if (p.variableScalar.maxValue > p.variableScalar.maxSupportedValue / maxVoters @@ -554,8 +552,8 @@ namespace QPI else { // option voting -> compute histogram - // TODO: ASSERT(optionCount) > 0 - // TODO: assert option count >= array capacity + ASSERT(votingSummary.optionCount > 0); + ASSERT(votingSummary.optionCount <= votingSummary.optionVoteCount.capacity()); auto& hist = votingSummary.optionVoteCount; hist.setAll(0); for (uint32 i = 0; i < pv.maxVoters; ++i) diff --git a/src/contracts/qpi.h b/src/contracts/qpi.h index 104ed6a0..2fabd22e 100644 --- a/src/contracts/qpi.h +++ b/src/contracts/qpi.h @@ -5,6 +5,8 @@ // m256i is used for the id data type #include "../platform/m256.h" +// ASSERT can be used to support debugging and speed-up development +#include "../platform/assert.h" namespace QPI { @@ -1404,7 +1406,7 @@ namespace QPI input, output, \ *(contractStateType::function##_locals*)qpi.__qpiAllocLocals(sizeof(contractStateType::function##_locals))); \ qpi.__qpiReleaseStateForReading(contractStateType::__contract_index); \ - qpi.__qpiFreeContextOtherContract(contractStateType::__contract_index); \ + qpi.__qpiFreeContextOtherContract(); \ qpi.__qpiFreeLocals() // Transfer invocation reward and invoke of other contract (procedure only) @@ -1420,7 +1422,7 @@ namespace QPI input, output, \ *(contractStateType::procedure##_locals*)qpi.__qpiAllocLocals(sizeof(contractStateType::procedure##_locals))); \ qpi.__qpiReleaseStateForWriting(contractStateType::__contract_index); \ - qpi.__qpiFreeContextOtherContract(contractStateType::__contract_index); \ + qpi.__qpiFreeContextOtherContract(); \ qpi.__qpiFreeLocals() #define QUERY_ORACLE(oracle, query) // TODO diff --git a/src/logging.h b/src/logging/logging.h similarity index 74% rename from src/logging.h rename to src/logging/logging.h index e2006aa4..c248f416 100644 --- a/src/logging.h +++ b/src/logging/logging.h @@ -3,12 +3,23 @@ #include "platform/m256.h" #include "platform/concurrency.h" #include "platform/time.h" +#include "platform/memory.h" +#include "platform/assert.h" + +#include "network_messages/header.h" #include "private_settings.h" +#include "public_settings.h" #include "system.h" -#include "network_core/peers.h" +#include "kangaroo_twelve.h" + +struct Peer; -#if LOG_QU_TRANSFERS | LOG_ASSET_ISSUANCES | LOG_ASSET_OWNERSHIP_CHANGES | LOG_ASSET_POSSESSION_CHANGES | LOG_CONTRACT_ERROR_MESSAGES | LOG_CONTRACT_WARNING_MESSAGES | LOG_CONTRACT_INFO_MESSAGES | LOG_CONTRACT_DEBUG_MESSAGES | LOG_CUSTOM_MESSAGES +#define LOG_UNIVERSE (LOG_ASSET_ISSUANCES | LOG_ASSET_OWNERSHIP_CHANGES | LOG_ASSET_POSSESSION_CHANGES) +#define LOG_SPECTRUM (LOG_QU_TRANSFERS | LOG_BURNINGS | LOG_DUST_BURNINGS | LOG_SPECTRUM_STATS) +#define LOG_CONTRACTS (LOG_CONTRACT_ERROR_MESSAGES | LOG_CONTRACT_WARNING_MESSAGES | LOG_CONTRACT_INFO_MESSAGES | LOG_CONTRACT_DEBUG_MESSAGES) + +#if LOG_SPECTRUM | LOG_UNIVERSE | LOG_CONTRACTS | LOG_CUSTOM_MESSAGES #define ENABLED_LOGGING 1 #else #define ENABLED_LOGGING 0 @@ -71,6 +82,8 @@ struct ResponseLogIdRangeFromTx #define CONTRACT_INFORMATION_MESSAGE 6 #define CONTRACT_DEBUG_MESSAGE 7 #define BURNING 8 +#define DUST_BURNING 9 +#define SPECTRUM_STATS 10 #define CUSTOM_MESSAGE 255 /* @@ -178,6 +191,42 @@ struct Burning char _terminator; // Only data before "_terminator" are logged }; +// Contains N entites and amounts of dust burns in the memory layout: [N with 2 bytes] | [public key 0] | [amount 0] | [public key 1] | [amount 1] | ... | [public key N-1] | [amount N-1]. +// CAUTION: This is a variable-size log type and the full log message content goes boyond the size of this struct! +struct DustBurning +{ + unsigned short numberOfBurns; + + struct Entity + { + m256i publicKey; + unsigned long long amount; + }; + static_assert(sizeof(Entity) == (sizeof(m256i) + sizeof(unsigned long long)), "Unexpected size"); + + unsigned int messageSize() const + { + return 2 + numberOfBurns * sizeof(Entity); + } + + Entity& entity(unsigned short i) + { + ASSERT(i < numberOfBurns); + char* buf = reinterpret_cast(this); + return *reinterpret_cast(buf + i * (sizeof(Entity)) + 2); + } +}; + +struct SpectrumStats +{ + unsigned long long totalAmount; + unsigned long long dustThresholdBurnAll; + unsigned long long dustThresholdBurnHalf; + unsigned int numberOfEntities; + unsigned int entityCategoryPopulations[48]; +}; + + /* * LOGGING IMPLEMENTATION */ @@ -367,12 +416,11 @@ class qLogger static bool initLogging() { #if ENABLED_LOGGING - EFI_STATUS status; if (logBuffer == NULL) { - if (status = bs->AllocatePool(EfiRuntimeServicesData, LOG_BUFFER_SIZE, (void**)&logBuffer)) + if (!allocatePool(LOG_BUFFER_SIZE, (void**)&logBuffer)) { - logStatusToConsole(L"EFI_BOOT_SERVICES.AllocatePool() fails", status, __LINE__); + logToConsole(L"Failed to allocate logging buffer!"); return false; } @@ -380,9 +428,9 @@ class qLogger if (mapTxToLogId == NULL) { - if (status = bs->AllocatePool(EfiRuntimeServicesData, LOG_TX_INFO_STORAGE * sizeof(BlobInfo), (void**)&mapTxToLogId)) + if (!allocatePool(LOG_TX_INFO_STORAGE * sizeof(BlobInfo), (void**)&mapTxToLogId)) { - logStatusToConsole(L"EFI_BOOT_SERVICES.AllocatePool() fails", status, __LINE__); + logToConsole(L"Failed to allocate logging buffer!"); return false; } @@ -390,9 +438,9 @@ class qLogger if (mapLogIdToBufferIndex == NULL) { - if (status = bs->AllocatePool(EfiRuntimeServicesData, LOG_MAX_STORAGE_ENTRIES * sizeof(BlobInfo), (void**)&mapLogIdToBufferIndex)) + if (!allocatePool(LOG_MAX_STORAGE_ENTRIES * sizeof(BlobInfo), (void**)&mapLogIdToBufferIndex)) { - logStatusToConsole(L"EFI_BOOT_SERVICES.AllocatePool() fails", status, __LINE__); + logToConsole(L"Failed to allocate logging buffer!"); return false; } @@ -401,10 +449,25 @@ class qLogger #endif return true; } + static void deinitLogging() { #if ENABLED_LOGGING - freePool(logBuffer); + if (logBuffer) + { + freePool(logBuffer); + logBuffer = nullptr; + } + if (mapTxToLogId) + { + freePool(mapTxToLogId); + mapTxToLogId = nullptr; + } + if (mapLogIdToBufferIndex) + { + freePool(mapLogIdToBufferIndex); + mapLogIdToBufferIndex = nullptr; + } #endif } @@ -540,6 +603,20 @@ class qLogger #endif } + void logDustBurning(const DustBurning* message) + { +#if LOG_DUST_BURNINGS + logMessage(message->messageSize(), DUST_BURNING, message); +#endif + } + + void logSpectrumStats(const SpectrumStats& message) + { +#if LOG_SPECTRUM_STATS + logMessage(sizeof(SpectrumStats), SPECTRUM_STATS, &message); +#endif + } + template void logCustomMessage(T message) { @@ -549,90 +626,11 @@ class qLogger logMessage(offsetof(T, _terminator), CUSTOM_MESSAGE, &message); #endif } + // Request: ranges of log ID - static void processRequestLog(Peer* peer, RequestResponseHeader* header) - { -#if ENABLED_LOGGING - RequestLog* request = header->getPayload(); - if (request->passcode[0] == logReaderPasscodes[0] - && request->passcode[1] == logReaderPasscodes[1] - && request->passcode[2] == logReaderPasscodes[2] - && request->passcode[3] == logReaderPasscodes[3]) - { - BlobInfo startIdBufferRange = logBuf.getBlobInfo(request->fromID); - BlobInfo endIdBufferRange = logBuf.getBlobInfo(request->toID); // inclusive - if (startIdBufferRange.startIndex != -1 && startIdBufferRange.length != -1 - && endIdBufferRange.startIndex != -1 && endIdBufferRange.length != -1) - { - if (endIdBufferRange.startIndex < startIdBufferRange.startIndex) - { - // round buffer case, only response first packet - let the client figure out and request the rest - unsigned long long i = 0; - for (i = request->fromID; i <= request->toID; i++) - { - BlobInfo iBufferRange = logBuf.getBlobInfo(i); - if (iBufferRange.startIndex < startIdBufferRange.startIndex) - { - i--; - break; - } - } - // first packet: from startID to end of buffer - { - BlobInfo iBufferRange = logBuf.getBlobInfo(i); - unsigned long long startFrom = startIdBufferRange.startIndex; - unsigned long long length = iBufferRange.length + iBufferRange.startIndex - startFrom; - if (length > RequestResponseHeader::max_size) - { - length = RequestResponseHeader::max_size; - } - enqueueResponse(peer, (unsigned int)(length), RespondLog::type, header->dejavu(), logBuffer + startFrom); - } - // second packet: from start buffer to endID - NOT SEND THIS - } - else - { - unsigned long long startFrom = startIdBufferRange.startIndex; - unsigned long long length = endIdBufferRange.length + endIdBufferRange.startIndex - startFrom; - if (length > RequestResponseHeader::max_size) - { - length = RequestResponseHeader::max_size; - } - enqueueResponse(peer, (unsigned int)(length), RespondLog::type, header->dejavu(), logBuffer + startFrom); - } - } - else - { - enqueueResponse(peer, 0, RespondLog::type, header->dejavu(), NULL); - } - return; - } -#endif - enqueueResponse(peer, 0, RespondLog::type, header->dejavu(), NULL); - } + static void processRequestLog(Peer* peer, RequestResponseHeader* header); - static void processRequestTxLogInfo(Peer* peer, RequestResponseHeader* header) - { -#if ENABLED_LOGGING - RequestLogIdRangeFromTx* request = header->getPayload(); - if (request->passcode[0] == logReaderPasscodes[0] - && request->passcode[1] == logReaderPasscodes[1] - && request->passcode[2] == logReaderPasscodes[2] - && request->passcode[3] == logReaderPasscodes[3] - && request->tick < system.tick - && request->tick >= system.initialTick - ) - { - ResponseLogIdRangeFromTx resp; - BlobInfo info = tx.getLogIdInfo(request->tick, request->txId); - resp.fromLogId = info.startIndex; - resp.length = info.length; - enqueueResponse(peer, sizeof(ResponseLogIdRangeFromTx), ResponseLogIdRangeFromTx::type, header->dejavu(), &resp); - return; - } -#endif - enqueueResponse(peer, 0, ResponseLogIdRangeFromTx::type, header->dejavu(), NULL); - } + static void processRequestTxLogInfo(Peer* peer, RequestResponseHeader* header); }; qLogger logger; diff --git a/src/logging/net_msg_impl.h b/src/logging/net_msg_impl.h new file mode 100644 index 00000000..5b1ae030 --- /dev/null +++ b/src/logging/net_msg_impl.h @@ -0,0 +1,87 @@ +#pragma once + +#include "logging/logging.h" +#include "network_core/peers.h" + + +// Request: ranges of log ID +void qLogger::processRequestLog(Peer* peer, RequestResponseHeader* header) +{ +#if ENABLED_LOGGING + RequestLog* request = header->getPayload(); + if (request->passcode[0] == logReaderPasscodes[0] + && request->passcode[1] == logReaderPasscodes[1] + && request->passcode[2] == logReaderPasscodes[2] + && request->passcode[3] == logReaderPasscodes[3]) + { + BlobInfo startIdBufferRange = logBuf.getBlobInfo(request->fromID); + BlobInfo endIdBufferRange = logBuf.getBlobInfo(request->toID); // inclusive + if (startIdBufferRange.startIndex != -1 && startIdBufferRange.length != -1 + && endIdBufferRange.startIndex != -1 && endIdBufferRange.length != -1) + { + if (endIdBufferRange.startIndex < startIdBufferRange.startIndex) + { + // round buffer case, only response first packet - let the client figure out and request the rest + for (unsigned long long i = request->fromID; i <= request->toID; i++) + { + BlobInfo iBufferRange = logBuf.getBlobInfo(i); + ASSERT(iBufferRange.startIndex >= 0); + ASSERT(iBufferRange.length >= 0); + if (iBufferRange.startIndex < startIdBufferRange.startIndex) + { + endIdBufferRange = logBuf.getBlobInfo(i - 1); + break; + } + } + // first packet: from startID to end of buffer IS SENT BELOW + // second packet: from start buffer to endID IS NOT SENT FROM HERE, but requested by client later + } + + long long startFrom = startIdBufferRange.startIndex; + long long length = endIdBufferRange.length + endIdBufferRange.startIndex - startFrom; + if (length > RequestResponseHeader::max_size) + { + unsigned long long toID = request->toID; + length -= endIdBufferRange.length; + while (length > RequestResponseHeader::max_size) + { + ASSERT(toID > request->fromID); + --toID; + endIdBufferRange = logBuf.getBlobInfo(toID); + length -= endIdBufferRange.length; + } + } + enqueueResponse(peer, (unsigned int)(length), RespondLog::type, header->dejavu(), logBuffer + startFrom); + } + else + { + enqueueResponse(peer, 0, RespondLog::type, header->dejavu(), NULL); + } + return; + } +#endif + enqueueResponse(peer, 0, RespondLog::type, header->dejavu(), NULL); +} + +void qLogger::processRequestTxLogInfo(Peer* peer, RequestResponseHeader* header) +{ +#if ENABLED_LOGGING + RequestLogIdRangeFromTx* request = header->getPayload(); + if (request->passcode[0] == logReaderPasscodes[0] + && request->passcode[1] == logReaderPasscodes[1] + && request->passcode[2] == logReaderPasscodes[2] + && request->passcode[3] == logReaderPasscodes[3] + && request->tick < system.tick + && request->tick >= system.initialTick + ) + { + ResponseLogIdRangeFromTx resp; + BlobInfo info = tx.getLogIdInfo(request->tick, request->txId); + resp.fromLogId = info.startIndex; + resp.length = info.length; + enqueueResponse(peer, sizeof(ResponseLogIdRangeFromTx), ResponseLogIdRangeFromTx::type, header->dejavu(), &resp); + return; + } +#endif + enqueueResponse(peer, 0, ResponseLogIdRangeFromTx::type, header->dejavu(), NULL); +} diff --git a/src/network_core/peers.h b/src/network_core/peers.h index ac6a0323..b55a9712 100644 --- a/src/network_core/peers.h +++ b/src/network_core/peers.h @@ -31,7 +31,7 @@ static_assert((NUMBER_OF_INCOMING_CONNECTIONS / NUMBER_OF_OUTGOING_CONNECTIONS) static volatile bool listOfPeersIsStatic = false; -typedef struct +struct Peer { EFI_TCP4_PROTOCOL* tcp4Protocol; EFI_TCP4_LISTEN_TOKEN connectAcceptToken; @@ -50,7 +50,7 @@ typedef struct BOOLEAN isClosing; // Indicate the peer is incomming connection type BOOLEAN isIncommingConnection; -} Peer; +}; typedef struct { diff --git a/src/platform/assert.h b/src/platform/assert.h new file mode 100644 index 00000000..827b1942 --- /dev/null +++ b/src/platform/assert.h @@ -0,0 +1,23 @@ +#pragma once + +#if defined(EXPECT_TRUE) + +// in gtest context, use EXPECT_TRUE as ASSERT +#define ASSERT EXPECT_TRUE + +#elif defined(NDEBUG) + +// with NDEBUG, make ASSERT disappear +#define ASSERT(expression) ((void)0) + +#else + +// otherwise, use addDebugMessageAssert() defined in debugging.h +#define ASSERT(expression) (void)( \ + (!!(expression)) || \ + (addDebugMessageAssert(_CRT_WIDE(#expression), _CRT_WIDE(__FILE__), (unsigned int)(__LINE__)), 0) \ + ) + +static void addDebugMessageAssert(const wchar_t* message, const wchar_t* file, const unsigned int lineNumber); + +#endif diff --git a/src/platform/debugging.h b/src/platform/debugging.h index 0fbb001e..772551ac 100644 --- a/src/platform/debugging.h +++ b/src/platform/debugging.h @@ -1,23 +1,21 @@ #pragma once +#include "assert.h" #include "concurrency.h" #include "file_io.h" #if defined(EXPECT_TRUE) -// in gtest context, use EXPECT_TRUE as ASSERT -#define ASSERT EXPECT_TRUE - +// In gtest context, print with standard library static void addDebugMessage(const CHAR16* msg) { wprintf(L"%ls\n", msg); } #elif defined(NDEBUG) + // static void addDebugMessage(const CHAR16* msg){} // empty impl -// with NDEBUG, make ASSERT disappear -#define ASSERT(expression) ((void)0) #else @@ -127,9 +125,4 @@ static void addDebugMessageAssert(const CHAR16* message, const CHAR16* file, con RELEASE(debugLogLock); } -#define ASSERT(expression) (void)( \ - (!!(expression)) || \ - (addDebugMessageAssert(_CRT_WIDE(#expression), _CRT_WIDE(__FILE__), (unsigned int)(__LINE__)), 0) \ - ) - #endif diff --git a/src/platform/file_io.h b/src/platform/file_io.h index fdcc5671..3d1a09a3 100644 --- a/src/platform/file_io.h +++ b/src/platform/file_io.h @@ -11,9 +11,9 @@ // If you get an error reading and writing files, set the chunk sizes below to // the cluster size set for formatting you disk. If you have no idea about the -// cluster size, try 32768. -#define READING_CHUNK_SIZE 1048576 -#define WRITING_CHUNK_SIZE 1048576 +// cluster size, try 16384. +#define READING_CHUNK_SIZE 32768 +#define WRITING_CHUNK_SIZE 32768 #define FILE_CHUNK_SIZE (209715200ULL) // for large file saving #define VOLUME_LABEL L"Qubic" diff --git a/src/platform/m256.h b/src/platform/m256.h index 4ac68e21..207013fd 100644 --- a/src/platform/m256.h +++ b/src/platform/m256.h @@ -56,11 +56,7 @@ union m256i m256i(unsigned long long ull0, unsigned long long ull1, unsigned long long ull2, unsigned long long ull3) { - // TODO: set with _mm256_set_epi64x - m256i_u64[0] = ull0; - m256i_u64[1] = ull1; - m256i_u64[2] = ull2; - m256i_u64[3] = ull3; + m256i_intr() = _mm256_set_epi64x(ull3, ull2, ull1, ull0); } m256i(const unsigned char value[32]) @@ -166,6 +162,11 @@ union m256i ret.setRandomValue(); return ret; } + + inline static m256i zero() + { + return _mm256_setzero_si256(); + } }; static_assert(sizeof(m256i) == 32, "m256 has unexpected size!"); diff --git a/src/platform/time.h b/src/platform/time.h index 9576d68f..c3105ffe 100644 --- a/src/platform/time.h +++ b/src/platform/time.h @@ -1,36 +1,35 @@ #pragma once #include "uefi.h" +#include "memory.h" #include -#ifdef NO_UEFI -#error "NO_UEFI implementation is missing!" -#endif -static EFI_TIME time; +static EFI_TIME utcTime; -static void initTime() -{ - bs->SetMem(&time, sizeof(time), 0); - time.Year = 2022; - time.Month = 4; - time.Day = 13; - time.Hour = 12; +static void updateTime() +{ +#ifdef NO_UEFI + // TODO? Current time outside of UEFI +#else EFI_TIME newTime; if (!rs->GetTime(&newTime, NULL)) { - bs->CopyMem(&time, &newTime, sizeof(time)); + bs->CopyMem(&utcTime, &newTime, sizeof(utcTime)); } +#endif } -static void updateTime() +static void initTime() { - EFI_TIME newTime; - if (!rs->GetTime(&newTime, NULL)) - { - bs->CopyMem(&time, &newTime, sizeof(time)); - } + setMem(&utcTime, sizeof(utcTime), 0); + utcTime.Year = 2022; + utcTime.Month = 4; + utcTime.Day = 13; + utcTime.Hour = 12; + + updateTime(); } inline int dayIndex(unsigned int year, unsigned int month, unsigned int day) // 0 = Wednesday diff --git a/src/private_settings.h b/src/private_settings.h index 09690a41..063c183f 100644 --- a/src/private_settings.h +++ b/src/private_settings.h @@ -23,6 +23,9 @@ static const unsigned char whiteListPeers[][4] = { }; #define LOG_QU_TRANSFERS 0 // "0" disables logging, "1" enables it +#define LOG_BURNINGS 0 +#define LOG_DUST_BURNINGS 1 +#define LOG_SPECTRUM_STATS 1 #define LOG_ASSET_ISSUANCES 0 #define LOG_ASSET_OWNERSHIP_CHANGES 0 #define LOG_ASSET_POSSESSION_CHANGES 0 @@ -30,7 +33,6 @@ static const unsigned char whiteListPeers[][4] = { #define LOG_CONTRACT_WARNING_MESSAGES 0 #define LOG_CONTRACT_INFO_MESSAGES 0 #define LOG_CONTRACT_DEBUG_MESSAGES 0 -#define LOG_BURNINGS 0 #define LOG_CUSTOM_MESSAGES 0 static unsigned long long logReaderPasscodes[4] = { 0, 0, 0, 0 // REMOVE THIS ENTRY AND REPLACE IT WITH YOUR OWN RANDOM NUMBERS IN [0..18446744073709551615] RANGE IF LOGGING IS ENABLED diff --git a/src/public_settings.h b/src/public_settings.h index bdf32c92..781f117a 100644 --- a/src/public_settings.h +++ b/src/public_settings.h @@ -5,9 +5,6 @@ ////////////////////////////////////////////////////////////////////////// // Config options for operators -// Need to be set depending on voting results of proposal on IPO of the CCF contract (1 if accepted, 0 if rejected) -#define IPO_OF_CCF 1 - // no need to define AVX512 here anymore, just change the project settings to use the AVX512 version // random seed is now obtained from spectrumDigests @@ -52,12 +49,12 @@ // Config options that should NOT be changed by operators #define VERSION_A 1 -#define VERSION_B 218 +#define VERSION_B 219 #define VERSION_C 0 // Epoch and initial tick for node startup -#define EPOCH 126 -#define TICK 15840000 +#define EPOCH 127 +#define TICK 15970000 #define ARBITRATOR "AFZPUAIYVPNUYGJRQVLUKOPPVLHAZQTGLYAAUUNBXFTVTAMSBKQBLEIEPCVJ" diff --git a/src/qubic.cpp b/src/qubic.cpp index 4a152f09..c9667a4f 100644 --- a/src/qubic.cpp +++ b/src/qubic.cpp @@ -38,7 +38,8 @@ #include "assets.h" #include "spectrum.h" -#include "logging.h" +#include "logging/logging.h" +#include "logging/net_msg_impl.h" #include "tick_storage.h" #include "vote_counter.h" @@ -282,18 +283,18 @@ static void logToConsole(const CHAR16* message) return; } - timestampedMessage[0] = (time.Year % 100) / 10 + L'0'; - timestampedMessage[1] = time.Year % 10 + L'0'; - timestampedMessage[2] = time.Month / 10 + L'0'; - timestampedMessage[3] = time.Month % 10 + L'0'; - timestampedMessage[4] = time.Day / 10 + L'0'; - timestampedMessage[5] = time.Day % 10 + L'0'; - timestampedMessage[6] = time.Hour / 10 + L'0'; - timestampedMessage[7] = time.Hour % 10 + L'0'; - timestampedMessage[8] = time.Minute / 10 + L'0'; - timestampedMessage[9] = time.Minute % 10 + L'0'; - timestampedMessage[10] = time.Second / 10 + L'0'; - timestampedMessage[11] = time.Second % 10 + L'0'; + timestampedMessage[0] = (utcTime.Year % 100) / 10 + L'0'; + timestampedMessage[1] = utcTime.Year % 10 + L'0'; + timestampedMessage[2] = utcTime.Month / 10 + L'0'; + timestampedMessage[3] = utcTime.Month % 10 + L'0'; + timestampedMessage[4] = utcTime.Day / 10 + L'0'; + timestampedMessage[5] = utcTime.Day % 10 + L'0'; + timestampedMessage[6] = utcTime.Hour / 10 + L'0'; + timestampedMessage[7] = utcTime.Hour % 10 + L'0'; + timestampedMessage[8] = utcTime.Minute / 10 + L'0'; + timestampedMessage[9] = utcTime.Minute % 10 + L'0'; + timestampedMessage[10] = utcTime.Second / 10 + L'0'; + timestampedMessage[11] = utcTime.Second % 10 + L'0'; timestampedMessage[12] = ' '; timestampedMessage[13] = 0; @@ -368,7 +369,7 @@ static void getComputerDigest(m256i& digest) const unsigned long long size = digestIndex < contractCount ? contractDescriptions[digestIndex].stateSize : 0; if (!size) { - contractStateDigests[digestIndex] = _mm256_setzero_si256(); + contractStateDigests[digestIndex] = m256i::zero(); } else { @@ -695,7 +696,7 @@ static void processBroadcastFutureTickData(Peer* peer, RequestResponseHeader* he && request->tickData.minute <= 59 && request->tickData.second <= 59 && request->tickData.millisecond <= 999 - && ms(request->tickData.year, request->tickData.month, request->tickData.day, request->tickData.hour, request->tickData.minute, request->tickData.second, request->tickData.millisecond) <= ms(time.Year - 2000, time.Month, time.Day, time.Hour, time.Minute, time.Second, time.Nanosecond / 1000000) + TIME_ACCURACY) + && ms(request->tickData.year, request->tickData.month, request->tickData.day, request->tickData.hour, request->tickData.minute, request->tickData.second, request->tickData.millisecond) <= ms(utcTime.Year - 2000, utcTime.Month, utcTime.Day, utcTime.Hour, utcTime.Minute, utcTime.Second, utcTime.Nanosecond / 1000000) + TIME_ACCURACY) { bool ok = true; for (unsigned int i = 0; i < NUMBER_OF_TRANSACTIONS_PER_TICK && ok; i++) @@ -1189,7 +1190,7 @@ static void processSpecialCommand(Peer* peer, RequestResponseHeader* header) // set time SpecialCommandSendTime* _request = header->getPayload(); EFI_TIME newTime; - copyMem(&newTime, &_request->utcTime, sizeof(_request->utcTime)); // caution: response.utcTime is subset of time (smaller size) + copyMem(&newTime, &_request->utcTime, sizeof(_request->utcTime)); // caution: response.utcTime is subset of newTime (smaller size) newTime.TimeZone = 0; newTime.Daylight = 0; EFI_STATUS status = rs->SetTime(&newTime); @@ -1205,7 +1206,7 @@ static void processSpecialCommand(Peer* peer, RequestResponseHeader* header) SpecialCommandSendTime response; response.everIncreasingNonceAndCommandType = (request->everIncreasingNonceAndCommandType & 0xFFFFFFFFFFFFFF) | (SPECIAL_COMMAND_SEND_TIME << 56); updateTime(); - copyMem(&response.utcTime, &time, sizeof(response.utcTime)); // caution: response.utcTime is subset of time (smaller size) + copyMem(&response.utcTime, &utcTime, sizeof(response.utcTime)); // caution: response.utcTime is subset of global utcTime (smaller size) enqueueResponse(peer, sizeof(SpecialCommandSendTime), SpecialCommand::type, header->dejavu(), &response); } break; @@ -1239,10 +1240,10 @@ static void processSpecialCommand(Peer* peer, RequestResponseHeader* header) // a tracker to detect if a thread is crashed static void checkinTime(unsigned long long processorNumber) { - threadTimeCheckin[processorNumber].second = time.Second; - threadTimeCheckin[processorNumber].minute = time.Minute; - threadTimeCheckin[processorNumber].hour = time.Hour; - threadTimeCheckin[processorNumber].day = time.Day; + threadTimeCheckin[processorNumber].second = utcTime.Second; + threadTimeCheckin[processorNumber].minute = utcTime.Minute; + threadTimeCheckin[processorNumber].hour = utcTime.Hour; + threadTimeCheckin[processorNumber].day = utcTime.Day; } static void setNewMiningSeed() @@ -1250,9 +1251,14 @@ static void setNewMiningSeed() score->initMiningData(spectrumDigests[(SPECTRUM_CAPACITY * 2 - 1) - 1]); } +static unsigned int getTickInMiningPhaseCycle() +{ + return (system.tick - system.initialTick) % (INTERNAL_COMPUTATIONS_INTERVAL + EXTERNAL_COMPUTATIONS_INTERVAL); +} + static void checkAndSwitchMiningPhase() { - const unsigned int r = (system.tick - system.initialTick) % (INTERNAL_COMPUTATIONS_INTERVAL + EXTERNAL_COMPUTATIONS_INTERVAL); + const unsigned int r = getTickInMiningPhaseCycle(); if (!r) { setNewMiningSeed(); @@ -1261,7 +1267,7 @@ static void checkAndSwitchMiningPhase() { if (r == INTERNAL_COMPUTATIONS_INTERVAL + 3) // 3 is added because of 3-tick shift for transaction confirmation { - score->initMiningData(_mm256_setzero_si256()); + score->initMiningData(m256i::zero()); } } } @@ -1586,8 +1592,6 @@ long long QPI::QpiContextProcedureCall::burn(long long amount) const const Burning burning = { _currentContractId , amount }; logger.logBurning(burning); - - spectrumInfo.totalAmount -= amount; } return remainingAmount; @@ -1733,7 +1737,7 @@ m256i QPI::QpiContextFunctionCall::nextId(const m256i& currentId) const } } - return _mm256_setzero_si256(); + return m256i::zero(); } long long QPI::QpiContextFunctionCall::numberOfPossessedShares(unsigned long long assetName, const m256i& issuer, const m256i& owner, const m256i& possessor, unsigned short ownershipManagingContractIndex, unsigned short possessionManagingContractIndex) const @@ -2120,7 +2124,7 @@ static void processTickTransactionContractIPO(const Transaction* transaction, co const long long amount = contractIPOBid->price * contractIPOBid->quantity; if (decreaseEnergy(spectrumIndex, amount)) { - const QuTransfer quTransfer = { transaction->sourcePublicKey , _mm256_setzero_si256() , amount }; + const QuTransfer quTransfer = { transaction->sourcePublicKey, m256i::zero(), amount }; logger.logQuTransfer(quTransfer); numberOfReleasedEntities = 0; @@ -2190,7 +2194,7 @@ static void processTickTransactionContractIPO(const Transaction* transaction, co for (unsigned int i = 0; i < numberOfReleasedEntities; i++) { increaseEnergy(releasedPublicKeys[i], releasedAmounts[i]); - const QuTransfer quTransfer = { _mm256_setzero_si256() , releasedPublicKeys[i] , releasedAmounts[i] }; + const QuTransfer quTransfer = { m256i::zero(), releasedPublicKeys[i], releasedAmounts[i] }; logger.logQuTransfer(quTransfer); } } @@ -2234,8 +2238,10 @@ static void processTickTransactionSolution(const MiningSolutionTransaction* tran ASSERT(transaction != nullptr); ASSERT(transaction->checkValidity()); ASSERT(transaction->tick == system.tick); - ASSERT(transaction->destinationPublicKey == arbitratorPublicKey || isZero(transaction->destinationPublicKey)); - ASSERT(!transaction->amount && transaction->inputSize == 64 && !transaction->inputType); + ASSERT(isZero(transaction->destinationPublicKey)); + ASSERT(transaction->amount >=MiningSolutionTransaction::minAmount() + && transaction->inputSize == 64 + && transaction->inputType == MiningSolutionTransaction::transactionType()); m256i data[3] = { transaction->sourcePublicKey, transaction->miningSeed, transaction->nonce }; static_assert(sizeof(data) == 3 * 32, "Unexpected array size"); @@ -2254,11 +2260,11 @@ static void processTickTransactionSolution(const MiningSolutionTransaction* tran const int threshold = (system.epoch < MAX_NUMBER_EPOCH) ? solutionThreshold[system.epoch] : SOLUTION_THRESHOLD_DEFAULT; if (score->isGoodScore(solutionScore, threshold)) { - if (transaction->amount) // Remove this condition after the migration period + // Solution deposit return { increaseEnergy(transaction->sourcePublicKey, transaction->amount); - const QuTransfer quTransfer = { _mm256_setzero_si256(), transaction->sourcePublicKey, transaction->amount }; + const QuTransfer quTransfer = { m256i::zero(), transaction->sourcePublicKey, transaction->amount }; logger.logQuTransfer(quTransfer); } @@ -2554,20 +2560,6 @@ static void processTickTransaction(const Transaction* transaction, const m256i& moneyFlew = processTickTransactionContractProcedure(transaction, spectrumIndex, contractIndex); } } - else - { - // Other transactions - // TODO: Remove after the migration period - if (transaction->destinationPublicKey == arbitratorPublicKey) - { - if (!transaction->amount - && transaction->inputSize == 64 - && !transaction->inputType) - { - processTickTransactionSolution((MiningSolutionTransaction*)transaction, processorNumber); - } - } - } } } @@ -2665,9 +2657,10 @@ static void processTick(unsigned long long processorNumber) const int spectrumIndex = ::spectrumIndex(transaction->sourcePublicKey); if (spectrumIndex >= 0) { - if ((transaction->destinationPublicKey == arbitratorPublicKey && !transaction->amount && !transaction->inputType) || - (isZero(transaction->destinationPublicKey) && transaction->amount >= MiningSolutionTransaction::minAmount() - && transaction->inputType == MiningSolutionTransaction::transactionType())) + // Solution transactions + if (isZero(transaction->destinationPublicKey) + && transaction->amount >= MiningSolutionTransaction::minAmount() + && transaction->inputType == MiningSolutionTransaction::transactionType()) { if (transaction->inputSize == 32 + 32) { @@ -2777,13 +2770,13 @@ static void processTick(unsigned long long processorNumber) broadcastedFutureTickData.tickData.epoch = system.epoch; broadcastedFutureTickData.tickData.tick = system.tick + TICK_TRANSACTIONS_PUBLICATION_OFFSET; - broadcastedFutureTickData.tickData.millisecond = time.Nanosecond / 1000000; - broadcastedFutureTickData.tickData.second = time.Second; - broadcastedFutureTickData.tickData.minute = time.Minute; - broadcastedFutureTickData.tickData.hour = time.Hour; - broadcastedFutureTickData.tickData.day = time.Day; - broadcastedFutureTickData.tickData.month = time.Month; - broadcastedFutureTickData.tickData.year = time.Year - 2000; + broadcastedFutureTickData.tickData.millisecond = utcTime.Nanosecond / 1000000; + broadcastedFutureTickData.tickData.second = utcTime.Second; + broadcastedFutureTickData.tickData.minute = utcTime.Minute; + broadcastedFutureTickData.tickData.hour = utcTime.Hour; + broadcastedFutureTickData.tickData.day = utcTime.Day; + broadcastedFutureTickData.tickData.month = utcTime.Month; + broadcastedFutureTickData.tickData.year = utcTime.Year - 2000; m256i timelockPreimage[3]; static_assert(sizeof(timelockPreimage) == 3 * 32, "Unexpected array size"); @@ -2859,7 +2852,7 @@ static void processTick(unsigned long long processorNumber) for (; j < NUMBER_OF_TRANSACTIONS_PER_TICK; j++) { - broadcastedFutureTickData.tickData.transactionDigests[j] = _mm256_setzero_si256(); + broadcastedFutureTickData.tickData.transactionDigests[j] = m256i::zero(); } bs->SetMem(broadcastedFutureTickData.tickData.contractFees, sizeof(broadcastedFutureTickData.tickData.contractFees), 0); @@ -2887,7 +2880,7 @@ static void processTick(unsigned long long processorNumber) { auto& payload = voteCounterPayload; // note: not thread-safe payload.transaction.sourcePublicKey = computorPublicKeys[ownComputorIndicesMapping[i]]; - payload.transaction.destinationPublicKey = _mm256_setzero_si256(); + payload.transaction.destinationPublicKey = m256i::zero(); payload.transaction.amount = 0; payload.transaction.tick = system.tick + TICK_VOTE_COUNTER_PUBLICATION_OFFSET; payload.transaction.inputType = VOTE_COUNTER_INPUT_TYPE; @@ -2903,32 +2896,37 @@ static void processTick(unsigned long long processorNumber) if (mainAuxStatus & 1) { + // Publish solutions that were sent via BroadcastMessage as MiningSolutionTransaction for (unsigned int i = 0; i < sizeof(computorSeeds) / sizeof(computorSeeds[0]); i++) { int solutionIndexToPublish = -1; + // Select solution to publish as tx (and mark solutions as obsolete, whose mining seed does not match). + // Primarily, consider solutions of the computor i that were already selected for tx before. unsigned int j; for (j = 0; j < system.numberOfSolutions; j++) { + // solutionPublicationTicks[j] > 0 means the solution has already been selected for creating tx + // but has neither been RECORDED (successfully processed by tx) nor marked OBSOLETE (outdated mining seed) if (solutionPublicationTicks[j] > 0 && system.solutions[j].computorPublicKey == computorPublicKeys[i]) { + // Only consider this sol if the scheduled tick has passed already (tx not successful) if (solutionPublicationTicks[j] <= (int)system.tick) { if (system.solutions[j].miningSeed == score->currentRandomSeed) { solutionIndexToPublish = j; + break; } else { - // obsolete solution solutionPublicationTicks[j] = SOLUTION_OBSOLETE_FLAG; } } - - break; } } + // Secondarily, if no solution has been selected above, consider new solutions without previous tx if (j == system.numberOfSolutions) { for (j = 0; j < system.numberOfSolutions; j++) @@ -2936,15 +2934,29 @@ static void processTick(unsigned long long processorNumber) if (!solutionPublicationTicks[j] && system.solutions[j].computorPublicKey == computorPublicKeys[i]) { - solutionIndexToPublish = j; - - break; + if (system.solutions[j].miningSeed == score->currentRandomSeed) + { + solutionIndexToPublish = j; + break; + } + else + { + solutionPublicationTicks[j] = SOLUTION_OBSOLETE_FLAG; + } } } } if (solutionIndexToPublish >= 0) { + // Compute tick offset, when to publish solution + unsigned int publishingTickOffset = MIN_MINING_SOLUTIONS_PUBLICATION_OFFSET; + + // Do not publish, if the solution tx would end up after reset of mining seed, preventing loss of security deposit + if (getTickInMiningPhaseCycle() + publishingTickOffset >= INTERNAL_COMPUTATIONS_INTERVAL + 3) + continue; + + // Prepare, sign, and broadcast MiningSolutionTransaction struct { Transaction transaction; @@ -2954,12 +2966,10 @@ static void processTick(unsigned long long processorNumber) } payload; static_assert(sizeof(payload) == sizeof(Transaction) + 32 + 32 + SIGNATURE_SIZE, "Unexpected struct size!"); payload.transaction.sourcePublicKey = computorPublicKeys[i]; - payload.transaction.destinationPublicKey = arbitratorPublicKey; - payload.transaction.amount = 0; - unsigned int random; - _rdrand32_step(&random); - solutionPublicationTicks[solutionIndexToPublish] = payload.transaction.tick = system.tick + MIN_MINING_SOLUTIONS_PUBLICATION_OFFSET + random % MIN_MINING_SOLUTIONS_PUBLICATION_OFFSET; - payload.transaction.inputType = 0; + payload.transaction.destinationPublicKey = m256i::zero(); + payload.transaction.amount = MiningSolutionTransaction::minAmount(); + solutionPublicationTicks[solutionIndexToPublish] = payload.transaction.tick = system.tick + publishingTickOffset; + payload.transaction.inputType = MiningSolutionTransaction::transactionType(); payload.transaction.inputSize = sizeof(payload.miningSeed) + sizeof(payload.nonce); payload.miningSeed = system.solutions[solutionIndexToPublish].miningSeed; payload.nonce = system.solutions[solutionIndexToPublish].nonce; @@ -3126,7 +3136,7 @@ static void endEpoch() int issuanceIndex, ownershipIndex, possessionIndex; if (finalPrice) { - if (!issueAsset(_mm256_setzero_si256(), (char*)contractDescriptions[contractIndex].assetName, 0, CONTRACT_ASSET_UNIT_OF_MEASUREMENT, NUMBER_OF_COMPUTORS, QX_CONTRACT_INDEX, &issuanceIndex, &ownershipIndex, &possessionIndex)) + if (!issueAsset(m256i::zero(), (char*)contractDescriptions[contractIndex].assetName, 0, CONTRACT_ASSET_UNIT_OF_MEASUREMENT, NUMBER_OF_COMPUTORS, QX_CONTRACT_INDEX, &issuanceIndex, &ownershipIndex, &possessionIndex)) { finalPrice = 0; } @@ -3163,7 +3173,7 @@ static void endEpoch() for (unsigned int i = 0; i < numberOfReleasedEntities; i++) { increaseEnergy(releasedPublicKeys[i], releasedAmounts[i]); - const QuTransfer quTransfer = { _mm256_setzero_si256() , releasedPublicKeys[i] , releasedAmounts[i] }; + const QuTransfer quTransfer = { m256i::zero(), releasedPublicKeys[i], releasedAmounts[i] }; logger.logQuTransfer(quTransfer); } contractStateLock[contractIndex].releaseRead(); @@ -3300,7 +3310,7 @@ static void endEpoch() increaseEnergy(rdEntry.destinationPublicKey, donation); if (revenue) { - const QuTransfer quTransfer = { _mm256_setzero_si256(), rdEntry.destinationPublicKey, donation }; + const QuTransfer quTransfer = { m256i::zero(), rdEntry.destinationPublicKey, donation }; logger.logQuTransfer(quTransfer); } } @@ -3310,7 +3320,7 @@ static void endEpoch() increaseEnergy(broadcastedComputors.computors.publicKeys[computorIndex], revenue); if (revenue) { - const QuTransfer quTransfer = { _mm256_setzero_si256() , broadcastedComputors.computors.publicKeys[computorIndex] , revenue }; + const QuTransfer quTransfer = { m256i::zero(), broadcastedComputors.computors.publicKeys[computorIndex], revenue }; logger.logQuTransfer(quTransfer); } } @@ -3318,7 +3328,7 @@ static void endEpoch() // Generate arbitrator revenue increaseEnergy((unsigned char*)&arbitratorPublicKey, arbitratorRevenue); - const QuTransfer quTransfer = { _mm256_setzero_si256() , arbitratorPublicKey , arbitratorRevenue }; + const QuTransfer quTransfer = { m256i::zero(), arbitratorPublicKey, arbitratorRevenue }; logger.logQuTransfer(quTransfer); } @@ -3477,7 +3487,7 @@ static bool saveAllNodeStates() appendText(message, directory); appendText(message, L"/"); appendText(message, SPECTRUM_FILE_NAME); logToConsole(message); - if (!saveSpectrum(directory)) + if (!saveSpectrum(SPECTRUM_FILE_NAME, directory)) { logToConsole(L"Failed to save spectrum"); return false; @@ -3623,7 +3633,7 @@ static bool loadAllNodeStates() SPECTRUM_FILE_NAME[sizeof(SPECTRUM_FILE_NAME) / sizeof(SPECTRUM_FILE_NAME[0]) - 4] = L'0'; SPECTRUM_FILE_NAME[sizeof(SPECTRUM_FILE_NAME) / sizeof(SPECTRUM_FILE_NAME[0]) - 3] = L'0'; SPECTRUM_FILE_NAME[sizeof(SPECTRUM_FILE_NAME) / sizeof(SPECTRUM_FILE_NAME[0]) - 2] = L'0'; - if (!loadSpectrum(directory)) + if (!loadSpectrum(SPECTRUM_FILE_NAME, directory)) { logToConsole(L"Failed to load spectrum"); return false; @@ -3859,7 +3869,7 @@ static void tickProcessor(void*) || uniqueNextTickTransactionDigestCounters[mostPopularUniqueNextTickTransactionDigestIndex] + (NUMBER_OF_COMPUTORS - totalUniqueNextTickTransactionDigestCounter) < QUORUM) { // Create empty tick - targetNextTickDataDigest = _mm256_setzero_si256(); + targetNextTickDataDigest = m256i::zero(); targetNextTickDataDigestIsKnown = true; } } @@ -3919,7 +3929,7 @@ static void tickProcessor(void*) if (numberOfEmptyNextTickTransactionDigest > NUMBER_OF_COMPUTORS - QUORUM || uniqueNextTickTransactionDigestCounters[mostPopularUniqueNextTickTransactionDigestIndex] + (NUMBER_OF_COMPUTORS - totalUniqueNextTickTransactionDigestCounter) < QUORUM) { - targetNextTickDataDigest = _mm256_setzero_si256(); + targetNextTickDataDigest = m256i::zero(); targetNextTickDataDigestIsKnown = true; } } @@ -4012,7 +4022,7 @@ static void tickProcessor(void*) nextTickData.epoch = 0; setMem(nextTickData.transactionDigests, NUMBER_OF_TRANSACTIONS_PER_TICK * sizeof(m256i), 0); // first and second tick of an epoch are always empty tick - targetNextTickDataDigest = _mm256_setzero_si256(); + targetNextTickDataDigest = m256i::zero(); targetNextTickDataDigestIsKnown = true; tickDataSuits = true; } @@ -4199,7 +4209,7 @@ static void tickProcessor(void*) } else { - etalonTick.transactionDigest = _mm256_setzero_si256(); + etalonTick.transactionDigest = m256i::zero(); } if (nextTickData.epoch == system.epoch) @@ -4211,7 +4221,7 @@ static void tickProcessor(void*) } else { - etalonTick.expectedNextTickTransactionDigest = _mm256_setzero_si256(); + etalonTick.expectedNextTickTransactionDigest = m256i::zero(); } if (system.tick > system.latestCreatedTick || system.tick == system.initialTick) @@ -4346,7 +4356,7 @@ static void tickProcessor(void*) if (forceNextTick) { - targetNextTickDataDigest = _mm256_setzero_si256(); + targetNextTickDataDigest = m256i::zero(); targetNextTickDataDigestIsKnown = true; } } @@ -5366,8 +5376,8 @@ static void logHealthStatus() for (int i = 0; i < nTickProcessorIDs; i++) { unsigned long long tid = tickProcessorIDs[i]; - long long diffInSecond = 86400 * (time.Day - threadTimeCheckin[tid].day) + 3600 * (time.Hour - threadTimeCheckin[tid].hour) - + 60 * (time.Minute - threadTimeCheckin[tid].minute) + (time.Second - threadTimeCheckin[tid].second); + long long diffInSecond = 86400 * (utcTime.Day - threadTimeCheckin[tid].day) + 3600 * (utcTime.Hour - threadTimeCheckin[tid].hour) + + 60 * (utcTime.Minute - threadTimeCheckin[tid].minute) + (utcTime.Second - threadTimeCheckin[tid].second); if (diffInSecond > 120) // if they don't check in in 2 minutes, we can assume the thread is already crashed { allThreadsAreGood = false; @@ -5380,8 +5390,8 @@ static void logHealthStatus() for (int i = 0; i < nRequestProcessorIDs; i++) { unsigned long long tid = requestProcessorIDs[i]; - long long diffInSecond = 86400 * (time.Day - threadTimeCheckin[tid].day) + 3600 * (time.Hour - threadTimeCheckin[tid].hour) - + 60 * (time.Minute - threadTimeCheckin[tid].minute) + (time.Second - threadTimeCheckin[tid].second); + long long diffInSecond = 86400 * (utcTime.Day - threadTimeCheckin[tid].day) + 3600 * (utcTime.Hour - threadTimeCheckin[tid].hour) + + 60 * (utcTime.Minute - threadTimeCheckin[tid].minute) + (utcTime.Second - threadTimeCheckin[tid].second); if (diffInSecond > 120) // if they don't check in in 2 minutes, we can assume the thread is already crashed { allThreadsAreGood = false; diff --git a/src/spectrum.h b/src/spectrum.h index 45c43cdb..dbe8f372 100644 --- a/src/spectrum.h +++ b/src/spectrum.h @@ -4,9 +4,12 @@ #include "platform/concurrency.h" #include "platform/file_io.h" #include "platform/time_stamp_counter.h" +#include "platform/memory.h" #include "network_messages/entity.h" +#include "logging/logging.h" + #include "public_settings.h" #include "system.h" #include "kangaroo_twelve.h" @@ -91,6 +94,51 @@ void updateAndAnalzeEntityCategoryPopulations() } } +void logSpectrumStats() +{ + SpectrumStats spectrumStats; + spectrumStats.totalAmount = spectrumInfo.totalAmount; + spectrumStats.dustThresholdBurnAll = dustThresholdBurnAll; + spectrumStats.dustThresholdBurnHalf = dustThresholdBurnHalf; + spectrumStats.numberOfEntities = spectrumInfo.numberOfEntities; + copyMem(spectrumStats.entityCategoryPopulations, entityCategoryPopulations, sizeof(entityCategoryPopulations)); + + logger.logSpectrumStats(spectrumStats); +} + +// Build and log variable-size DustBurning log message. +// Assumes to be used tick processor or contract processor only, so can use reorgBuffer. +struct DustBurnLogger +{ + DustBurnLogger() + { + buf = (DustBurning*)reorgBuffer; + buf->numberOfBurns = 0; + } + + // Add burned amount of of entity, may send buffered message to logging. + void addDustBurn(const m256i& publicKey, unsigned long long amount) + { + DustBurning::Entity& e = buf->entity(buf->numberOfBurns++); + e.publicKey = publicKey; + e.amount = amount; + + if (buf->numberOfBurns == 1000) // TODO: better use 0xffff (when proved to be stable with logging) + finished(); + } + + // Send buffered message to logging + void finished() + { + logger.logDustBurning(buf); + buf->numberOfBurns = 0; + } + + // send to log when max size is full or finished +private: + DustBurning* buf; +}; + // Clean up spectrum hash map, removing all entities with balance 0. Updates spectrumInfo. static void reorganizeSpectrum() { @@ -182,7 +230,7 @@ static long long energy(const int index) return spectrum[index].incomingAmount - spectrum[index].outgoingAmount; } -// Increase balance of entity. Does not update spectrumInfo.totalAmount. +// Increase balance of entity. static void increaseEnergy(const m256i& publicKey, long long amount) { if (!isZero(publicKey) && amount >= 0) @@ -191,10 +239,17 @@ static void increaseEnergy(const m256i& publicKey, long long amount) ACQUIRE(spectrumLock); + // Anti-dust feature: prevent that spectrum fills to more than 75% of capacity to keep hash map lookup fast if (spectrumInfo.numberOfEntities >= (SPECTRUM_CAPACITY / 2) + (SPECTRUM_CAPACITY / 4)) { - // Update anti-dust burn thresholds + // Update anti-dust burn thresholds (and log spectrum stats before burning) updateAndAnalzeEntityCategoryPopulations(); +#if LOG_SPECTRUM_STATS + logSpectrumStats(); +#endif +#if LOG_DUST_BURNINGS + DustBurnLogger dbl; +#endif if (dustThresholdBurnAll > 0) { @@ -205,6 +260,9 @@ static void increaseEnergy(const m256i& publicKey, long long amount) if (balance <= dustThresholdBurnAll && balance) { spectrum[i].outgoingAmount = spectrum[i].incomingAmount; +#if LOG_DUST_BURNINGS + dbl.addDustBurn(spectrum[i].publicKey, balance); +#endif } } } @@ -221,18 +279,27 @@ static void increaseEnergy(const m256i& publicKey, long long amount) if (++countBurnCanadiates & 1) { spectrum[i].outgoingAmount = spectrum[i].incomingAmount; +#if LOG_DUST_BURNINGS + dbl.addDustBurn(spectrum[i].publicKey, balance); +#endif } } } } +#if LOG_DUST_BURNINGS + // Finished dust burning (pass message to log) + dbl.finished(); +#endif + // Remove entries with balance zero from hash map reorganizeSpectrum(); - // Correct total amount (spectrum info has been recomputed before increasing energy; - // in transfer case energy has been decreased before and total amount is not changed - // without anti-dust burning) - spectrumInfo.totalAmount += amount; +#if LOG_SPECTRUM_STATS + // Log spectrum stats after burning (before increasing energy / potenitally creating entity) + updateAndAnalzeEntityCategoryPopulations(); + logSpectrumStats(); +#endif } iteration: @@ -241,6 +308,8 @@ static void increaseEnergy(const m256i& publicKey, long long amount) spectrum[index].incomingAmount += amount; spectrum[index].numberOfIncomingTransfers++; spectrum[index].latestIncomingTransferTick = system.tick; + + spectrumInfo.totalAmount += amount; } else { @@ -252,6 +321,17 @@ static void increaseEnergy(const m256i& publicKey, long long amount) spectrum[index].latestIncomingTransferTick = system.tick; spectrumInfo.numberOfEntities++; + spectrumInfo.totalAmount += amount; + +#if LOG_SPECTRUM_STATS + if ((spectrumInfo.numberOfEntities & 0x7ffff) == 1) + { + // Log spectrum stats when the number of entities hits the next half million + // (== 1 is to avoid duplicate when anti-dust is triggered) + updateAndAnalzeEntityCategoryPopulations(); + logSpectrumStats(); + } +#endif } else { @@ -265,7 +345,7 @@ static void increaseEnergy(const m256i& publicKey, long long amount) } } -// Decrease balance of entity if it is high enough. Does not update spectrumInfo.totalAmount. +// Decrease balance of entity if it is high enough. static bool decreaseEnergy(const int index, long long amount) { if (amount >= 0) @@ -278,6 +358,8 @@ static bool decreaseEnergy(const int index, long long amount) spectrum[index].numberOfOutgoingTransfers++; spectrum[index].latestOutgoingTransferTick = system.tick; + spectrumInfo.totalAmount -= amount; + RELEASE(spectrumLock); return true; diff --git a/test/m256.cpp b/test/m256.cpp index 6c8dcdc9..62114ba3 100644 --- a/test/m256.cpp +++ b/test/m256.cpp @@ -130,6 +130,26 @@ TEST(TestCore256BitClass, ConstructAssignCompare) { for (int j = 0; j < 32; ++j) EXPECT_EQ(v1.m256i_u8[j], 7 + i + j); } + + // m256i::zero() + EXPECT_FALSE(isZero(v1)); + EXPECT_FALSE(v1 == m256i::zero()); + EXPECT_TRUE(v1 != m256i::zero()); + v1 = m256i::zero(); + EXPECT_TRUE(isZero(v1)); + EXPECT_TRUE(v1 == m256i::zero()); + EXPECT_FALSE(v1 != m256i::zero()); + + // 4 x u64 constructor + m256i v7(1234, 5678, 9012, 3456); + EXPECT_EQ(v7.u64._0, 1234); + EXPECT_EQ(v7.u64._1, 5678); + EXPECT_EQ(v7.u64._2, 9012); + EXPECT_EQ(v7.u64._3, 3456); + EXPECT_EQ(v7.m256i_u64[0], 1234); + EXPECT_EQ(v7.m256i_u64[1], 5678); + EXPECT_EQ(v7.m256i_u64[2], 9012); + EXPECT_EQ(v7.m256i_u64[3], 3456); } TEST(TestCore256BitFunctionsIntrinsicType, isZero) { @@ -162,7 +182,7 @@ TEST(TestCore256BitFunctionsIntrinsicType, isZeroPerformance) // measure comparison with existing zero m256i t0 = std::chrono::high_resolution_clock::now(); - m256i zero = _mm256_setzero_si256(); + m256i zero = m256i::zero(); for (int i = 0; i < N; ++i) { optimizeBarrierValue.i64._0 = i; @@ -180,7 +200,7 @@ TEST(TestCore256BitFunctionsIntrinsicType, isZeroPerformance) { optimizeBarrierValue.i64._0 = i; value = optimizeBarrierValue; - zeroVol = _mm256_setzero_si256(); + zeroVol = m256i::zero(); optimizeBarrierResult = (value == zeroVol); } t1 = std::chrono::high_resolution_clock::now(); diff --git a/test/spectrum.cpp b/test/spectrum.cpp index f17e120d..aacf4bbf 100644 --- a/test/spectrum.cpp +++ b/test/spectrum.cpp @@ -2,8 +2,15 @@ #include "gtest/gtest.h" +// workaround for name clash with stdlib #define system qubicSystemStruct +// enable some logging for testing +#include "../src/private_settings.h" +#define LOG_DUST_BURNINGS 1 +#define LOG_SPECTRUM_STATS 1 +// TODO: use smaller logging buffer here than in core + #include "../src/spectrum.h" #include @@ -132,10 +139,12 @@ struct SpectrumTest system.tick = 15700000; clearSpectrum(); antiDustCornerCase = false; + EXPECT_TRUE(logger.initLogging()); } ~SpectrumTest() { + logger.deinitLogging(); deinitSpectrum(); deinitCommonBuffers(); } @@ -161,6 +170,8 @@ struct SpectrumTest void afterAntiDust() { + checkAndGetInfo(); + // Print anti-dust info auto duration_ms = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - beforeAntiDustTimestamp); std::cout << "Transfer with anti-dust took " << duration_ms << " ms: entities " @@ -236,7 +247,6 @@ TEST(TestCoreSpectrum, AntiDustOneRichRandomDust) // Create spectrum with one rich ID SpectrumTest test; increaseEnergy(m256i::randomValue(), 1000000000000llu); - spectrumInfo.totalAmount += 1000000000000llu; test.dust_attack(1, 1, 1); test.dust_attack(100, 100, 1); @@ -250,7 +260,6 @@ TEST(TestCoreSpectrum, AntiDustManyRichRandomDust) for (int i = 0; i < 10000; i++) { increaseEnergy(m256i::randomValue(), i * 100000llu); - spectrumInfo.totalAmount += i * 100000llu; } test.dust_attack(1, 1000, 1); @@ -265,7 +274,6 @@ TEST(TestCoreSpectrum, AntiDustEdgeCaseAllInSameBin) for (unsigned long long i = 0; i < (SPECTRUM_CAPACITY / 2 + SPECTRUM_CAPACITY / 4); ++i) { increaseEnergy(m256i(i, 1, 2, 3), 100llu); - spectrumInfo.totalAmount += 100llu; } test.beforeAntiDust(); @@ -273,10 +281,27 @@ TEST(TestCoreSpectrum, AntiDustEdgeCaseAllInSameBin) test.afterAntiDust(); } -TEST(TestCoreSpectrum, AntiDustEdgeCaseHugeBins) +SpectrumStats* getSpectrumStatsLog(long long id) +{ + qLogger::BlobInfo bi = logger.logBuf.getBlobInfo(id); + EXPECT_EQ(bi.length, LOG_HEADER_SIZE + sizeof(SpectrumStats)); + return reinterpret_cast(logger.logBuffer + bi.startIndex + LOG_HEADER_SIZE); +} + +DustBurning* getDustBurningLog(long long id) +{ + qLogger::BlobInfo bi = logger.logBuf.getBlobInfo(id); + DustBurning* db = reinterpret_cast(logger.logBuffer + bi.startIndex + LOG_HEADER_SIZE); + EXPECT_EQ(bi.length, LOG_HEADER_SIZE + db->messageSize()); + return db; +} + +TEST(TestCoreSpectrum, AntiDustEdgeCaseHugeBinsAndLogging) { SpectrumTest test; test.antiDustCornerCase = true; + + // build-up spectrum for (unsigned long long i = 0; i < (SPECTRUM_CAPACITY / 2 + SPECTRUM_CAPACITY / 4); ++i) { unsigned long long amount; @@ -285,11 +310,74 @@ TEST(TestCoreSpectrum, AntiDustEdgeCaseHugeBins) else if (i < SPECTRUM_CAPACITY / 2 + SPECTRUM_CAPACITY / 4) amount = 10000; increaseEnergy(m256i(i, 1, 2, 3), amount); - spectrumInfo.totalAmount += amount; } + + // test anti-dust test.beforeAntiDust(); - increaseEnergy(m256i::randomValue(), 1000llu); + increaseEnergy(m256i(SPECTRUM_CAPACITY - 1, 1, 2, 3), 1000llu); test.afterAntiDust(); + + // check logs: + // first 24 are from building up spectrum + SpectrumStats* stats; + for (int i = 0; i < 24; ++i) + { + stats = getSpectrumStatsLog(i); + EXPECT_EQ(stats->numberOfEntities, i * 524288 + 1); + EXPECT_EQ(stats->entityCategoryPopulations[6], std::min(i * 524288 + 1, int(SPECTRUM_CAPACITY / 4))); + EXPECT_EQ(stats->entityCategoryPopulations[13], (i < 8) ? 0 : (i - 8) * 524288 + 1); + EXPECT_EQ(stats->totalAmount, stats->entityCategoryPopulations[6] * 100llu + stats->entityCategoryPopulations[13] * 10000llu); + + if (i < 16) + { + EXPECT_EQ(stats->dustThresholdBurnAll, 0); + EXPECT_EQ(stats->dustThresholdBurnHalf, 0); + } + else + { + EXPECT_EQ(stats->dustThresholdBurnAll, (2 << 6) - 1); + EXPECT_EQ(stats->dustThresholdBurnHalf, 0); + } + } + + // Check state before anti-dust + SpectrumStats* beforeAntidustStats = getSpectrumStatsLog(24); + EXPECT_EQ(beforeAntidustStats->numberOfEntities, 24 * 524288); + EXPECT_EQ(beforeAntidustStats->entityCategoryPopulations[6], SPECTRUM_CAPACITY / 4); + EXPECT_EQ(beforeAntidustStats->entityCategoryPopulations[13], SPECTRUM_CAPACITY / 2); + EXPECT_EQ(beforeAntidustStats->totalAmount, beforeAntidustStats->entityCategoryPopulations[6] * 100llu + beforeAntidustStats->entityCategoryPopulations[13] * 10000llu); + EXPECT_EQ(beforeAntidustStats->dustThresholdBurnAll, (2 << 12) - 1); + EXPECT_EQ(beforeAntidustStats->dustThresholdBurnHalf, (2 << 13) - 1); + + // Check dust burning log messages + int balancesBurned = 0; + int logId = 25; + while (balancesBurned < 8 * 1048576) + { + DustBurning* db = getDustBurningLog(logId); + for (int i = 0; i < db->numberOfBurns; ++i) + { + // Of the first 4M entities, all are burned (amount 100), of the following every second is burned. + unsigned long long expectedSpectrumIndex = balancesBurned; + if (balancesBurned >= 4194304) + expectedSpectrumIndex = (balancesBurned - 4194304) * 2 + 4194304; + + DustBurning::Entity& e = db->entity(i); + EXPECT_EQ(e.publicKey, m256i(expectedSpectrumIndex, 1, 2, 3)); + EXPECT_EQ(e.amount, (balancesBurned < 4194304) ? 100 : 10000); + ++balancesBurned; + } + ++logId; + } + + // Finally, check state logged after dust burning (logged before increaing energy / adding new entity) + SpectrumStats* afterAntidustStats = getSpectrumStatsLog(logId); + EXPECT_EQ(afterAntidustStats->numberOfEntities, 4194304); + EXPECT_EQ(afterAntidustStats->entityCategoryPopulations[9], 0); + EXPECT_EQ(afterAntidustStats->entityCategoryPopulations[13], 4 * 1048576); + EXPECT_EQ(afterAntidustStats->totalAmount, afterAntidustStats->entityCategoryPopulations[13] * 10000llu); + EXPECT_EQ(afterAntidustStats->dustThresholdBurnAll, 0); + EXPECT_EQ(afterAntidustStats->dustThresholdBurnHalf, 0); } TEST(TestCoreSpectrum, AntiDustEdgeCaseHugeBinZeroBalance) @@ -298,7 +386,6 @@ TEST(TestCoreSpectrum, AntiDustEdgeCaseHugeBinZeroBalance) m256i richId(123, 4, 5, 6); unsigned long long amount = 1000; increaseEnergy(richId, 100 * amount); - spectrumInfo.totalAmount += 100 * amount; unsigned int spectrum75pct = (SPECTRUM_CAPACITY / 2 + SPECTRUM_CAPACITY / 4); for (unsigned long long i = 0; i < spectrum75pct - 1; ++i) {