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)
{