diff --git a/src/Qubic.vcxproj b/src/Qubic.vcxproj index 27c41c20..63046690 100644 --- a/src/Qubic.vcxproj +++ b/src/Qubic.vcxproj @@ -23,6 +23,7 @@ + diff --git a/src/Qubic.vcxproj.filters b/src/Qubic.vcxproj.filters index 94918128..2e3899a1 100644 --- a/src/Qubic.vcxproj.filters +++ b/src/Qubic.vcxproj.filters @@ -116,7 +116,7 @@ contracts - contracts + contracts contract_core @@ -211,6 +211,9 @@ platform + + contracts + diff --git a/src/assets/assets.h b/src/assets/assets.h index 96b135e0..e693cfaa 100644 --- a/src/assets/assets.h +++ b/src/assets/assets.h @@ -28,7 +28,7 @@ // TODO: move this into AssetStorage class GLOBAL_VAR_DECL volatile char universeLock GLOBAL_VAR_INIT(0); -GLOBAL_VAR_DECL Asset* assets GLOBAL_VAR_INIT(nullptr); +GLOBAL_VAR_DECL AssetRecord* assets GLOBAL_VAR_INIT(nullptr); GLOBAL_VAR_DECL m256i* assetDigests GLOBAL_VAR_INIT(nullptr); static constexpr unsigned long long assetDigestsSizeInBytes = (ASSETS_CAPACITY * 2 - 1) * 32ULL; GLOBAL_VAR_DECL unsigned long long* assetChangeFlags GLOBAL_VAR_INIT(nullptr); @@ -159,7 +159,7 @@ static unsigned int issuanceIndex(const m256i& issuer, unsigned long long assetN static bool initAssets() { - if (!allocatePool(ASSETS_CAPACITY * sizeof(Asset), (void**)&assets) + if (!allocatePool(ASSETS_CAPACITY * sizeof(AssetRecord), (void**)&assets) || !allocatePool(assetDigestsSizeInBytes, (void**)&assetDigests) || !allocatePool(ASSETS_CAPACITY / 8, (void**)&assetChangeFlags)) { @@ -276,7 +276,7 @@ static long long issueAsset(const m256i& issuerPublicKey, const char name[7], ch } static sint64 numberOfShares( - const AssetIssuanceId& issuanceId, + const Asset& issuanceId, const AssetOwnershipSelect& ownership = AssetOwnershipSelect::any(), const AssetPossessionSelect& possession = AssetPossessionSelect::any()) { @@ -505,7 +505,7 @@ static void getUniverseDigest(m256i& digest) { if (assetChangeFlags[digestIndex >> 6] & (1ULL << (digestIndex & 63))) { - KangarooTwelve(&assets[digestIndex], sizeof(Asset), &assetDigests[digestIndex], 32); + KangarooTwelve(&assets[digestIndex], sizeof(AssetRecord), &assetDigests[digestIndex], 32); } } unsigned int previousLevelBeginning = 0; @@ -538,10 +538,10 @@ static bool saveUniverse(const CHAR16* fileName = UNIVERSE_FILE_NAME, const CHAR const unsigned long long beginningTick = __rdtsc(); ACQUIRE(universeLock); - long long savedSize = save(fileName, ASSETS_CAPACITY * sizeof(Asset), (unsigned char*)assets, directory); + long long savedSize = save(fileName, ASSETS_CAPACITY * sizeof(AssetRecord), (unsigned char*)assets, directory); RELEASE(universeLock); - if (savedSize == ASSETS_CAPACITY * sizeof(Asset)) + if (savedSize == ASSETS_CAPACITY * sizeof(AssetRecord)) { setNumber(message, savedSize, TRUE); appendText(message, L" bytes of the universe data are saved ("); @@ -555,8 +555,8 @@ static bool saveUniverse(const CHAR16* fileName = UNIVERSE_FILE_NAME, const CHAR static bool loadUniverse(const CHAR16* fileName = UNIVERSE_FILE_NAME, CHAR16* directory = NULL) { - long long loadedSize = load(fileName, ASSETS_CAPACITY * sizeof(Asset), (unsigned char*)assets, directory); - if (loadedSize != ASSETS_CAPACITY * sizeof(Asset)) + long long loadedSize = load(fileName, ASSETS_CAPACITY * sizeof(AssetRecord), (unsigned char*)assets, directory); + if (loadedSize != ASSETS_CAPACITY * sizeof(AssetRecord)) { logStatusToConsole(L"EFI_FILE_PROTOCOL.Read() reads invalid number of bytes", loadedSize, __LINE__); @@ -571,8 +571,8 @@ static void assetsEndEpoch() ACQUIRE(universeLock); // rebuild asset hash map, getting rid of all elements with zero shares - Asset* reorgAssets = (Asset*)reorgBuffer; - setMem(reorgAssets, ASSETS_CAPACITY * sizeof(Asset), 0); + AssetRecord* reorgAssets = (AssetRecord*)reorgBuffer; + setMem(reorgAssets, ASSETS_CAPACITY * sizeof(AssetRecord), 0); for (unsigned int i = 0; i < ASSETS_CAPACITY; i++) { if (assets[i].varStruct.possession.type == POSSESSION @@ -591,7 +591,7 @@ static void assetsEndEpoch() { if (reorgAssets[issuanceIndex].varStruct.issuance.type == EMPTY) { - copyMem(&reorgAssets[issuanceIndex], &assets[oldIssuanceIndex], sizeof(Asset)); + copyMem(&reorgAssets[issuanceIndex], &assets[oldIssuanceIndex], sizeof(AssetRecord)); } const m256i& ownerPublicKey = assets[oldOwnershipIndex].varStruct.ownership.publicKey; @@ -651,7 +651,7 @@ static void assetsEndEpoch() } } } - copyMem(assets, reorgAssets, ASSETS_CAPACITY * sizeof(Asset)); + copyMem(assets, reorgAssets, ASSETS_CAPACITY * sizeof(AssetRecord)); setMem(assetChangeFlags, ASSETS_CAPACITY / 8, 0xFF); diff --git a/src/assets/net_msg_impl.h b/src/assets/net_msg_impl.h index 2cbcb8b6..96a6a1c4 100644 --- a/src/assets/net_msg_impl.h +++ b/src/assets/net_msg_impl.h @@ -25,7 +25,7 @@ static void processRequestIssuedAssets(Peer* peer, RequestResponseHeader* header if (assets[universeIndex].varStruct.issuance.type == ISSUANCE && assets[universeIndex].varStruct.issuance.publicKey == request->publicKey) { - bs->CopyMem(&response.asset, &assets[universeIndex], sizeof(Asset)); + bs->CopyMem(&response.asset, &assets[universeIndex], sizeof(AssetRecord)); response.tick = system.tick; response.universeIndex = universeIndex; getSiblings(response.universeIndex, assetDigests, response.siblings); @@ -62,8 +62,8 @@ static void processRequestOwnedAssets(Peer* peer, RequestResponseHeader* header) if (assets[universeIndex].varStruct.issuance.type == OWNERSHIP && assets[universeIndex].varStruct.issuance.publicKey == request->publicKey) { - bs->CopyMem(&response.asset, &assets[universeIndex], sizeof(Asset)); - bs->CopyMem(&response.issuanceAsset, &assets[assets[universeIndex].varStruct.ownership.issuanceIndex], sizeof(Asset)); + bs->CopyMem(&response.asset, &assets[universeIndex], sizeof(AssetRecord)); + bs->CopyMem(&response.issuanceAsset, &assets[assets[universeIndex].varStruct.ownership.issuanceIndex], sizeof(AssetRecord)); response.tick = system.tick; response.universeIndex = universeIndex; getSiblings(response.universeIndex, assetDigests, response.siblings); @@ -100,9 +100,9 @@ static void processRequestPossessedAssets(Peer* peer, RequestResponseHeader* hea if (assets[universeIndex].varStruct.issuance.type == POSSESSION && assets[universeIndex].varStruct.issuance.publicKey == request->publicKey) { - bs->CopyMem(&response.asset, &assets[universeIndex], sizeof(Asset)); - bs->CopyMem(&response.ownershipAsset, &assets[assets[universeIndex].varStruct.possession.ownershipIndex], sizeof(Asset)); - bs->CopyMem(&response.issuanceAsset, &assets[assets[assets[universeIndex].varStruct.possession.ownershipIndex].varStruct.ownership.issuanceIndex], sizeof(Asset)); + bs->CopyMem(&response.asset, &assets[universeIndex], sizeof(AssetRecord)); + bs->CopyMem(&response.ownershipAsset, &assets[assets[universeIndex].varStruct.possession.ownershipIndex], sizeof(AssetRecord)); + bs->CopyMem(&response.issuanceAsset, &assets[assets[assets[universeIndex].varStruct.possession.ownershipIndex].varStruct.ownership.issuanceIndex], sizeof(AssetRecord)); response.tick = system.tick; response.universeIndex = universeIndex; getSiblings(response.universeIndex, assetDigests, response.siblings); diff --git a/src/common_buffers.h b/src/common_buffers.h index 89eea93d..4868c83d 100644 --- a/src/common_buffers.h +++ b/src/common_buffers.h @@ -8,7 +8,7 @@ constexpr unsigned long long spectrumSizeInBytes = SPECTRUM_CAPACITY * sizeof(::Entity); -constexpr unsigned long long universeSizeInBytes = ASSETS_CAPACITY * sizeof(Asset); +constexpr unsigned long long universeSizeInBytes = ASSETS_CAPACITY * sizeof(AssetRecord); // Buffer used for reorganizing spectrum and universe hash maps, currently also used as scratchpad buffer for contracts // Must be large enough to fit any contract, full spectrum, and full universe! diff --git a/src/contract_core/contract_def.h b/src/contract_core/contract_def.h index 9826c336..d022bba6 100644 --- a/src/contract_core/contract_def.h +++ b/src/contract_core/contract_def.h @@ -149,7 +149,11 @@ struct __FunctionOrProcedureBeginEndGuard #define CONTRACT_INDEX QEARN_CONTRACT_INDEX #define CONTRACT_STATE_TYPE QEARN #define CONTRACT_STATE2_TYPE QEARN2 +#ifdef QEARN_UPDATE #include "contracts/Qearn.h" +#else +#include "contracts/Qearn_old.h" +#endif #undef CONTRACT_INDEX #undef CONTRACT_STATE_TYPE diff --git a/src/contract_core/qpi_asset_impl.h b/src/contract_core/qpi_asset_impl.h index 462d4456..2887c93f 100644 --- a/src/contract_core/qpi_asset_impl.h +++ b/src/contract_core/qpi_asset_impl.h @@ -7,7 +7,7 @@ // Start iteration with given issuance and given ownership filter (selects first record). -void QPI::AssetOwnershipIterator::begin(const QPI::AssetIssuanceId& issuance, const QPI::AssetOwnershipSelect& ownership) +void QPI::AssetOwnershipIterator::begin(const QPI::Asset& issuance, const QPI::AssetOwnershipSelect& ownership) { _issuance = issuance; _issuanceIdx = ::issuanceIndex(issuance.issuer, issuance.assetName); @@ -125,7 +125,7 @@ sint64 QPI::AssetOwnershipIterator::numberOfOwnedShares() const // Start iteration with given issuance and given ownership + possession filters (selects first record). -void QPI::AssetPossessionIterator::begin(const AssetIssuanceId& issuance, const AssetOwnershipSelect& ownership, const AssetPossessionSelect& possession) +void QPI::AssetPossessionIterator::begin(const Asset& issuance, const AssetOwnershipSelect& ownership, const AssetPossessionSelect& possession) { AssetOwnershipIterator::begin(issuance, ownership); @@ -433,7 +433,7 @@ long long QPI::QpiContextFunctionCall::numberOfPossessedShares(unsigned long lon return ::numberOfPossessedShares(assetName, issuer, owner, possessor, ownershipManagingContractIndex, possessionManagingContractIndex); } -sint64 QPI::QpiContextFunctionCall::numberOfShares(const QPI::AssetIssuanceId& issuanceId, const QPI::AssetOwnershipSelect& ownership, const QPI::AssetPossessionSelect& possession) const +sint64 QPI::QpiContextFunctionCall::numberOfShares(const QPI::Asset& issuanceId, const QPI::AssetOwnershipSelect& ownership, const QPI::AssetPossessionSelect& possession) const { return ::numberOfShares(issuanceId, ownership, possession); } diff --git a/src/contract_core/qpi_collection_impl.h b/src/contract_core/qpi_collection_impl.h index c96b23d3..8c8282dd 100644 --- a/src/contract_core/qpi_collection_impl.h +++ b/src/contract_core/qpi_collection_impl.h @@ -1,4 +1,4 @@ -// Implements functions of QPI::collection in order to: +// Implements functions of QPI::Collection in order to: // 1. keep setMem() and copyMem() unavailable to contracts // 2. keep QPI file smaller and easier to read for contract devs // CAUTION: Include this AFTER the contract implementations! @@ -11,7 +11,7 @@ namespace QPI { template - void collection::_softReset() + void Collection::_softReset() { setMem(_povs, sizeof(_povs), 0); setMem(_povOccupationFlags, sizeof(_povOccupationFlags), 0); @@ -20,7 +20,7 @@ namespace QPI } template - sint64 collection::_povIndex(const id& pov) const + sint64 Collection::_povIndex(const id& pov) const { sint64 povIndex = pov.u64._0 & (L - 1); for (sint64 counter = 0; counter < L; counter += 32) @@ -46,7 +46,7 @@ namespace QPI } template - sint64 collection::_headIndex(const sint64 povIndex, const sint64 maxPriority) const + sint64 Collection::_headIndex(const sint64 povIndex, const sint64 maxPriority) const { // with current code path, pov is not empty here const auto& pov = _povs[povIndex]; @@ -95,7 +95,7 @@ namespace QPI } template - sint64 collection::_tailIndex(const sint64 povIndex, const sint64 minPriority) const + sint64 Collection::_tailIndex(const sint64 povIndex, const sint64 minPriority) const { // with current code path, pov is not empty here const auto& pov = _povs[povIndex]; @@ -145,7 +145,7 @@ namespace QPI } template - sint64 collection::_searchElement(const sint64 bstRootIndex, + sint64 Collection::_searchElement(const sint64 bstRootIndex, const sint64 priority, int* pIterationsCount) const { sint64 idx = bstRootIndex; @@ -183,7 +183,7 @@ namespace QPI } template - sint64 collection::_addPovElement(const sint64 povIndex, const T value, const sint64 priority) + sint64 Collection::_addPovElement(const sint64 povIndex, const T value, const sint64 priority) { const sint64 newElementIdx = _population++; auto& newElement = _elements[newElementIdx].init(value, priority, povIndex); @@ -230,7 +230,7 @@ namespace QPI } template - uint64 collection::_getSortedElements(const sint64 rootIdx, sint64* sortedElementIndices) const + uint64 Collection::_getSortedElements(const sint64 rootIdx, sint64* sortedElementIndices) const { uint64 count = 0; sint64 elementIdx = rootIdx; @@ -269,7 +269,7 @@ namespace QPI } template - inline void collection::_set(sint64_4& vec, sint64 v0, sint64 v1, sint64 v2, sint64 v3) const + inline void Collection::_set(sint64_4& vec, sint64 v0, sint64 v1, sint64 v2, sint64 v3) const { vec.set(0, v0); vec.set(1, v1); @@ -278,7 +278,7 @@ namespace QPI } template - sint64 collection::_rebuild(sint64 rootIdx) + sint64 Collection::_rebuild(sint64 rootIdx) { auto* sortedElementIndices = reinterpret_cast(::__scratchpad()); if (sortedElementIndices == NULL) @@ -364,7 +364,7 @@ namespace QPI } template - sint64 collection::_getMostLeft(sint64 elementIdx) const + sint64 Collection::_getMostLeft(sint64 elementIdx) const { while (_elements[elementIdx].bstLeftIndex != NULL_INDEX) { @@ -374,7 +374,7 @@ namespace QPI } template - sint64 collection::_getMostRight(sint64 elementIdx) const + sint64 Collection::_getMostRight(sint64 elementIdx) const { while (_elements[elementIdx].bstRightIndex != NULL_INDEX) { @@ -384,7 +384,7 @@ namespace QPI } template - sint64 collection::_previousElementIndex(sint64 elementIdx) const + sint64 Collection::_previousElementIndex(sint64 elementIdx) const { elementIdx &= (L - 1); if (uint64(elementIdx) < _population) @@ -415,7 +415,7 @@ namespace QPI } template - sint64 collection::_nextElementIndex(sint64 elementIdx) const + sint64 Collection::_nextElementIndex(sint64 elementIdx) const { elementIdx &= (L - 1); if (uint64(elementIdx) < _population) @@ -446,7 +446,7 @@ namespace QPI } template - bool collection::_updateParent(const sint64 elementIdx, const sint64 newElementIdx) + bool Collection::_updateParent(const sint64 elementIdx, const sint64 newElementIdx) { if (elementIdx != NULL_INDEX) { @@ -473,7 +473,7 @@ namespace QPI } template - void collection::_moveElement(const sint64 srcIdx, const sint64 dstIdx) + void Collection::_moveElement(const sint64 srcIdx, const sint64 dstIdx) { copyMem(&_elements[dstIdx], &_elements[srcIdx], sizeof(_elements[0])); @@ -516,7 +516,7 @@ namespace QPI } template - uint64 collection::_getEncodedPovOccupationFlags(const uint64* povOccupationFlags, const sint64 povIndex) const + uint64 Collection::_getEncodedPovOccupationFlags(const uint64* povOccupationFlags, const sint64 povIndex) const { const sint64 offset = (povIndex & 31) << 1; uint64 flags = povOccupationFlags[povIndex >> 5] >> offset; @@ -528,7 +528,7 @@ namespace QPI } template - sint64 collection::add(const id& pov, T element, sint64 priority) + sint64 Collection::add(const id& pov, T element, sint64 priority) { if (_population < capacity() && _markRemovalCounter < capacity()) { @@ -562,12 +562,12 @@ namespace QPI } template - void collection::cleanup() + void Collection::cleanup() { // _povs gets occupied over time with entries of type 3 which means they are marked for cleanup. - // Once cleanup is called it's necessary to remove all these type 3 entries by reconstructing a fresh collection residing in scratchpad buffer. + // Once cleanup is called it's necessary to remove all these type 3 entries by reconstructing a fresh Collection residing in scratchpad buffer. // The _elements array is not reorganized by the cleanup (only references to _povs are updated). - // Cleanup() called for a collection having only type 3 entries in _povs must give the result equal to reset() memory content wise. + // Cleanup() called for a Collection having only type 3 entries in _povs must give the result equal to reset() memory content wise. // Quick check to cleanup if (!_markRemovalCounter) @@ -575,7 +575,7 @@ namespace QPI return; } - // Speedup case of empty collection but existed marked for removal povs + // Speedup case of empty Collection but existed marked for removal povs if (!population()) { _softReset(); @@ -590,7 +590,7 @@ namespace QPI setMem(::__scratchpad(), sizeof(_povs) + sizeof(_povOccupationFlags), 0); uint64 newPopulation = 0; - // Go through pov hash map. For each pov that is occupied but not marked for removal, insert pov in new collection's pov buffers and + // Go through pov hash map. For each pov that is occupied but not marked for removal, insert pov in new Collection's pov buffers and // update povIndex in elements belonging to pov. constexpr uint64 oldPovIndexGroupCount = (L >> 5) ? (L >> 5) : 1; for (sint64 oldPovIndexGroup = 0; oldPovIndexGroup < oldPovIndexGroupCount; oldPovIndexGroup++) @@ -603,7 +603,7 @@ namespace QPI for (maskBits >>= oldPovIndexOffset; oldPovIndexOffset < oldPovIndexOffsetEnd; oldPovIndexOffset += 2, maskBits >>= 2) { - // Only add pov to new collection that are occupied and not marked for removal + // Only add pov to new Collection that are occupied and not marked for removal if (maskBits & 3ULL) { // find empty position in new pov hash map @@ -674,13 +674,13 @@ namespace QPI } template - inline T collection::element(sint64 elementIndex) const + inline T Collection::element(sint64 elementIndex) const { return _elements[elementIndex & (L - 1)].value; } template - sint64 collection::headIndex(const id& pov) const + sint64 Collection::headIndex(const id& pov) const { const sint64 povIndex = _povIndex(pov); @@ -688,7 +688,7 @@ namespace QPI } template - sint64 collection::headIndex(const id& pov, sint64 maxPriority) const + sint64 Collection::headIndex(const id& pov, sint64 maxPriority) const { const sint64 povIndex = _povIndex(pov); if (povIndex < 0) @@ -700,19 +700,19 @@ namespace QPI } template - sint64 collection::nextElementIndex(sint64 elementIndex) const + sint64 Collection::nextElementIndex(sint64 elementIndex) const { return _nextElementIndex(elementIndex); } template - inline uint64 collection::population() const + inline uint64 Collection::population() const { return _population; } template - uint64 collection::population(const id& pov) const + uint64 Collection::population(const id& pov) const { const sint64 povIndex = _povIndex(pov); @@ -720,25 +720,25 @@ namespace QPI } template - id collection::pov(sint64 elementIndex) const + id Collection::pov(sint64 elementIndex) const { return _povs[_elements[elementIndex & (L - 1)].povIndex].value; } template - sint64 collection::prevElementIndex(sint64 elementIndex) const + sint64 Collection::prevElementIndex(sint64 elementIndex) const { return _previousElementIndex(elementIndex); } template - sint64 collection::priority(sint64 elementIndex) const + sint64 Collection::priority(sint64 elementIndex) const { return _elements[elementIndex & (L - 1)].priority; } template - sint64 collection::remove(sint64 elementIdx) + sint64 Collection::remove(sint64 elementIdx) { sint64 nextElementIdxOfRemoved = NULL_INDEX; elementIdx &= (L - 1); @@ -853,7 +853,7 @@ namespace QPI } template - void collection::replace(sint64 oldElementIndex, const T& newElement) + void Collection::replace(sint64 oldElementIndex, const T& newElement) { if (uint64(oldElementIndex) < _population) { @@ -862,13 +862,13 @@ namespace QPI } template - void collection::reset() + void Collection::reset() { setMem(this, sizeof(*this), 0); } template - sint64 collection::tailIndex(const id& pov) const + sint64 Collection::tailIndex(const id& pov) const { const sint64 povIndex = _povIndex(pov); @@ -876,7 +876,7 @@ namespace QPI } template - sint64 collection::tailIndex(const id& pov, sint64 minPriority) const + sint64 Collection::tailIndex(const id& pov, sint64 minPriority) const { const sint64 povIndex = _povIndex(pov); if (povIndex < 0) diff --git a/src/contract_core/qpi_trivial_impl.h b/src/contract_core/qpi_trivial_impl.h index d77a589d..d61e7afc 100644 --- a/src/contract_core/qpi_trivial_impl.h +++ b/src/contract_core/qpi_trivial_impl.h @@ -23,14 +23,14 @@ namespace QPI // Check if array is sorted in given range (duplicates allowed). Returns false if range is invalid. template - bool isArraySorted(const array& array, uint64 beginIdx, uint64 endIdx) + bool isArraySorted(const Array& Array, uint64 beginIdx, uint64 endIdx) { if (endIdx > L || beginIdx > endIdx) return false; for (uint64 i = beginIdx + 1; i < endIdx; ++i) { - if (array.get(i - 1) > array.get(i)) + if (Array.get(i - 1) > Array.get(i)) return false; } @@ -39,14 +39,14 @@ namespace QPI // Check if array is sorted without duplicates in given range. Returns false if range is invalid. template - bool isArraySortedWithoutDuplicates(const array& array, uint64 beginIdx, uint64 endIdx) + bool isArraySortedWithoutDuplicates(const Array& Array, uint64 beginIdx, uint64 endIdx) { if (endIdx > L || beginIdx > endIdx) return false; for (uint64 i = beginIdx + 1; i < endIdx; ++i) { - if (array.get(i - 1) >= array.get(i)) + if (Array.get(i - 1) >= Array.get(i)) return false; } diff --git a/src/contracts/ComputorControlledFund.h b/src/contracts/ComputorControlledFund.h index 49ccf645..2c3a44fe 100644 --- a/src/contracts/ComputorControlledFund.h +++ b/src/contracts/ComputorControlledFund.h @@ -27,13 +27,13 @@ struct CCF : public ContractBase struct LatestTransfersEntry { id destination; - array url; + Array url; sint64 amount; uint32 tick; bool success; }; - typedef array LatestTransfersT; + typedef Array LatestTransfersT; private: //---------------------------------------------------------------------------- @@ -102,7 +102,7 @@ struct CCF : public ContractBase struct GetProposalIndices_output { uint16 numOfIndices; // Number of valid entries in indices. Call again if it is 64. - array indices; // Requested proposal indices. Valid entries are in range 0 ... (numOfIndices - 1). + Array indices; // Requested proposal indices. Valid entries are in range 0 ... (numOfIndices - 1). }; PUBLIC_FUNCTION(GetProposalIndices) diff --git a/src/contracts/GeneralQuorumProposal.h b/src/contracts/GeneralQuorumProposal.h index e0a41786..95df2bda 100644 --- a/src/contracts/GeneralQuorumProposal.h +++ b/src/contracts/GeneralQuorumProposal.h @@ -31,7 +31,7 @@ struct GQMPROP : public ContractBase uint16 firstEpoch; }; - typedef array RevenueDonationT; + typedef Array RevenueDonationT; private: //---------------------------------------------------------------------------- @@ -125,7 +125,7 @@ struct GQMPROP : public ContractBase struct GetProposalIndices_output { uint16 numOfIndices; // Number of valid entries in indices. Call again if it is 64. - array indices; // Requested proposal indices. Valid entries are in range 0 ... (numOfIndices - 1). + Array indices; // Requested proposal indices. Valid entries are in range 0 ... (numOfIndices - 1). }; PUBLIC_FUNCTION(GetProposalIndices) diff --git a/src/contracts/QVAULT.h b/src/contracts/QVAULT.h index 77e848bd..8cbdf017 100644 --- a/src/contracts/QVAULT.h +++ b/src/contracts/QVAULT.h @@ -216,7 +216,7 @@ struct QVAULT : public ContractBase id adminAddress, newAdminAddress1, newAdminAddress2, newAdminAddress3; id bannedAddress1, bannedAddress2, bannedAddress3; id unbannedAddress1, unbannedAddress2, unbannedAddress3; - array bannedAddress; + Array bannedAddress; uint32 numberOfBannedAddress; uint32 shareholderDividend, QCAPHolderPermille, reinvestingPermille, devPermille, burnPermille; uint32 newQCAPHolderPermille1, newReinvestingPermille1, newDevPermille1; @@ -606,7 +606,7 @@ struct QVAULT : public ContractBase { ::Entity entity; AssetPossessionIterator iter; - AssetIssuanceId QCAPId; + Asset QCAPId; uint64 revenue; uint64 paymentForShareholders; uint64 paymentForQCAPHolders; diff --git a/src/contracts/Qearn.h b/src/contracts/Qearn.h index 513c8277..16cc52a6 100644 --- a/src/contracts/Qearn.h +++ b/src/contracts/Qearn.h @@ -171,6 +171,36 @@ struct QEARN : public ContractBase }; + struct getBurnedAndBoostedStats_input { + + }; + + struct getBurnedAndBoostedStats_output { + + uint64 burnedAmount; + uint64 averageBurnedPercent; + uint64 boostedAmount; + uint64 averageBoostedPercent; + uint64 rewardedAmount; + uint64 averageRewardedPercent; + + }; + + struct getBurnedAndBoostedStatsPerEpoch_input { + uint32 epoch; + }; + + struct getBurnedAndBoostedStatsPerEpoch_output { + + uint64 burnedAmount; + uint64 burnedPercent; + uint64 boostedAmount; + uint64 boostedPercent; + uint64 rewardedAmount; + uint64 rewardedPercent; + + }; + protected: struct RoundInfo { @@ -180,8 +210,8 @@ struct QEARN : public ContractBase }; - array _initialRoundInfo; - array _currentRoundInfo; + Array _initialRoundInfo; + Array _currentRoundInfo; struct EpochIndexInfo { @@ -189,7 +219,7 @@ struct QEARN : public ContractBase uint32 endIndex; }; - array _epochIndex; + Array _epochIndex; struct LockInfo { @@ -199,7 +229,7 @@ struct QEARN : public ContractBase }; - array locker; + Array locker; struct HistoryInfo { @@ -209,12 +239,22 @@ struct QEARN : public ContractBase }; - array earlyUnlocker; - array fullyUnlocker; + Array earlyUnlocker; + Array fullyUnlocker; uint32 _earlyUnlockedCnt; uint32 _fullyUnlockedCnt; + struct StatsInfo { + + uint64 burnedAmount; + uint64 boostedAmount; + uint64 rewardedAmount; + + }; + + Array statsInfo; + struct getStateOfRound_locals { uint32 firstEpoch; }; @@ -258,6 +298,7 @@ struct QEARN : public ContractBase struct getStatsPerEpoch_locals { + ::Entity entity; uint32 cnt, _t; }; @@ -266,7 +307,9 @@ struct QEARN : public ContractBase output.earlyUnlockedAmount = state._initialRoundInfo.get(input.epoch)._totalLockedAmount - state._currentRoundInfo.get(input.epoch)._totalLockedAmount; output.earlyUnlockedPercent = QPI::div(output.earlyUnlockedAmount * 10000ULL, state._initialRoundInfo.get(input.epoch)._totalLockedAmount); - output.totalLockedAmount = 0; + qpi.getEntity(SELF, locals.entity); + output.totalLockedAmount = locals.entity.incomingAmount - locals.entity.outgoingAmount; + output.averageAPY = 0; locals.cnt = 0; @@ -282,12 +325,54 @@ struct QEARN : public ContractBase } locals.cnt++; - output.totalLockedAmount += state._currentRoundInfo.get(locals._t)._totalLockedAmount; output.averageAPY += QPI::div(state._currentRoundInfo.get(locals._t)._epochBonusAmount * 10000000ULL, state._currentRoundInfo.get(locals._t)._totalLockedAmount); } output.averageAPY = QPI::div(output.averageAPY, locals.cnt * 1ULL); + + _ + struct getBurnedAndBoostedStats_locals + { + uint32 _t; + }; + + PUBLIC_FUNCTION_WITH_LOCALS(getBurnedAndBoostedStats) + + output.boostedAmount = 0; + output.burnedAmount = 0; + output.rewardedAmount = 0; + output.averageBurnedPercent = 0; + output.averageBoostedPercent = 0; + output.averageRewardedPercent = 0; + + for(locals._t = 138; locals._t < qpi.epoch(); locals._t++) + { + output.boostedAmount += state.statsInfo.get(locals._t).boostedAmount; + output.burnedAmount += state.statsInfo.get(locals._t).burnedAmount; + output.rewardedAmount += state.statsInfo.get(locals._t).rewardedAmount; + + output.averageBurnedPercent += div(state.statsInfo.get(locals._t).burnedAmount * 10000000, state._initialRoundInfo.get(locals._t)._epochBonusAmount); + output.averageBoostedPercent += div(state.statsInfo.get(locals._t).boostedAmount * 10000000, state._initialRoundInfo.get(locals._t)._epochBonusAmount); + output.averageRewardedPercent += div(state.statsInfo.get(locals._t).rewardedAmount * 10000000, state._initialRoundInfo.get(locals._t)._epochBonusAmount); + } + + output.averageBurnedPercent = div(output.averageBurnedPercent, qpi.epoch() - 138ULL); + output.averageBoostedPercent = div(output.averageBoostedPercent, qpi.epoch() - 138ULL); + output.averageRewardedPercent = div(output.averageRewardedPercent, qpi.epoch() - 138ULL); + + _ + + PUBLIC_FUNCTION(getBurnedAndBoostedStatsPerEpoch) + + output.boostedAmount = state.statsInfo.get(input.epoch).boostedAmount; + output.burnedAmount = state.statsInfo.get(input.epoch).burnedAmount; + output.rewardedAmount = state.statsInfo.get(input.epoch).rewardedAmount; + + output.burnedPercent = div(state.statsInfo.get(input.epoch).burnedAmount * 10000000, state._initialRoundInfo.get(input.epoch)._epochBonusAmount); + output.boostedPercent = div(state.statsInfo.get(input.epoch).boostedAmount * 10000000, state._initialRoundInfo.get(input.epoch)._epochBonusAmount); + output.rewardedPercent = div(state.statsInfo.get(input.epoch).rewardedAmount * 10000000, state._initialRoundInfo.get(input.epoch)._epochBonusAmount); + _ struct getUserLockedInfo_locals { @@ -478,6 +563,7 @@ struct QEARN : public ContractBase RoundInfo updatedRoundInfo; LockInfo updatedUserInfo; HistoryInfo unlockerInfo; + StatsInfo tmpStats; uint64 amountOfUnlocking; uint64 amountOfReward; @@ -640,6 +726,15 @@ struct QEARN : public ContractBase qpi.transfer(qpi.invocator(), locals.amountOfUnlocking + locals.amountOfReward); qpi.burn(locals.amountOfburn); + if(input.lockedEpoch != qpi.epoch()) + { + locals.tmpStats.burnedAmount = state.statsInfo.get(input.lockedEpoch).burnedAmount + locals.amountOfburn; + locals.tmpStats.rewardedAmount = state.statsInfo.get(input.lockedEpoch).rewardedAmount + locals.amountOfReward; + locals.tmpStats.boostedAmount = state.statsInfo.get(input.lockedEpoch).boostedAmount + QPI::div(locals.divCalcu * (100 - locals.burnPercent - locals.earlyUnlockingPercent) * 1ULL, 10000000ULL); + + state.statsInfo.set(input.lockedEpoch, locals.tmpStats); + } + locals.updatedRoundInfo._totalLockedAmount = state._currentRoundInfo.get(input.lockedEpoch)._totalLockedAmount - locals.amountOfUnlocking; locals.updatedRoundInfo._epochBonusAmount = state._currentRoundInfo.get(input.lockedEpoch)._epochBonusAmount - locals.amountOfReward - locals.amountOfburn; @@ -722,6 +817,8 @@ struct QEARN : public ContractBase REGISTER_USER_FUNCTION(getUserLockStatus, 4); REGISTER_USER_FUNCTION(getEndedStatus, 5); REGISTER_USER_FUNCTION(getStatsPerEpoch, 6); + REGISTER_USER_FUNCTION(getBurnedAndBoostedStats, 7); + REGISTER_USER_FUNCTION(getBurnedAndBoostedStatsPerEpoch, 8); REGISTER_USER_PROCEDURE(lock, 1); REGISTER_USER_PROCEDURE(unlock, 2); @@ -733,6 +830,7 @@ struct QEARN : public ContractBase HistoryInfo INITIALIZE_HISTORY; LockInfo INITIALIZE_USER; RoundInfo INITIALIZE_ROUNDINFO; + StatsInfo INITIALIZE_STATS; uint32 t; bit status; @@ -767,17 +865,6 @@ struct QEARN : public ContractBase state._initialRoundInfo.set(qpi.epoch(), locals.INITIALIZE_ROUNDINFO); state._currentRoundInfo.set(qpi.epoch(), locals.INITIALIZE_ROUNDINFO); - - /* - the initial total locked amount should exclude the amount that locked on initial epoch but it didn't exclude that amount before. - so now it is updated. - I recorded the initial total locked amount of epoch 139 with 1834842583179. - This updates should be deployed on mainnet to chnage the initial infos of epoch 139. it will be deleted an epoch after deployment on mainnet. - */ - locals.INITIALIZE_ROUNDINFO._epochBonusAmount = state._initialRoundInfo.get(139)._epochBonusAmount; - locals.INITIALIZE_ROUNDINFO._totalLockedAmount = 1834842583179; - - state._initialRoundInfo.set(139, locals.INITIALIZE_ROUNDINFO); _ struct END_EPOCH_locals @@ -786,6 +873,7 @@ struct QEARN : public ContractBase LockInfo INITIALIZE_USER; RoundInfo INITIALIZE_ROUNDINFO; EpochIndexInfo tmpEpochIndex; + StatsInfo tmpStats; uint64 _rewardPercent; uint64 _rewardAmount; @@ -809,6 +897,7 @@ struct QEARN : public ContractBase locals._burnAmount = state._currentRoundInfo.get(locals.lockedEpoch)._epochBonusAmount; locals._rewardPercent = QPI::div(state._currentRoundInfo.get(locals.lockedEpoch)._epochBonusAmount * 10000000ULL, state._currentRoundInfo.get(locals.lockedEpoch)._totalLockedAmount); + locals.tmpStats.rewardedAmount = state.statsInfo.get(locals.lockedEpoch).rewardedAmount; for(locals._t = state._epochIndex.get(locals.lockedEpoch).startIndex; locals._t < locals.endIndex; locals._t++) { @@ -841,6 +930,7 @@ struct QEARN : public ContractBase state.locker.set(locals._t, locals.INITIALIZE_USER); locals._burnAmount -= locals._rewardAmount; + locals.tmpStats.rewardedAmount += locals._rewardAmount; } locals.tmpEpochIndex.startIndex = 0; @@ -907,5 +997,10 @@ struct QEARN : public ContractBase state._epochIndex.set(qpi.epoch() + 1, locals.tmpEpochIndex); qpi.burn(locals._burnAmount); + + locals.tmpStats.boostedAmount = state.statsInfo.get(locals.lockedEpoch).boostedAmount; + locals.tmpStats.burnedAmount = state.statsInfo.get(locals.lockedEpoch).burnedAmount + locals._burnAmount; + + state.statsInfo.set(locals.lockedEpoch, locals.tmpStats); _ }; diff --git a/src/contracts/Qearn_old.h b/src/contracts/Qearn_old.h new file mode 100644 index 00000000..9e2ee051 --- /dev/null +++ b/src/contracts/Qearn_old.h @@ -0,0 +1,911 @@ +using namespace QPI; + +constexpr uint64 QEARN_MINIMUM_LOCKING_AMOUNT = 10000000; +constexpr uint64 QEARN_MAX_LOCKS = 4194304; +constexpr uint64 QEARN_MAX_EPOCHS = 4096; +constexpr uint64 QEARN_MAX_USERS = 131072; +constexpr uint64 QEARN_MAX_LOCK_AMOUNT = 1000000000000ULL; +constexpr uint64 QEARN_MAX_BONUS_AMOUNT = 1000000000000ULL; +constexpr uint64 QEARN_INITIAL_EPOCH = 138; + +constexpr uint64 QEARN_EARLY_UNLOCKING_PERCENT_0_3 = 0; +constexpr uint64 QEARN_EARLY_UNLOCKING_PERCENT_4_7 = 5; +constexpr uint64 QEARN_EARLY_UNLOCKING_PERCENT_8_11 = 5; +constexpr uint64 QEARN_EARLY_UNLOCKING_PERCENT_12_15 = 10; +constexpr uint64 QEARN_EARLY_UNLOCKING_PERCENT_16_19 = 15; +constexpr uint64 QEARN_EARLY_UNLOCKING_PERCENT_20_23 = 20; +constexpr uint64 QEARN_EARLY_UNLOCKING_PERCENT_24_27 = 25; +constexpr uint64 QEARN_EARLY_UNLOCKING_PERCENT_28_31 = 30; +constexpr uint64 QEARN_EARLY_UNLOCKING_PERCENT_32_35 = 35; +constexpr uint64 QEARN_EARLY_UNLOCKING_PERCENT_36_39 = 40; +constexpr uint64 QEARN_EARLY_UNLOCKING_PERCENT_40_43 = 45; +constexpr uint64 QEARN_EARLY_UNLOCKING_PERCENT_44_47 = 50; +constexpr uint64 QEARN_EARLY_UNLOCKING_PERCENT_48_51 = 55; + +constexpr uint64 QEARN_BURN_PERCENT_0_3 = 0; +constexpr uint64 QEARN_BURN_PERCENT_4_7 = 45; +constexpr uint64 QEARN_BURN_PERCENT_8_11 = 45; +constexpr uint64 QEARN_BURN_PERCENT_12_15 = 45; +constexpr uint64 QEARN_BURN_PERCENT_16_19 = 40; +constexpr uint64 QEARN_BURN_PERCENT_20_23 = 40; +constexpr uint64 QEARN_BURN_PERCENT_24_27 = 35; +constexpr uint64 QEARN_BURN_PERCENT_28_31 = 35; +constexpr uint64 QEARN_BURN_PERCENT_32_35 = 35; +constexpr uint64 QEARN_BURN_PERCENT_36_39 = 30; +constexpr uint64 QEARN_BURN_PERCENT_40_43 = 30; +constexpr uint64 QEARN_BURN_PERCENT_44_47 = 30; +constexpr uint64 QEARN_BURN_PERCENT_48_51 = 25; + +constexpr sint32 QEARN_INVALID_INPUT_AMOUNT = 0; +constexpr sint32 QEARN_LOCK_SUCCESS = 1; +constexpr sint32 QEARN_INVALID_INPUT_LOCKED_EPOCH = 2; +constexpr sint32 QEARN_INVALID_INPUT_UNLOCK_AMOUNT = 3; +constexpr sint32 QEARN_EMPTY_LOCKED = 4; +constexpr sint32 QEARN_UNLOCK_SUCCESS = 5; +constexpr sint32 QEARN_OVERFLOW_USER = 6; +constexpr sint32 QEARN_LIMIT_LOCK = 7; + +struct QEARN2 +{ +}; + +struct QEARN : public ContractBase +{ +public: + struct getLockInfoPerEpoch_input { + uint32 Epoch; /* epoch number to get information */ + }; + + struct getLockInfoPerEpoch_output { + uint64 lockedAmount; /* initial total locked amount in epoch */ + uint64 bonusAmount; /* initial bonus amount in epoch*/ + uint64 currentLockedAmount; /* total locked amount in epoch. exactly the amount excluding the amount unlocked early*/ + uint64 currentBonusAmount; /* bonus amount in epoch excluding the early unlocking */ + uint64 yield; /* Yield calculated by 10000000 multiple*/ + }; + + struct getUserLockedInfo_input { + id user; + uint32 epoch; + }; + + struct getUserLockedInfo_output { + uint64 lockedAmount; /* the amount user locked at input.epoch */ + }; + + /* + getStateOfRound FUNCTION + + getStateOfRound function returns following. + + 0 = open epoch,not started yet + 1 = running epoch + 2 = ended epoch(>52weeks) + */ + struct getStateOfRound_input { + uint32 epoch; + }; + + struct getStateOfRound_output { + uint32 state; + }; + + /* + getUserLockStatus FUNCTION + + the status will return the binary status. + 1101010010110101001011010100101101010010110101001001 + + 1 means locked in [index of 1] weeks ago. 0 means unlocked in [index of zero] weeks ago. + The frontend can get the status of locked in 52 epochs. in above binary status, + the frontend can know that user locked 0 week ago, 1 week ago, 3 weeks ago, 5, 8,10,11,13 weeks ago. + */ + struct getUserLockStatus_input { + id user; + }; + + struct getUserLockStatus_output { + uint64 status; + }; + + /* + getEndedStatus FUNCTION + + output.earlyRewardedAmount returns the amount rewarded by unlocking early at current epoch + output.earlyUnlockedAmount returns the amount unlocked by unlocking early at current epoch + output.fullyRewardedAmount returns the amount rewarded by unlocking fully at the end of previous epoch + output.fullyUnlockedAmount returns the amount unlocked by unlocking fully at the end of previous epoch + + let's assume that current epoch is 170, user unlocked the 15B qu totally at this epoch and he got the 30B qu of reward. + in this case, output.earlyUnlockedAmount = 15B qu, output.earlyRewardedAmount = 30B qu + if this user unlocks 3B qu additionally at this epoch and rewarded 6B qu, + in this case, output.earlyUnlockedAmount = 18B qu, output.earlyRewardedAmount = 36B qu + state.earlyUnlocker array would be initialized at the end of every epoch + + let's assume also that current epoch is 170, user got the 15B(locked amount for 52 weeks) + 10B(rewarded amount for 52 weeks) at the end of epoch 169. + in this case, output.fullyRewardedAmount = 10B, output.fullyUnlockedAmount = 15B + state.fullyUnlocker array would be decided with distributions at the end of every epoch + + state.earlyUnlocker, state.fullyUnlocker arrays would be initialized and decided by following expression at the END_EPOCH_WITH_LOCALS function. + state._earlyUnlockedCnt = 0; + state._fullyUnlockedCnt = 0; + */ + + struct getEndedStatus_input { + id user; + }; + + struct getEndedStatus_output { + uint64 fullyUnlockedAmount; + uint64 fullyRewardedAmount; + uint64 earlyUnlockedAmount; + uint64 earlyRewardedAmount; + }; + + struct lock_input { + }; + + struct lock_output { + sint32 returnCode; + }; + + struct unlock_input { + uint64 amount; /* unlocking amount */ + uint32 lockedEpoch; /* locked epoch */ + }; + + struct unlock_output { + sint32 returnCode; + }; + + struct getStatsPerEpoch_input { + uint32 epoch; + }; + + struct getStatsPerEpoch_output { + + uint64 earlyUnlockedAmount; + uint64 earlyUnlockedPercent; + uint64 totalLockedAmount; + uint64 averageAPY; + + }; + +protected: + + struct RoundInfo { + + uint64 _totalLockedAmount; // The initial total locked amount in any epoch. Max Epoch is 65535 + uint64 _epochBonusAmount; // The initial bonus amount per an epoch. Max Epoch is 65535 + + }; + + Array _initialRoundInfo; + Array _currentRoundInfo; + + struct EpochIndexInfo { + + uint32 startIndex; + uint32 endIndex; + }; + + Array _epochIndex; + + struct LockInfo { + + uint64 _lockedAmount; + id ID; + uint32 _lockedEpoch; + + }; + + Array locker; + + struct HistoryInfo { + + uint64 _unlockedAmount; + uint64 _rewardedAmount; + id _unlockedID; + + }; + + Array earlyUnlocker; + Array fullyUnlocker; + + uint32 _earlyUnlockedCnt; + uint32 _fullyUnlockedCnt; + + struct getStateOfRound_locals { + uint32 firstEpoch; + }; + + PUBLIC_FUNCTION_WITH_LOCALS(getStateOfRound) + if(input.epoch < QEARN_INITIAL_EPOCH) + { // non staking + output.state = 2; + return ; + } + if(input.epoch > qpi.epoch()) + { + output.state = 0; // opening round, not started yet + } + locals.firstEpoch = qpi.epoch() - 52; + if(input.epoch <= qpi.epoch() && input.epoch >= locals.firstEpoch) + { + output.state = 1; // running round, available unlocking early + } + if(input.epoch < locals.firstEpoch) + { + output.state = 2; // ended round + } + _ + + PUBLIC_FUNCTION(getLockInfoPerEpoch) + + output.bonusAmount = state._initialRoundInfo.get(input.Epoch)._epochBonusAmount; + output.lockedAmount = state._initialRoundInfo.get(input.Epoch)._totalLockedAmount; + output.currentBonusAmount = state._currentRoundInfo.get(input.Epoch)._epochBonusAmount; + output.currentLockedAmount = state._currentRoundInfo.get(input.Epoch)._totalLockedAmount; + if(state._currentRoundInfo.get(input.Epoch)._totalLockedAmount) + { + output.yield = state._currentRoundInfo.get(input.Epoch)._epochBonusAmount * 10000000ULL / state._currentRoundInfo.get(input.Epoch)._totalLockedAmount; + } + else + { + output.yield = 0ULL; + } + _ + + struct getStatsPerEpoch_locals + { + uint32 cnt, _t; + }; + + PUBLIC_FUNCTION_WITH_LOCALS(getStatsPerEpoch) + + output.earlyUnlockedAmount = state._initialRoundInfo.get(input.epoch)._totalLockedAmount - state._currentRoundInfo.get(input.epoch)._totalLockedAmount; + output.earlyUnlockedPercent = QPI::div(output.earlyUnlockedAmount * 10000ULL, state._initialRoundInfo.get(input.epoch)._totalLockedAmount); + + output.totalLockedAmount = 0; + output.averageAPY = 0; + locals.cnt = 0; + + for(locals._t = qpi.epoch() - 1U; locals._t >= qpi.epoch() - 52U; locals._t--) + { + if(locals._t < QEARN_INITIAL_EPOCH) + { + break; + } + if(state._currentRoundInfo.get(locals._t)._totalLockedAmount == 0) + { + continue; + } + + locals.cnt++; + output.totalLockedAmount += state._currentRoundInfo.get(locals._t)._totalLockedAmount; + output.averageAPY += QPI::div(state._currentRoundInfo.get(locals._t)._epochBonusAmount * 10000000ULL, state._currentRoundInfo.get(locals._t)._totalLockedAmount); + } + + output.averageAPY = QPI::div(output.averageAPY, locals.cnt * 1ULL); + + _ + + struct getUserLockedInfo_locals { + uint32 _t; + uint32 startIndex; + uint32 endIndex; + }; + + PUBLIC_FUNCTION_WITH_LOCALS(getUserLockedInfo) + + locals.startIndex = state._epochIndex.get(input.epoch).startIndex; + locals.endIndex = state._epochIndex.get(input.epoch).endIndex; + + for(locals._t = locals.startIndex; locals._t < locals.endIndex; locals._t++) + { + if(state.locker.get(locals._t).ID == input.user) + { + output.lockedAmount = state.locker.get(locals._t)._lockedAmount; + return; + } + } + _ + + struct getUserLockStatus_locals { + uint64 bn; + uint32 _t; + uint32 _r; + uint32 endIndex; + uint8 lockedWeeks; + }; + + PUBLIC_FUNCTION_WITH_LOCALS(getUserLockStatus) + + output.status = 0ULL; + locals.endIndex = state._epochIndex.get(qpi.epoch()).endIndex; + + for(locals._t = 0; locals._t < locals.endIndex; locals._t++) + { + if(state.locker.get(locals._t)._lockedAmount > 0 && state.locker.get(locals._t).ID == input.user) + { + + locals.lockedWeeks = qpi.epoch() - state.locker.get(locals._t)._lockedEpoch; + locals.bn = 1ULL< 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + return; + } + + locals.endIndex = state._epochIndex.get(qpi.epoch()).endIndex; + + for(locals.t = state._epochIndex.get(qpi.epoch()).startIndex ; locals.t < locals.endIndex; locals.t++) + { + + if(state.locker.get(locals.t).ID == qpi.invocator()) + { // the case to be locked several times at one epoch, at that time, this address already located in state.Locker array, the amount will be increased as current locking amount. + if(state.locker.get(locals.t)._lockedAmount + qpi.invocationReward() > QEARN_MAX_LOCK_AMOUNT) + { + output.returnCode = QEARN_LIMIT_LOCK; + if(qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + return; + } + + locals.newLocker._lockedAmount = state.locker.get(locals.t)._lockedAmount + qpi.invocationReward(); + locals.newLocker._lockedEpoch = qpi.epoch(); + locals.newLocker.ID = qpi.invocator(); + + state.locker.set(locals.t, locals.newLocker); + + locals.updatedRoundInfo._totalLockedAmount = state._initialRoundInfo.get(qpi.epoch())._totalLockedAmount + qpi.invocationReward(); + locals.updatedRoundInfo._epochBonusAmount = state._initialRoundInfo.get(qpi.epoch())._epochBonusAmount; + state._initialRoundInfo.set(qpi.epoch(), locals.updatedRoundInfo); + + locals.updatedRoundInfo._totalLockedAmount = state._currentRoundInfo.get(qpi.epoch())._totalLockedAmount + qpi.invocationReward(); + locals.updatedRoundInfo._epochBonusAmount = state._currentRoundInfo.get(qpi.epoch())._epochBonusAmount; + state._currentRoundInfo.set(qpi.epoch(), locals.updatedRoundInfo); + + output.returnCode = QEARN_LOCK_SUCCESS; // additional locking of this epoch is succeed + return ; + } + + } + + if(locals.endIndex == QEARN_MAX_LOCKS - 1) + { + output.returnCode = QEARN_OVERFLOW_USER; + if(qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + return ; // overflow users in Qearn + } + + if(qpi.invocationReward() > QEARN_MAX_LOCK_AMOUNT) + { + output.returnCode = QEARN_LIMIT_LOCK; + if(qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + return; + } + + locals.newLocker.ID = qpi.invocator(); + locals.newLocker._lockedAmount = qpi.invocationReward(); + locals.newLocker._lockedEpoch = qpi.epoch(); + + state.locker.set(locals.endIndex, locals.newLocker); + + locals.tmpIndex.startIndex = state._epochIndex.get(qpi.epoch()).startIndex; + locals.tmpIndex.endIndex = locals.endIndex + 1; + state._epochIndex.set(qpi.epoch(), locals.tmpIndex); + + locals.updatedRoundInfo._totalLockedAmount = state._initialRoundInfo.get(qpi.epoch())._totalLockedAmount + qpi.invocationReward(); + locals.updatedRoundInfo._epochBonusAmount = state._initialRoundInfo.get(qpi.epoch())._epochBonusAmount; + state._initialRoundInfo.set(qpi.epoch(), locals.updatedRoundInfo); + + locals.updatedRoundInfo._totalLockedAmount = state._currentRoundInfo.get(qpi.epoch())._totalLockedAmount + qpi.invocationReward(); + locals.updatedRoundInfo._epochBonusAmount = state._currentRoundInfo.get(qpi.epoch())._epochBonusAmount; + state._currentRoundInfo.set(qpi.epoch(), locals.updatedRoundInfo); + + output.returnCode = QEARN_LOCK_SUCCESS; // new locking of this epoch is succeed + _ + + struct unlock_locals { + + RoundInfo updatedRoundInfo; + LockInfo updatedUserInfo; + HistoryInfo unlockerInfo; + + uint64 amountOfUnlocking; + uint64 amountOfReward; + uint64 amountOfburn; + uint64 rewardPercent; + sint64 divCalcu; + uint32 earlyUnlockingPercent; + uint32 burnPercent; + uint32 indexOfinvocator; + uint32 _t; + uint32 countOfLastVacancy; + uint32 countOfLockedEpochs; + uint32 startIndex; + uint32 endIndex; + + }; + + PUBLIC_PROCEDURE_WITH_LOCALS(unlock) + + if (input.lockedEpoch > QEARN_MAX_EPOCHS || input.lockedEpoch < QEARN_INITIAL_EPOCH) + { + + output.returnCode = QEARN_INVALID_INPUT_LOCKED_EPOCH; // if user try to unlock with wrong locked epoch, it should be failed to unlock. + return ; + + } + + if(input.amount < QEARN_MINIMUM_LOCKING_AMOUNT) + { + + output.returnCode = QEARN_INVALID_INPUT_AMOUNT; + return ; + + } + + locals.indexOfinvocator = QEARN_MAX_LOCKS; + locals.startIndex = state._epochIndex.get(input.lockedEpoch).startIndex; + locals.endIndex = state._epochIndex.get(input.lockedEpoch).endIndex; + + for(locals._t = locals.startIndex ; locals._t < locals.endIndex; locals._t++) + { + + if(state.locker.get(locals._t).ID == qpi.invocator()) + { + if(state.locker.get(locals._t)._lockedAmount < input.amount) + { + + output.returnCode = QEARN_INVALID_INPUT_UNLOCK_AMOUNT; // if the amount to be wanted to unlock is more than locked amount, it should be failed to unlock + return ; + + } + else + { + locals.indexOfinvocator = locals._t; + break; + } + } + + } + + if(locals.indexOfinvocator == QEARN_MAX_LOCKS) + { + + output.returnCode = QEARN_EMPTY_LOCKED; // if there is no any locked info in state.Locker array, it shows this address didn't lock at the epoch (input.Locked_Epoch) + return ; + } + + /* the rest amount after unlocking should be more than MINIMUM_LOCKING_AMOUNT */ + if(state.locker.get(locals.indexOfinvocator)._lockedAmount - input.amount < QEARN_MINIMUM_LOCKING_AMOUNT) + { + locals.amountOfUnlocking = state.locker.get(locals.indexOfinvocator)._lockedAmount; + } + else + { + locals.amountOfUnlocking = input.amount; + } + + locals.countOfLockedEpochs = qpi.epoch() - input.lockedEpoch - 1; + + locals.earlyUnlockingPercent = QEARN_EARLY_UNLOCKING_PERCENT_0_3; + locals.burnPercent = QEARN_BURN_PERCENT_0_3; + + if(locals.countOfLockedEpochs >= 4 && locals.countOfLockedEpochs <= 7) + { + locals.earlyUnlockingPercent = QEARN_EARLY_UNLOCKING_PERCENT_4_7; + locals.burnPercent = QEARN_BURN_PERCENT_4_7; + } + + else if(locals.countOfLockedEpochs >= 8 && locals.countOfLockedEpochs <= 11) + { + locals.earlyUnlockingPercent = QEARN_EARLY_UNLOCKING_PERCENT_8_11; + locals.burnPercent = QEARN_BURN_PERCENT_8_11; + } + + else if(locals.countOfLockedEpochs >= 12 && locals.countOfLockedEpochs <= 15) + { + locals.earlyUnlockingPercent = QEARN_EARLY_UNLOCKING_PERCENT_12_15; + locals.burnPercent = QEARN_BURN_PERCENT_12_15; + } + + else if(locals.countOfLockedEpochs >= 16 && locals.countOfLockedEpochs <= 19) + { + locals.earlyUnlockingPercent = QEARN_EARLY_UNLOCKING_PERCENT_16_19; + locals.burnPercent = QEARN_BURN_PERCENT_16_19; + } + + else if(locals.countOfLockedEpochs >= 20 && locals.countOfLockedEpochs <= 23) + { + locals.earlyUnlockingPercent = QEARN_EARLY_UNLOCKING_PERCENT_20_23; + locals.burnPercent = QEARN_BURN_PERCENT_20_23; + } + + else if(locals.countOfLockedEpochs >= 24 && locals.countOfLockedEpochs <= 27) + { + locals.earlyUnlockingPercent = QEARN_EARLY_UNLOCKING_PERCENT_24_27; + locals.burnPercent = QEARN_BURN_PERCENT_24_27; + } + + else if(locals.countOfLockedEpochs >= 28 && locals.countOfLockedEpochs <= 31) + { + locals.earlyUnlockingPercent = QEARN_EARLY_UNLOCKING_PERCENT_28_31; + locals.burnPercent = QEARN_BURN_PERCENT_28_31; + } + + else if(locals.countOfLockedEpochs >= 32 && locals.countOfLockedEpochs <= 35) + { + locals.earlyUnlockingPercent = QEARN_EARLY_UNLOCKING_PERCENT_32_35; + locals.burnPercent = QEARN_BURN_PERCENT_32_35; + } + + else if(locals.countOfLockedEpochs >= 36 && locals.countOfLockedEpochs <= 39) + { + locals.earlyUnlockingPercent = QEARN_EARLY_UNLOCKING_PERCENT_36_39; + locals.burnPercent = QEARN_BURN_PERCENT_36_39; + } + + else if(locals.countOfLockedEpochs >= 40 && locals.countOfLockedEpochs <= 43) + { + locals.earlyUnlockingPercent = QEARN_EARLY_UNLOCKING_PERCENT_40_43; + locals.burnPercent = QEARN_BURN_PERCENT_40_43; + } + + else if(locals.countOfLockedEpochs >= 44 && locals.countOfLockedEpochs <= 47) + { + locals.earlyUnlockingPercent = QEARN_EARLY_UNLOCKING_PERCENT_44_47; + locals.burnPercent = QEARN_BURN_PERCENT_44_47; + } + + else if(locals.countOfLockedEpochs >= 48 && locals.countOfLockedEpochs <= 51) + { + locals.earlyUnlockingPercent = QEARN_EARLY_UNLOCKING_PERCENT_48_51; + locals.burnPercent = QEARN_BURN_PERCENT_48_51; + } + + locals.rewardPercent = QPI::div(state._currentRoundInfo.get(input.lockedEpoch)._epochBonusAmount * 10000000ULL, state._currentRoundInfo.get(input.lockedEpoch)._totalLockedAmount); + locals.divCalcu = QPI::div(locals.rewardPercent * locals.amountOfUnlocking , 100ULL); + locals.amountOfReward = QPI::div(locals.divCalcu * locals.earlyUnlockingPercent * 1ULL , 10000000ULL); + locals.amountOfburn = QPI::div(locals.divCalcu * locals.burnPercent * 1ULL, 10000000ULL); + + qpi.transfer(qpi.invocator(), locals.amountOfUnlocking + locals.amountOfReward); + qpi.burn(locals.amountOfburn); + + locals.updatedRoundInfo._totalLockedAmount = state._currentRoundInfo.get(input.lockedEpoch)._totalLockedAmount - locals.amountOfUnlocking; + locals.updatedRoundInfo._epochBonusAmount = state._currentRoundInfo.get(input.lockedEpoch)._epochBonusAmount - locals.amountOfReward - locals.amountOfburn; + + state._currentRoundInfo.set(input.lockedEpoch, locals.updatedRoundInfo); + + if(qpi.epoch() == input.lockedEpoch) + { + locals.updatedRoundInfo._totalLockedAmount = state._initialRoundInfo.get(input.lockedEpoch)._totalLockedAmount - locals.amountOfUnlocking; + locals.updatedRoundInfo._epochBonusAmount = state._initialRoundInfo.get(input.lockedEpoch)._epochBonusAmount; + + state._initialRoundInfo.set(input.lockedEpoch, locals.updatedRoundInfo); + } + + if(state.locker.get(locals.indexOfinvocator)._lockedAmount == locals.amountOfUnlocking) + { + locals.updatedUserInfo.ID = NULL_ID; + locals.updatedUserInfo._lockedAmount = 0; + locals.updatedUserInfo._lockedEpoch = 0; + } + else + { + locals.updatedUserInfo.ID = qpi.invocator(); + locals.updatedUserInfo._lockedAmount = state.locker.get(locals.indexOfinvocator)._lockedAmount - locals.amountOfUnlocking; + locals.updatedUserInfo._lockedEpoch = state.locker.get(locals.indexOfinvocator)._lockedEpoch; + } + + state.locker.set(locals.indexOfinvocator, locals.updatedUserInfo); + + if(state._currentRoundInfo.get(input.lockedEpoch)._totalLockedAmount == 0 && input.lockedEpoch != qpi.epoch()) + { + + // If all users have unlocked early, burn bonus + qpi.burn(state._currentRoundInfo.get(input.lockedEpoch)._epochBonusAmount); + + locals.updatedRoundInfo._totalLockedAmount = 0; + locals.updatedRoundInfo._epochBonusAmount = 0; + + state._currentRoundInfo.set(input.lockedEpoch, locals.updatedRoundInfo); + + } + + if(input.lockedEpoch != qpi.epoch()) + { + + locals.unlockerInfo._unlockedID = qpi.invocator(); + + for(locals._t = 0; locals._t < state._earlyUnlockedCnt; locals._t++) + { + if(state.earlyUnlocker.get(locals._t)._unlockedID == qpi.invocator()) + { + + locals.unlockerInfo._rewardedAmount = state.earlyUnlocker.get(locals._t)._rewardedAmount + locals.amountOfReward; + locals.unlockerInfo._unlockedAmount = state.earlyUnlocker.get(locals._t)._unlockedAmount + locals.amountOfUnlocking; + + state.earlyUnlocker.set(locals._t, locals.unlockerInfo); + + break; + } + } + + if(locals._t == state._earlyUnlockedCnt && state._earlyUnlockedCnt < QEARN_MAX_USERS) + { + locals.unlockerInfo._rewardedAmount = locals.amountOfReward; + locals.unlockerInfo._unlockedAmount = locals.amountOfUnlocking; + + state.earlyUnlocker.set(locals._t, locals.unlockerInfo); + state._earlyUnlockedCnt++; + } + + } + + output.returnCode = QEARN_UNLOCK_SUCCESS; // unlock is succeed + _ + + REGISTER_USER_FUNCTIONS_AND_PROCEDURES + + REGISTER_USER_FUNCTION(getLockInfoPerEpoch, 1); + REGISTER_USER_FUNCTION(getUserLockedInfo, 2); + REGISTER_USER_FUNCTION(getStateOfRound, 3); + REGISTER_USER_FUNCTION(getUserLockStatus, 4); + REGISTER_USER_FUNCTION(getEndedStatus, 5); + REGISTER_USER_FUNCTION(getStatsPerEpoch, 6); + + REGISTER_USER_PROCEDURE(lock, 1); + REGISTER_USER_PROCEDURE(unlock, 2); + + _ + + struct BEGIN_EPOCH_locals + { + HistoryInfo INITIALIZE_HISTORY; + LockInfo INITIALIZE_USER; + RoundInfo INITIALIZE_ROUNDINFO; + + uint32 t; + bit status; + uint64 pre_epoch_balance; + uint64 current_balance; + ::Entity entity; + uint32 locked_epoch; + }; + + BEGIN_EPOCH_WITH_LOCALS + + qpi.getEntity(SELF, locals.entity); + locals.current_balance = locals.entity.incomingAmount - locals.entity.outgoingAmount; + + locals.pre_epoch_balance = 0ULL; + locals.locked_epoch = qpi.epoch() - 52; + for(locals.t = qpi.epoch() - 1; locals.t >= locals.locked_epoch; locals.t--) + { + locals.pre_epoch_balance += state._currentRoundInfo.get(locals.t)._epochBonusAmount + state._currentRoundInfo.get(locals.t)._totalLockedAmount; + } + + if(locals.current_balance - locals.pre_epoch_balance > QEARN_MAX_BONUS_AMOUNT) + { + qpi.burn(locals.current_balance - locals.pre_epoch_balance - QEARN_MAX_BONUS_AMOUNT); + locals.INITIALIZE_ROUNDINFO._epochBonusAmount = QEARN_MAX_BONUS_AMOUNT; + } + else + { + locals.INITIALIZE_ROUNDINFO._epochBonusAmount = locals.current_balance - locals.pre_epoch_balance; + } + locals.INITIALIZE_ROUNDINFO._totalLockedAmount = 0; + + state._initialRoundInfo.set(qpi.epoch(), locals.INITIALIZE_ROUNDINFO); + state._currentRoundInfo.set(qpi.epoch(), locals.INITIALIZE_ROUNDINFO); + + /* + the initial total locked amount should exclude the amount that locked on initial epoch but it didn't exclude that amount before. + so now it is updated. + I recorded the initial total locked amount of epoch 139 with 1834842583179. + This updates should be deployed on mainnet to chnage the initial infos of epoch 139. it will be deleted an epoch after deployment on mainnet. + */ + locals.INITIALIZE_ROUNDINFO._epochBonusAmount = state._initialRoundInfo.get(139)._epochBonusAmount; + locals.INITIALIZE_ROUNDINFO._totalLockedAmount = 1834842583179; + + state._initialRoundInfo.set(139, locals.INITIALIZE_ROUNDINFO); + _ + + struct END_EPOCH_locals + { + HistoryInfo INITIALIZE_HISTORY; + LockInfo INITIALIZE_USER; + RoundInfo INITIALIZE_ROUNDINFO; + EpochIndexInfo tmpEpochIndex; + + uint64 _rewardPercent; + uint64 _rewardAmount; + uint64 _burnAmount; + uint32 lockedEpoch; + uint32 startEpoch; + uint32 _t; + sint32 st; + sint32 en; + uint32 endIndex; + + }; + + END_EPOCH_WITH_LOCALS + + state._earlyUnlockedCnt = 0; + state._fullyUnlockedCnt = 0; + locals.lockedEpoch = qpi.epoch() - 52; + locals.endIndex = state._epochIndex.get(locals.lockedEpoch).endIndex; + + locals._burnAmount = state._currentRoundInfo.get(locals.lockedEpoch)._epochBonusAmount; + + locals._rewardPercent = QPI::div(state._currentRoundInfo.get(locals.lockedEpoch)._epochBonusAmount * 10000000ULL, state._currentRoundInfo.get(locals.lockedEpoch)._totalLockedAmount); + + for(locals._t = state._epochIndex.get(locals.lockedEpoch).startIndex; locals._t < locals.endIndex; locals._t++) + { + if(state.locker.get(locals._t)._lockedAmount == 0) + { + continue; + } + + ASSERT(state.locker.get(locals._t)._lockedEpoch == locals.lockedEpoch); + + locals._rewardAmount = QPI::div(state.locker.get(locals._t)._lockedAmount * locals._rewardPercent, 10000000ULL); + qpi.transfer(state.locker.get(locals._t).ID, locals._rewardAmount + state.locker.get(locals._t)._lockedAmount); + + if(state._fullyUnlockedCnt < QEARN_MAX_USERS) + { + + locals.INITIALIZE_HISTORY._unlockedID = state.locker.get(locals._t).ID; + locals.INITIALIZE_HISTORY._unlockedAmount = state.locker.get(locals._t)._lockedAmount; + locals.INITIALIZE_HISTORY._rewardedAmount = locals._rewardAmount; + + state.fullyUnlocker.set(state._fullyUnlockedCnt, locals.INITIALIZE_HISTORY); + + state._fullyUnlockedCnt++; + } + + locals.INITIALIZE_USER.ID = NULL_ID; + locals.INITIALIZE_USER._lockedAmount = 0; + locals.INITIALIZE_USER._lockedEpoch = 0; + + state.locker.set(locals._t, locals.INITIALIZE_USER); + + locals._burnAmount -= locals._rewardAmount; + } + + locals.tmpEpochIndex.startIndex = 0; + locals.tmpEpochIndex.endIndex = 0; + state._epochIndex.set(locals.lockedEpoch, locals.tmpEpochIndex); + + locals.startEpoch = locals.lockedEpoch + 1; + if (locals.startEpoch < QEARN_INITIAL_EPOCH) + locals.startEpoch = QEARN_INITIAL_EPOCH; + + // remove all gaps in Locker array (from beginning) and update epochIndex + locals.tmpEpochIndex.startIndex = 0; + for(locals._t = locals.startEpoch; locals._t <= qpi.epoch(); locals._t++) + { + // This for loop iteration moves all elements of one epoch the to start of its range of the Locker array. + // The startIndex is given by the end of the range of the previous epoch, the new endIndex is found in the + // gap removal process. + locals.st = locals.tmpEpochIndex.startIndex; + locals.en = state._epochIndex.get(locals._t).endIndex; + ASSERT(locals.st <= locals.en); + + while(locals.st < locals.en) + { + // try to set locals.st to first empty slot + while (state.locker.get(locals.st)._lockedAmount && locals.st < locals.en) + { + locals.st++; + } + + // try set locals.en to last non-empty slot in epoch + --locals.en; + while (!state.locker.get(locals.en)._lockedAmount && locals.st < locals.en) + { + locals.en--; + } + + // if st and en meet, there are no gaps to be closed by moving in this epoch range + if (locals.st >= locals.en) + { + // make locals.en point behind last element again + ++locals.en; + break; + } + + // move entry from locals.en to locals.st + state.locker.set(locals.st, state.locker.get(locals.en)); + + // make locals.en slot empty -> locals.en points behind last element again + locals.INITIALIZE_USER.ID = NULL_ID; + locals.INITIALIZE_USER._lockedAmount = 0; + locals.INITIALIZE_USER._lockedEpoch = 0; + state.locker.set(locals.en, locals.INITIALIZE_USER); + } + + // update epoch index + locals.tmpEpochIndex.endIndex = locals.en; + state._epochIndex.set(locals._t, locals.tmpEpochIndex); + + // set start index of next epoch to end index of current epoch + locals.tmpEpochIndex.startIndex = locals.tmpEpochIndex.endIndex; + } + + locals.tmpEpochIndex.endIndex = locals.tmpEpochIndex.startIndex; + state._epochIndex.set(qpi.epoch() + 1, locals.tmpEpochIndex); + + qpi.burn(locals._burnAmount); + _ +}; diff --git a/src/contracts/Quottery.h b/src/contracts/Quottery.h index 093535e6..e988f44f 100644 --- a/src/contracts/Quottery.h +++ b/src/contracts/Quottery.h @@ -135,7 +135,7 @@ struct QUOTTERY : public ContractBase }; struct getBetOptionDetail_output { - array bettor; + Array bettor; }; struct getActiveBet_input { @@ -143,7 +143,7 @@ struct QUOTTERY : public ContractBase struct getActiveBet_output { uint32 count; - array activeBetId; + Array activeBetId; }; struct getBetByCreator_input { @@ -152,7 +152,7 @@ struct QUOTTERY : public ContractBase struct getBetByCreator_output { uint32 count; - array betId; + Array betId; }; struct cancelBet_input { @@ -201,27 +201,27 @@ struct QUOTTERY : public ContractBase /**************************************/ /************CONTRACT STATES***********/ /**************************************/ - array mBetID; - array mCreator; - array mBetDesc; - array mOptionDesc; - array mBetAmountPerSlot; - array mMaxNumberOfBetSlotPerOption; - array mOracleProvider; - array mOracleFees; - array mCurrentBetState; - array mNumberOption; - array mOpenDate; - array mCloseDate; - array mEndDate; - array mIsOccupied; - array mBetEndTick; + Array mBetID; + Array mCreator; + Array mBetDesc; + Array mOptionDesc; + Array mBetAmountPerSlot; + Array mMaxNumberOfBetSlotPerOption; + Array mOracleProvider; + Array mOracleFees; + Array mCurrentBetState; + Array mNumberOption; + Array mOpenDate; + Array mCloseDate; + Array mEndDate; + Array mIsOccupied; + Array mBetEndTick; //bettor info: - array mBettorID; - array mBettorBetOption; + Array mBettorID; + Array mBettorBetOption; // bet result: - array mBetResultWonOption; - array mBetResultOPId; + Array mBetResultWonOption; + Array mBetResultOPId; //static assert for developing: static_assert(sizeof(mBetID) == (sizeof(uint32) * QUOTTERY_MAX_BET), "bet id array"); diff --git a/src/contracts/Qx.h b/src/contracts/Qx.h index 3ee33a2a..c423a4f6 100644 --- a/src/contracts/Qx.h +++ b/src/contracts/Qx.h @@ -32,7 +32,7 @@ struct QX : public ContractBase sint64 numberOfShares; }; - array orders; + Array orders; }; struct AssetBidOrders_input @@ -50,7 +50,7 @@ struct QX : public ContractBase sint64 numberOfShares; }; - array orders; + Array orders; }; struct EntityAskOrders_input @@ -68,7 +68,7 @@ struct QX : public ContractBase sint64 numberOfShares; }; - array orders; + Array orders; }; struct EntityBidOrders_input @@ -86,7 +86,7 @@ struct QX : public ContractBase sint64 numberOfShares; }; - array orders; + Array orders; }; struct IssueAsset_input @@ -175,7 +175,7 @@ struct QX : public ContractBase id entity; sint64 numberOfShares; }; - collection<_AssetOrder, 2097152 * X_MULTIPLIER> _assetOrders; + Collection<_AssetOrder, 2097152 * X_MULTIPLIER> _assetOrders; struct _EntityOrder { @@ -183,7 +183,7 @@ struct QX : public ContractBase uint64 assetName; sint64 numberOfShares; }; - collection<_EntityOrder, 2097152 * X_MULTIPLIER> _entityOrders; + Collection<_EntityOrder, 2097152 * X_MULTIPLIER> _entityOrders; // TODO: change to "locals" variables and remove from state? -> every func/proc can define struct of "locals" that is passed as an argument (stored on stack structure per processor) sint64 _elementIndex, _elementIndex2; diff --git a/src/contracts/qpi.h b/src/contracts/qpi.h index c12b900b..4a980a41 100644 --- a/src/contracts/qpi.h +++ b/src/contracts/qpi.h @@ -116,11 +116,11 @@ namespace QPI // Array of L bits encoded in array of uint64 (overall size is at least 8 bytes, L must be 2^N) template - struct bit_array + struct BitArray { private: static_assert(L && !(L & (L - 1)), - "The capacity of the bit_array must be 2^N." + "The capacity of the BitArray must be 2^N." ); static constexpr uint64 _bits = L; @@ -175,23 +175,23 @@ namespace QPI }; // Bit array convenience definitions - typedef bit_array<2> bit_2; - typedef bit_array<4> bit_4; - typedef bit_array<8> bit_8; - typedef bit_array<16> bit_16; - typedef bit_array<32> bit_32; - typedef bit_array<64> bit_64; - typedef bit_array<128> bit_128; - typedef bit_array<256> bit_256; - typedef bit_array<512> bit_512; - typedef bit_array<1024> bit_1024; - typedef bit_array<2048> bit_2048; - typedef bit_array<4096> bit_4096; + typedef BitArray<2> bit_2; + typedef BitArray<4> bit_4; + typedef BitArray<8> bit_8; + typedef BitArray<16> bit_16; + typedef BitArray<32> bit_32; + typedef BitArray<64> bit_64; + typedef BitArray<128> bit_128; + typedef BitArray<256> bit_256; + typedef BitArray<512> bit_512; + typedef BitArray<1024> bit_1024; + typedef BitArray<2048> bit_2048; + typedef BitArray<4096> bit_4096; // Array of L elements of type T (L must be 2^N) template - struct array + struct Array { private: static_assert(L && !(L & (L - 1)), @@ -266,49 +266,49 @@ namespace QPI }; // Array convenience definitions - typedef array sint8_2; - typedef array sint8_4; - typedef array sint8_8; + typedef Array sint8_2; + typedef Array sint8_4; + typedef Array sint8_8; - typedef array uint8_2; - typedef array uint8_4; - typedef array uint8_8; + typedef Array uint8_2; + typedef Array uint8_4; + typedef Array uint8_8; - typedef array sint16_2; - typedef array sint16_4; - typedef array sint16_8; + typedef Array sint16_2; + typedef Array sint16_4; + typedef Array sint16_8; - typedef array uint16_2; - typedef array uint16_4; - typedef array uint16_8; + typedef Array uint16_2; + typedef Array uint16_4; + typedef Array uint16_8; - typedef array sint32_2; - typedef array sint32_4; - typedef array sint32_8; + typedef Array sint32_2; + typedef Array sint32_4; + typedef Array sint32_8; - typedef array uint32_2; - typedef array uint32_4; - typedef array uint32_8; + typedef Array uint32_2; + typedef Array uint32_4; + typedef Array uint32_8; - typedef array sint64_2; - typedef array sint64_4; - typedef array sint64_8; + typedef Array sint64_2; + typedef Array sint64_4; + typedef Array sint64_8; - typedef array uint64_2; - typedef array uint64_4; - typedef array uint64_8; + typedef Array uint64_2; + typedef Array uint64_4; + typedef Array uint64_8; - typedef array id_2; - typedef array id_4; - typedef array id_8; + typedef Array id_2; + typedef Array id_4; + typedef Array id_8; // Check if array is sorted in given range (duplicates allowed). Returns false if range is invalid. template - bool isArraySorted(const array& array, uint64 beginIdx = 0, uint64 endIdx = L); + bool isArraySorted(const Array& Array, uint64 beginIdx = 0, uint64 endIdx = L); // Check if array is sorted without duplicates in given range. Returns false if range is invalid. template - bool isArraySortedWithoutDuplicates(const array& array, uint64 beginIdx = 0, uint64 endIdx = L); + bool isArraySortedWithoutDuplicates(const Array& Array, uint64 beginIdx = 0, uint64 endIdx = L); // Hash function class to be used with the hash map. @@ -402,11 +402,11 @@ namespace QPI // Collection of priority queues of elements with type T and total element capacity L. // Each ID pov (point of view) has an own queue. template - struct collection + struct Collection { private: static_assert(L && !(L & (L - 1)), - "The capacity of the collection must be 2^N." + "The capacity of the Collection must be 2^N." ); static constexpr sint64 _nEncodedFlags = L > 32 ? 32 : L; @@ -593,7 +593,7 @@ namespace QPI ////////// - struct AssetIssuanceId + struct Asset { id issuer; uint64 assetName; @@ -650,7 +650,7 @@ namespace QPI class AssetOwnershipIterator { protected: - AssetIssuanceId _issuance; + Asset _issuance; unsigned int _issuanceIdx; AssetOwnershipSelect _ownership; unsigned int _ownershipIdx; @@ -661,13 +661,13 @@ namespace QPI } public: - AssetOwnershipIterator(const AssetIssuanceId& issuance, const AssetOwnershipSelect& ownership = AssetOwnershipSelect::any()) + AssetOwnershipIterator(const Asset& issuance, const AssetOwnershipSelect& ownership = AssetOwnershipSelect::any()) { begin(issuance, ownership); } // Start iteration with given issuance and given ownership filter (selects first record). - inline void begin(const AssetIssuanceId& issuance, const AssetOwnershipSelect& ownership = AssetOwnershipSelect::any()); + inline void begin(const Asset& issuance, const AssetOwnershipSelect& ownership = AssetOwnershipSelect::any()); // Return if iteration with next() has reached end. inline bool reachedEnd() const; @@ -709,13 +709,13 @@ namespace QPI unsigned int _possessionIdx; public: - AssetPossessionIterator(const AssetIssuanceId& issuance, const AssetOwnershipSelect& ownership = AssetOwnershipSelect::any(), const AssetPossessionSelect& possession = AssetPossessionSelect::any()) + AssetPossessionIterator(const Asset& issuance, const AssetOwnershipSelect& ownership = AssetOwnershipSelect::any(), const AssetPossessionSelect& possession = AssetPossessionSelect::any()) { begin(issuance, ownership, possession); } // Start iteration with given issuance and given ownership + possession filters (selects first record). - inline void begin(const AssetIssuanceId& issuance, const AssetOwnershipSelect& ownership = AssetOwnershipSelect::any(), const AssetPossessionSelect& possession = AssetPossessionSelect::any()); + inline void begin(const Asset& issuance, const AssetOwnershipSelect& ownership = AssetOwnershipSelect::any(), const AssetPossessionSelect& possession = AssetPossessionSelect::any()); // Return if iteration with next() has reached end. inline bool reachedEnd() const; @@ -782,7 +782,7 @@ namespace QPI union { // Number of votes for different options (0 = no change, 1 to N = yes to specific proposed value) - array optionVoteCount; + Array optionVoteCount; // Scalar voting result (currently only for proposalType VariableScalarMean, mean value of all valid votes) sint64 scalarVotingResult; @@ -876,7 +876,7 @@ namespace QPI struct ProposalDataV1 { // URL explaining proposal, zero-terminated string. - array url; + Array url; // Epoch, when proposal is active. For setProposal(), 0 means to clear proposal and non-zero means the current epoch. uint16 epoch; @@ -894,14 +894,14 @@ namespace QPI struct Transfer { id destination; - array amounts; // N first amounts are the proposed options (non-negative, sorted without duplicates), rest zero + Array amounts; // N first amounts are the proposed options (non-negative, sorted without duplicates), rest zero } transfer; // Used if type class is Variable and type is not VariableScalarMean struct VariableOptions { uint64 variable; // For identifying variable (interpreted by contract only) - array values; // N first amounts are proposed options sorted without duplicates, rest zero + Array values; // N first amounts are proposed options sorted without duplicates, rest zero } variableOptions; // Used if type is VariableScalarMean @@ -980,7 +980,7 @@ namespace QPI struct ProposalDataYesNo { // URL explaining proposal, zero-terminated string. - array url; + Array url; // Epoch, when proposal is active. For setProposal(), 0 means to clear proposal and non-zero means the current epoch. uint16 epoch; @@ -1298,7 +1298,7 @@ namespace QPI ) const; inline sint64 numberOfShares( - const AssetIssuanceId& issuanceId, + const Asset& issuanceId, const AssetOwnershipSelect& ownership = AssetOwnershipSelect::any(), const AssetPossessionSelect& possession = AssetPossessionSelect::any() ) const; @@ -1315,7 +1315,7 @@ namespace QPI bit signatureValidity( const id& entity, const id& digest, - const array& signature + const Array& signature ) const; inline uint32 tick( diff --git a/src/network_messages/assets.h b/src/network_messages/assets.h index dc6a04b1..9edaf16a 100644 --- a/src/network_messages/assets.h +++ b/src/network_messages/assets.h @@ -15,7 +15,7 @@ #define MOLE 5 #define SECOND 6 -struct Asset +struct AssetRecord { union { @@ -72,7 +72,7 @@ static_assert(sizeof(RequestIssuedAssets) == 32, "Something is wrong with the st struct RespondIssuedAssets { - Asset asset; + AssetRecord asset; unsigned int tick; unsigned int universeIndex; m256i siblings[ASSETS_DEPTH]; @@ -97,8 +97,8 @@ static_assert(sizeof(RequestOwnedAssets) == 32, "Something is wrong with the str struct RespondOwnedAssets { - Asset asset; - Asset issuanceAsset; + AssetRecord asset; + AssetRecord issuanceAsset; unsigned int tick; unsigned int universeIndex; m256i siblings[ASSETS_DEPTH]; @@ -123,9 +123,9 @@ static_assert(sizeof(RequestPossessedAssets) == 32, "Something is wrong with the struct RespondPossessedAssets { - Asset asset; - Asset ownershipAsset; - Asset issuanceAsset; + AssetRecord asset; + AssetRecord ownershipAsset; + AssetRecord issuanceAsset; unsigned int tick; unsigned int universeIndex; m256i siblings[ASSETS_DEPTH]; diff --git a/src/network_messages/broadcast_message.h b/src/network_messages/broadcast_message.h index cace6bbc..c58e6b82 100644 --- a/src/network_messages/broadcast_message.h +++ b/src/network_messages/broadcast_message.h @@ -3,6 +3,8 @@ #include "common_def.h" #define MESSAGE_TYPE_SOLUTION 0 +#define MESSAGE_TYPE_CUSTOM_MINING_TASK 1 +#define MESSAGE_TYPE_CUSTOM_MINING_SOLUTION 2 // TODO: documentation needed: // "A General Message type used to send/receive messages from/to peers." -> right? @@ -46,8 +48,5 @@ struct CustomMiningSolutionMessage m256i zero; m256i gammingNonce; - m256i codeFileTrailerDigest; - m256i dataFileTrailerDigest; - // Solution payload }; \ No newline at end of file diff --git a/src/public_settings.h b/src/public_settings.h index f4e7c951..bad67964 100644 --- a/src/public_settings.h +++ b/src/public_settings.h @@ -49,12 +49,12 @@ // Config options that should NOT be changed by operators #define VERSION_A 1 -#define VERSION_B 231 +#define VERSION_B 232 #define VERSION_C 0 // Epoch and initial tick for node startup -#define EPOCH 143 -#define TICK 18330000 +#define EPOCH 144 +#define TICK 18480000 #define ARBITRATOR "AFZPUAIYVPNUYGJRQVLUKOPPVLHAZQTGLYAAUUNBXFTVTAMSBKQBLEIEPCVJ" @@ -71,7 +71,7 @@ static unsigned short CONTRACT_FILE_NAME[] = L"contract????.???"; #define MAX_DURATION 9000000 #define NUMBER_OF_OPTIMIZATION_STEPS 60 #define NEURON_VALUE_LIMIT 1LL -#define SOLUTION_THRESHOLD_DEFAULT 136 +#define SOLUTION_THRESHOLD_DEFAULT 137 #define SOLUTION_SECURITY_DEPOSIT 1000000 diff --git a/src/qubic.cpp b/src/qubic.cpp index 35c1e374..92f209ed 100644 --- a/src/qubic.cpp +++ b/src/qubic.cpp @@ -1,5 +1,7 @@ #define SINGLE_COMPILE_UNIT +#define QEARN_UPDATE + // contract_def.h needs to be included first to make sure that contracts have minimal access #include "contract_core/contract_def.h" #include "contract_core/contract_exec.h" @@ -206,6 +208,9 @@ static volatile char minerScoreArrayLock = 0; static SpecialCommandGetMiningScoreRanking requestMiningScoreRanking; +static unsigned long long customMiningMessageCounters[NUMBER_OF_COMPUTORS] = { 0 }; + + // variables and declare for persisting state static volatile int requestPersistingNodeState = 0; static volatile int persistingNodeStateTickProcWaiting = 0; @@ -494,11 +499,19 @@ static void processBroadcastMessage(const unsigned long long processorNumber, Re } else { - for (unsigned int i = 0; i < sizeof(computorSeeds) / sizeof(computorSeeds[0]); i++) + for (unsigned int i = 0; i < NUMBER_OF_COMPUTORS; i++) { - if (request->destinationPublicKey == computorPublicKeys[i]) + if (request->sourcePublicKey == broadcastedComputors.computors.publicKeys[i]) { - // See CustomMiningSolutionMessage structure + unsigned char sharedKeyAndGammingNonce[64]; + bs->SetMem(sharedKeyAndGammingNonce, 32, 0); + bs->CopyMem(&sharedKeyAndGammingNonce[32], &request->gammingNonce, 32); + unsigned char gammingKey[32]; + KangarooTwelve64To32(sharedKeyAndGammingNonce, gammingKey); + if (gammingKey[0] == MESSAGE_TYPE_CUSTOM_MINING_SOLUTION) + { + customMiningMessageCounters[i]++; + } break; } @@ -1623,7 +1636,7 @@ unsigned char QPI::QpiContextFunctionCall::second() const return etalonTick.second; } -bool QPI::QpiContextFunctionCall::signatureValidity(const m256i& entity, const m256i& digest, const array& signature) const +bool QPI::QpiContextFunctionCall::signatureValidity(const m256i& entity, const m256i& digest, const Array& signature) const { return verify(entity.m256i_u8, digest.m256i_u8, reinterpret_cast(&signature)); } @@ -3003,6 +3016,684 @@ static void endEpoch() system.initialTick = system.tick; mainAuxStatus = ((mainAuxStatus & 1) << 1) | ((mainAuxStatus & 2) >> 1); + + // TODO: Remove after 451+ computors signal they know how to issue custom mining messages + customMiningMessageCounters[0] = 0; + customMiningMessageCounters[1] = 0; + customMiningMessageCounters[2] = 0; + customMiningMessageCounters[3] = 0; + customMiningMessageCounters[4] = 0; + customMiningMessageCounters[5] = 0; + customMiningMessageCounters[6] = 0; + customMiningMessageCounters[7] = 0; + customMiningMessageCounters[8] = 0; + customMiningMessageCounters[9] = 0; + customMiningMessageCounters[10] = 0; + customMiningMessageCounters[11] = 0; + customMiningMessageCounters[12] = 0; + customMiningMessageCounters[13] = 0; + customMiningMessageCounters[14] = 0; + customMiningMessageCounters[15] = 0; + customMiningMessageCounters[16] = 0; + customMiningMessageCounters[17] = 0; + customMiningMessageCounters[18] = 0; + customMiningMessageCounters[19] = 0; + customMiningMessageCounters[20] = 0; + customMiningMessageCounters[21] = 0; + customMiningMessageCounters[22] = 0; + customMiningMessageCounters[23] = 0; + customMiningMessageCounters[24] = 0; + customMiningMessageCounters[25] = 0; + customMiningMessageCounters[26] = 0; + customMiningMessageCounters[27] = 0; + customMiningMessageCounters[28] = 0; + customMiningMessageCounters[29] = 0; + customMiningMessageCounters[30] = 0; + customMiningMessageCounters[31] = 0; + customMiningMessageCounters[32] = 0; + customMiningMessageCounters[33] = 0; + customMiningMessageCounters[34] = 0; + customMiningMessageCounters[35] = 0; + customMiningMessageCounters[36] = 0; + customMiningMessageCounters[37] = 0; + customMiningMessageCounters[38] = 0; + customMiningMessageCounters[39] = 0; + customMiningMessageCounters[40] = 0; + customMiningMessageCounters[41] = 0; + customMiningMessageCounters[42] = 0; + customMiningMessageCounters[43] = 0; + customMiningMessageCounters[44] = 0; + customMiningMessageCounters[45] = 0; + customMiningMessageCounters[46] = 0; + customMiningMessageCounters[47] = 0; + customMiningMessageCounters[48] = 0; + customMiningMessageCounters[49] = 0; + customMiningMessageCounters[50] = 0; + customMiningMessageCounters[51] = 0; + customMiningMessageCounters[52] = 0; + customMiningMessageCounters[53] = 0; + customMiningMessageCounters[54] = 0; + customMiningMessageCounters[55] = 0; + customMiningMessageCounters[56] = 0; + customMiningMessageCounters[57] = 0; + customMiningMessageCounters[58] = 0; + customMiningMessageCounters[59] = 0; + customMiningMessageCounters[60] = 0; + customMiningMessageCounters[61] = 0; + customMiningMessageCounters[62] = 0; + customMiningMessageCounters[63] = 0; + customMiningMessageCounters[64] = 0; + customMiningMessageCounters[65] = 0; + customMiningMessageCounters[66] = 0; + customMiningMessageCounters[67] = 0; + customMiningMessageCounters[68] = 0; + customMiningMessageCounters[69] = 0; + customMiningMessageCounters[70] = 0; + customMiningMessageCounters[71] = 0; + customMiningMessageCounters[72] = 0; + customMiningMessageCounters[73] = 0; + customMiningMessageCounters[74] = 0; + customMiningMessageCounters[75] = 0; + customMiningMessageCounters[76] = 0; + customMiningMessageCounters[77] = 0; + customMiningMessageCounters[78] = 0; + customMiningMessageCounters[79] = 0; + customMiningMessageCounters[80] = 0; + customMiningMessageCounters[81] = 0; + customMiningMessageCounters[82] = 0; + customMiningMessageCounters[83] = 0; + customMiningMessageCounters[84] = 0; + customMiningMessageCounters[85] = 0; + customMiningMessageCounters[86] = 0; + customMiningMessageCounters[87] = 0; + customMiningMessageCounters[88] = 0; + customMiningMessageCounters[89] = 0; + customMiningMessageCounters[90] = 0; + customMiningMessageCounters[91] = 0; + customMiningMessageCounters[92] = 0; + customMiningMessageCounters[93] = 0; + customMiningMessageCounters[94] = 0; + customMiningMessageCounters[95] = 0; + customMiningMessageCounters[96] = 0; + customMiningMessageCounters[97] = 0; + customMiningMessageCounters[98] = 0; + customMiningMessageCounters[99] = 0; + customMiningMessageCounters[100] = 0; + customMiningMessageCounters[101] = 0; + customMiningMessageCounters[102] = 0; + customMiningMessageCounters[103] = 0; + customMiningMessageCounters[104] = 0; + customMiningMessageCounters[105] = 0; + customMiningMessageCounters[106] = 0; + customMiningMessageCounters[107] = 0; + customMiningMessageCounters[108] = 0; + customMiningMessageCounters[109] = 0; + customMiningMessageCounters[110] = 0; + customMiningMessageCounters[111] = 0; + customMiningMessageCounters[112] = 0; + customMiningMessageCounters[113] = 0; + customMiningMessageCounters[114] = 0; + customMiningMessageCounters[115] = 0; + customMiningMessageCounters[116] = 0; + customMiningMessageCounters[117] = 0; + customMiningMessageCounters[118] = 0; + customMiningMessageCounters[119] = 0; + customMiningMessageCounters[120] = 0; + customMiningMessageCounters[121] = 0; + customMiningMessageCounters[122] = 0; + customMiningMessageCounters[123] = 0; + customMiningMessageCounters[124] = 0; + customMiningMessageCounters[125] = 0; + customMiningMessageCounters[126] = 0; + customMiningMessageCounters[127] = 0; + customMiningMessageCounters[128] = 0; + customMiningMessageCounters[129] = 0; + customMiningMessageCounters[130] = 0; + customMiningMessageCounters[131] = 0; + customMiningMessageCounters[132] = 0; + customMiningMessageCounters[133] = 0; + customMiningMessageCounters[134] = 0; + customMiningMessageCounters[135] = 0; + customMiningMessageCounters[136] = 0; + customMiningMessageCounters[137] = 0; + customMiningMessageCounters[138] = 0; + customMiningMessageCounters[139] = 0; + customMiningMessageCounters[140] = 0; + customMiningMessageCounters[141] = 0; + customMiningMessageCounters[142] = 0; + customMiningMessageCounters[143] = 0; + customMiningMessageCounters[144] = 0; + customMiningMessageCounters[145] = 0; + customMiningMessageCounters[146] = 0; + customMiningMessageCounters[147] = 0; + customMiningMessageCounters[148] = 0; + customMiningMessageCounters[149] = 0; + customMiningMessageCounters[150] = 0; + customMiningMessageCounters[151] = 0; + customMiningMessageCounters[152] = 0; + customMiningMessageCounters[153] = 0; + customMiningMessageCounters[154] = 0; + customMiningMessageCounters[155] = 0; + customMiningMessageCounters[156] = 0; + customMiningMessageCounters[157] = 0; + customMiningMessageCounters[158] = 0; + customMiningMessageCounters[159] = 0; + customMiningMessageCounters[160] = 0; + customMiningMessageCounters[161] = 0; + customMiningMessageCounters[162] = 0; + customMiningMessageCounters[163] = 0; + customMiningMessageCounters[164] = 0; + customMiningMessageCounters[165] = 0; + customMiningMessageCounters[166] = 0; + customMiningMessageCounters[167] = 0; + customMiningMessageCounters[168] = 0; + customMiningMessageCounters[169] = 0; + customMiningMessageCounters[170] = 0; + customMiningMessageCounters[171] = 0; + customMiningMessageCounters[172] = 0; + customMiningMessageCounters[173] = 0; + customMiningMessageCounters[174] = 0; + customMiningMessageCounters[175] = 0; + customMiningMessageCounters[176] = 0; + customMiningMessageCounters[177] = 0; + customMiningMessageCounters[178] = 0; + customMiningMessageCounters[179] = 0; + customMiningMessageCounters[180] = 0; + customMiningMessageCounters[181] = 0; + customMiningMessageCounters[182] = 0; + customMiningMessageCounters[183] = 0; + customMiningMessageCounters[184] = 0; + customMiningMessageCounters[185] = 0; + customMiningMessageCounters[186] = 0; + customMiningMessageCounters[187] = 0; + customMiningMessageCounters[188] = 0; + customMiningMessageCounters[189] = 0; + customMiningMessageCounters[190] = 0; + customMiningMessageCounters[191] = 0; + customMiningMessageCounters[192] = 0; + customMiningMessageCounters[193] = 0; + customMiningMessageCounters[194] = 0; + customMiningMessageCounters[195] = 0; + customMiningMessageCounters[196] = 0; + customMiningMessageCounters[197] = 0; + customMiningMessageCounters[198] = 0; + customMiningMessageCounters[199] = 0; + customMiningMessageCounters[200] = 0; + customMiningMessageCounters[201] = 0; + customMiningMessageCounters[202] = 0; + customMiningMessageCounters[203] = 0; + customMiningMessageCounters[204] = 0; + customMiningMessageCounters[205] = 0; + customMiningMessageCounters[206] = 0; + customMiningMessageCounters[207] = 0; + customMiningMessageCounters[208] = 0; + customMiningMessageCounters[209] = 0; + customMiningMessageCounters[210] = 0; + customMiningMessageCounters[211] = 0; + customMiningMessageCounters[212] = 0; + customMiningMessageCounters[213] = 0; + customMiningMessageCounters[214] = 0; + customMiningMessageCounters[215] = 0; + customMiningMessageCounters[216] = 0; + customMiningMessageCounters[217] = 0; + customMiningMessageCounters[218] = 0; + customMiningMessageCounters[219] = 0; + customMiningMessageCounters[220] = 0; + customMiningMessageCounters[221] = 0; + customMiningMessageCounters[222] = 0; + customMiningMessageCounters[223] = 0; + customMiningMessageCounters[224] = 0; + customMiningMessageCounters[225] = 0; + customMiningMessageCounters[226] = 0; + customMiningMessageCounters[227] = 0; + customMiningMessageCounters[228] = 0; + customMiningMessageCounters[229] = 0; + customMiningMessageCounters[230] = 0; + customMiningMessageCounters[231] = 0; + customMiningMessageCounters[232] = 0; + customMiningMessageCounters[233] = 0; + customMiningMessageCounters[234] = 0; + customMiningMessageCounters[235] = 0; + customMiningMessageCounters[236] = 0; + customMiningMessageCounters[237] = 0; + customMiningMessageCounters[238] = 0; + customMiningMessageCounters[239] = 0; + customMiningMessageCounters[240] = 0; + customMiningMessageCounters[241] = 0; + customMiningMessageCounters[242] = 0; + customMiningMessageCounters[243] = 0; + customMiningMessageCounters[244] = 0; + customMiningMessageCounters[245] = 0; + customMiningMessageCounters[246] = 0; + customMiningMessageCounters[247] = 0; + customMiningMessageCounters[248] = 0; + customMiningMessageCounters[249] = 0; + customMiningMessageCounters[250] = 0; + customMiningMessageCounters[251] = 0; + customMiningMessageCounters[252] = 0; + customMiningMessageCounters[253] = 0; + customMiningMessageCounters[254] = 0; + customMiningMessageCounters[255] = 0; + customMiningMessageCounters[256] = 0; + customMiningMessageCounters[257] = 0; + customMiningMessageCounters[258] = 0; + customMiningMessageCounters[259] = 0; + customMiningMessageCounters[260] = 0; + customMiningMessageCounters[261] = 0; + customMiningMessageCounters[262] = 0; + customMiningMessageCounters[263] = 0; + customMiningMessageCounters[264] = 0; + customMiningMessageCounters[265] = 0; + customMiningMessageCounters[266] = 0; + customMiningMessageCounters[267] = 0; + customMiningMessageCounters[268] = 0; + customMiningMessageCounters[269] = 0; + customMiningMessageCounters[270] = 0; + customMiningMessageCounters[271] = 0; + customMiningMessageCounters[272] = 0; + customMiningMessageCounters[273] = 0; + customMiningMessageCounters[274] = 0; + customMiningMessageCounters[275] = 0; + customMiningMessageCounters[276] = 0; + customMiningMessageCounters[277] = 0; + customMiningMessageCounters[278] = 0; + customMiningMessageCounters[279] = 0; + customMiningMessageCounters[280] = 0; + customMiningMessageCounters[281] = 0; + customMiningMessageCounters[282] = 0; + customMiningMessageCounters[283] = 0; + customMiningMessageCounters[284] = 0; + customMiningMessageCounters[285] = 0; + customMiningMessageCounters[286] = 0; + customMiningMessageCounters[287] = 0; + customMiningMessageCounters[288] = 0; + customMiningMessageCounters[289] = 0; + customMiningMessageCounters[290] = 0; + customMiningMessageCounters[291] = 0; + customMiningMessageCounters[292] = 0; + customMiningMessageCounters[293] = 0; + customMiningMessageCounters[294] = 0; + customMiningMessageCounters[295] = 0; + customMiningMessageCounters[296] = 0; + customMiningMessageCounters[297] = 0; + customMiningMessageCounters[298] = 0; + customMiningMessageCounters[299] = 0; + customMiningMessageCounters[300] = 0; + customMiningMessageCounters[301] = 0; + customMiningMessageCounters[302] = 0; + customMiningMessageCounters[303] = 0; + customMiningMessageCounters[304] = 0; + customMiningMessageCounters[305] = 0; + customMiningMessageCounters[306] = 0; + customMiningMessageCounters[307] = 0; + customMiningMessageCounters[308] = 0; + customMiningMessageCounters[309] = 0; + customMiningMessageCounters[310] = 0; + customMiningMessageCounters[311] = 0; + customMiningMessageCounters[312] = 0; + customMiningMessageCounters[313] = 0; + customMiningMessageCounters[314] = 0; + customMiningMessageCounters[315] = 0; + customMiningMessageCounters[316] = 0; + customMiningMessageCounters[317] = 0; + customMiningMessageCounters[318] = 0; + customMiningMessageCounters[319] = 0; + customMiningMessageCounters[320] = 0; + customMiningMessageCounters[321] = 0; + customMiningMessageCounters[322] = 0; + customMiningMessageCounters[323] = 0; + customMiningMessageCounters[324] = 0; + customMiningMessageCounters[325] = 0; + customMiningMessageCounters[326] = 0; + customMiningMessageCounters[327] = 0; + customMiningMessageCounters[328] = 0; + customMiningMessageCounters[329] = 0; + customMiningMessageCounters[330] = 0; + customMiningMessageCounters[331] = 0; + customMiningMessageCounters[332] = 0; + customMiningMessageCounters[333] = 0; + customMiningMessageCounters[334] = 0; + customMiningMessageCounters[335] = 0; + customMiningMessageCounters[336] = 0; + customMiningMessageCounters[337] = 0; + customMiningMessageCounters[338] = 0; + customMiningMessageCounters[339] = 0; + customMiningMessageCounters[340] = 0; + customMiningMessageCounters[341] = 0; + customMiningMessageCounters[342] = 0; + customMiningMessageCounters[343] = 0; + customMiningMessageCounters[344] = 0; + customMiningMessageCounters[345] = 0; + customMiningMessageCounters[346] = 0; + customMiningMessageCounters[347] = 0; + customMiningMessageCounters[348] = 0; + customMiningMessageCounters[349] = 0; + customMiningMessageCounters[350] = 0; + customMiningMessageCounters[351] = 0; + customMiningMessageCounters[352] = 0; + customMiningMessageCounters[353] = 0; + customMiningMessageCounters[354] = 0; + customMiningMessageCounters[355] = 0; + customMiningMessageCounters[356] = 0; + customMiningMessageCounters[357] = 0; + customMiningMessageCounters[358] = 0; + customMiningMessageCounters[359] = 0; + customMiningMessageCounters[360] = 0; + customMiningMessageCounters[361] = 0; + customMiningMessageCounters[362] = 0; + customMiningMessageCounters[363] = 0; + customMiningMessageCounters[364] = 0; + customMiningMessageCounters[365] = 0; + customMiningMessageCounters[366] = 0; + customMiningMessageCounters[367] = 0; + customMiningMessageCounters[368] = 0; + customMiningMessageCounters[369] = 0; + customMiningMessageCounters[370] = 0; + customMiningMessageCounters[371] = 0; + customMiningMessageCounters[372] = 0; + customMiningMessageCounters[373] = 0; + customMiningMessageCounters[374] = 0; + customMiningMessageCounters[375] = 0; + customMiningMessageCounters[376] = 0; + customMiningMessageCounters[377] = 0; + customMiningMessageCounters[378] = 0; + customMiningMessageCounters[379] = 0; + customMiningMessageCounters[380] = 0; + customMiningMessageCounters[381] = 0; + customMiningMessageCounters[382] = 0; + customMiningMessageCounters[383] = 0; + customMiningMessageCounters[384] = 0; + customMiningMessageCounters[385] = 0; + customMiningMessageCounters[386] = 0; + customMiningMessageCounters[387] = 0; + customMiningMessageCounters[388] = 0; + customMiningMessageCounters[389] = 0; + customMiningMessageCounters[390] = 0; + customMiningMessageCounters[391] = 0; + customMiningMessageCounters[392] = 0; + customMiningMessageCounters[393] = 0; + customMiningMessageCounters[394] = 0; + customMiningMessageCounters[395] = 0; + customMiningMessageCounters[396] = 0; + customMiningMessageCounters[397] = 0; + customMiningMessageCounters[398] = 0; + customMiningMessageCounters[399] = 0; + customMiningMessageCounters[400] = 0; + customMiningMessageCounters[401] = 0; + customMiningMessageCounters[402] = 0; + customMiningMessageCounters[403] = 0; + customMiningMessageCounters[404] = 0; + customMiningMessageCounters[405] = 0; + customMiningMessageCounters[406] = 0; + customMiningMessageCounters[407] = 0; + customMiningMessageCounters[408] = 0; + customMiningMessageCounters[409] = 0; + customMiningMessageCounters[410] = 0; + customMiningMessageCounters[411] = 0; + customMiningMessageCounters[412] = 0; + customMiningMessageCounters[413] = 0; + customMiningMessageCounters[414] = 0; + customMiningMessageCounters[415] = 0; + customMiningMessageCounters[416] = 0; + customMiningMessageCounters[417] = 0; + customMiningMessageCounters[418] = 0; + customMiningMessageCounters[419] = 0; + customMiningMessageCounters[420] = 0; + customMiningMessageCounters[421] = 0; + customMiningMessageCounters[422] = 0; + customMiningMessageCounters[423] = 0; + customMiningMessageCounters[424] = 0; + customMiningMessageCounters[425] = 0; + customMiningMessageCounters[426] = 0; + customMiningMessageCounters[427] = 0; + customMiningMessageCounters[428] = 0; + customMiningMessageCounters[429] = 0; + customMiningMessageCounters[430] = 0; + customMiningMessageCounters[431] = 0; + customMiningMessageCounters[432] = 0; + customMiningMessageCounters[433] = 0; + customMiningMessageCounters[434] = 0; + customMiningMessageCounters[435] = 0; + customMiningMessageCounters[436] = 0; + customMiningMessageCounters[437] = 0; + customMiningMessageCounters[438] = 0; + customMiningMessageCounters[439] = 0; + customMiningMessageCounters[440] = 0; + customMiningMessageCounters[441] = 0; + customMiningMessageCounters[442] = 0; + customMiningMessageCounters[443] = 0; + customMiningMessageCounters[444] = 0; + customMiningMessageCounters[445] = 0; + customMiningMessageCounters[446] = 0; + customMiningMessageCounters[447] = 0; + customMiningMessageCounters[448] = 0; + customMiningMessageCounters[449] = 0; + customMiningMessageCounters[450] = 0; + customMiningMessageCounters[451] = 0; + customMiningMessageCounters[452] = 0; + customMiningMessageCounters[453] = 0; + customMiningMessageCounters[454] = 0; + customMiningMessageCounters[455] = 0; + customMiningMessageCounters[456] = 0; + customMiningMessageCounters[457] = 0; + customMiningMessageCounters[458] = 0; + customMiningMessageCounters[459] = 0; + customMiningMessageCounters[460] = 0; + customMiningMessageCounters[461] = 0; + customMiningMessageCounters[462] = 0; + customMiningMessageCounters[463] = 0; + customMiningMessageCounters[464] = 0; + customMiningMessageCounters[465] = 0; + customMiningMessageCounters[466] = 0; + customMiningMessageCounters[467] = 0; + customMiningMessageCounters[468] = 0; + customMiningMessageCounters[469] = 0; + customMiningMessageCounters[470] = 0; + customMiningMessageCounters[471] = 0; + customMiningMessageCounters[472] = 0; + customMiningMessageCounters[473] = 0; + customMiningMessageCounters[474] = 0; + customMiningMessageCounters[475] = 0; + customMiningMessageCounters[476] = 0; + customMiningMessageCounters[477] = 0; + customMiningMessageCounters[478] = 0; + customMiningMessageCounters[479] = 0; + customMiningMessageCounters[480] = 0; + customMiningMessageCounters[481] = 0; + customMiningMessageCounters[482] = 0; + customMiningMessageCounters[483] = 0; + customMiningMessageCounters[484] = 0; + customMiningMessageCounters[485] = 0; + customMiningMessageCounters[486] = 0; + customMiningMessageCounters[487] = 0; + customMiningMessageCounters[488] = 0; + customMiningMessageCounters[489] = 0; + customMiningMessageCounters[490] = 0; + customMiningMessageCounters[491] = 0; + customMiningMessageCounters[492] = 0; + customMiningMessageCounters[493] = 0; + customMiningMessageCounters[494] = 0; + customMiningMessageCounters[495] = 0; + customMiningMessageCounters[496] = 0; + customMiningMessageCounters[497] = 0; + customMiningMessageCounters[498] = 0; + customMiningMessageCounters[499] = 0; + customMiningMessageCounters[500] = 0; + customMiningMessageCounters[501] = 0; + customMiningMessageCounters[502] = 0; + customMiningMessageCounters[503] = 0; + customMiningMessageCounters[504] = 0; + customMiningMessageCounters[505] = 0; + customMiningMessageCounters[506] = 0; + customMiningMessageCounters[507] = 0; + customMiningMessageCounters[508] = 0; + customMiningMessageCounters[509] = 0; + customMiningMessageCounters[510] = 0; + customMiningMessageCounters[511] = 0; + customMiningMessageCounters[512] = 0; + customMiningMessageCounters[513] = 0; + customMiningMessageCounters[514] = 0; + customMiningMessageCounters[515] = 0; + customMiningMessageCounters[516] = 0; + customMiningMessageCounters[517] = 0; + customMiningMessageCounters[518] = 0; + customMiningMessageCounters[519] = 0; + customMiningMessageCounters[520] = 0; + customMiningMessageCounters[521] = 0; + customMiningMessageCounters[522] = 0; + customMiningMessageCounters[523] = 0; + customMiningMessageCounters[524] = 0; + customMiningMessageCounters[525] = 0; + customMiningMessageCounters[526] = 0; + customMiningMessageCounters[527] = 0; + customMiningMessageCounters[528] = 0; + customMiningMessageCounters[529] = 0; + customMiningMessageCounters[530] = 0; + customMiningMessageCounters[531] = 0; + customMiningMessageCounters[532] = 0; + customMiningMessageCounters[533] = 0; + customMiningMessageCounters[534] = 0; + customMiningMessageCounters[535] = 0; + customMiningMessageCounters[536] = 0; + customMiningMessageCounters[537] = 0; + customMiningMessageCounters[538] = 0; + customMiningMessageCounters[539] = 0; + customMiningMessageCounters[540] = 0; + customMiningMessageCounters[541] = 0; + customMiningMessageCounters[542] = 0; + customMiningMessageCounters[543] = 0; + customMiningMessageCounters[544] = 0; + customMiningMessageCounters[545] = 0; + customMiningMessageCounters[546] = 0; + customMiningMessageCounters[547] = 0; + customMiningMessageCounters[548] = 0; + customMiningMessageCounters[549] = 0; + customMiningMessageCounters[550] = 0; + customMiningMessageCounters[551] = 0; + customMiningMessageCounters[552] = 0; + customMiningMessageCounters[553] = 0; + customMiningMessageCounters[554] = 0; + customMiningMessageCounters[555] = 0; + customMiningMessageCounters[556] = 0; + customMiningMessageCounters[557] = 0; + customMiningMessageCounters[558] = 0; + customMiningMessageCounters[559] = 0; + customMiningMessageCounters[560] = 0; + customMiningMessageCounters[561] = 0; + customMiningMessageCounters[562] = 0; + customMiningMessageCounters[563] = 0; + customMiningMessageCounters[564] = 0; + customMiningMessageCounters[565] = 0; + customMiningMessageCounters[566] = 0; + customMiningMessageCounters[567] = 0; + customMiningMessageCounters[568] = 0; + customMiningMessageCounters[569] = 0; + customMiningMessageCounters[570] = 0; + customMiningMessageCounters[571] = 0; + customMiningMessageCounters[572] = 0; + customMiningMessageCounters[573] = 0; + customMiningMessageCounters[574] = 0; + customMiningMessageCounters[575] = 0; + customMiningMessageCounters[576] = 0; + customMiningMessageCounters[577] = 0; + customMiningMessageCounters[578] = 0; + customMiningMessageCounters[579] = 0; + customMiningMessageCounters[580] = 0; + customMiningMessageCounters[581] = 0; + customMiningMessageCounters[582] = 0; + customMiningMessageCounters[583] = 0; + customMiningMessageCounters[584] = 0; + customMiningMessageCounters[585] = 0; + customMiningMessageCounters[586] = 0; + customMiningMessageCounters[587] = 0; + customMiningMessageCounters[588] = 0; + customMiningMessageCounters[589] = 0; + customMiningMessageCounters[590] = 0; + customMiningMessageCounters[591] = 0; + customMiningMessageCounters[592] = 0; + customMiningMessageCounters[593] = 0; + customMiningMessageCounters[594] = 0; + customMiningMessageCounters[595] = 0; + customMiningMessageCounters[596] = 0; + customMiningMessageCounters[597] = 0; + customMiningMessageCounters[598] = 0; + customMiningMessageCounters[599] = 0; + customMiningMessageCounters[600] = 0; + customMiningMessageCounters[601] = 0; + customMiningMessageCounters[602] = 0; + customMiningMessageCounters[603] = 0; + customMiningMessageCounters[604] = 0; + customMiningMessageCounters[605] = 0; + customMiningMessageCounters[606] = 0; + customMiningMessageCounters[607] = 0; + customMiningMessageCounters[608] = 0; + customMiningMessageCounters[609] = 0; + customMiningMessageCounters[610] = 0; + customMiningMessageCounters[611] = 0; + customMiningMessageCounters[612] = 0; + customMiningMessageCounters[613] = 0; + customMiningMessageCounters[614] = 0; + customMiningMessageCounters[615] = 0; + customMiningMessageCounters[616] = 0; + customMiningMessageCounters[617] = 0; + customMiningMessageCounters[618] = 0; + customMiningMessageCounters[619] = 0; + customMiningMessageCounters[620] = 0; + customMiningMessageCounters[621] = 0; + customMiningMessageCounters[622] = 0; + customMiningMessageCounters[623] = 0; + customMiningMessageCounters[624] = 0; + customMiningMessageCounters[625] = 0; + customMiningMessageCounters[626] = 0; + customMiningMessageCounters[627] = 0; + customMiningMessageCounters[628] = 0; + customMiningMessageCounters[629] = 0; + customMiningMessageCounters[630] = 0; + customMiningMessageCounters[631] = 0; + customMiningMessageCounters[632] = 0; + customMiningMessageCounters[633] = 0; + customMiningMessageCounters[634] = 0; + customMiningMessageCounters[635] = 0; + customMiningMessageCounters[636] = 0; + customMiningMessageCounters[637] = 0; + customMiningMessageCounters[638] = 0; + customMiningMessageCounters[639] = 0; + customMiningMessageCounters[640] = 0; + customMiningMessageCounters[641] = 0; + customMiningMessageCounters[642] = 0; + customMiningMessageCounters[643] = 0; + customMiningMessageCounters[644] = 0; + customMiningMessageCounters[645] = 0; + customMiningMessageCounters[646] = 0; + customMiningMessageCounters[647] = 0; + customMiningMessageCounters[648] = 0; + customMiningMessageCounters[649] = 0; + customMiningMessageCounters[650] = 0; + customMiningMessageCounters[651] = 0; + customMiningMessageCounters[652] = 0; + customMiningMessageCounters[653] = 0; + customMiningMessageCounters[654] = 0; + customMiningMessageCounters[655] = 0; + customMiningMessageCounters[656] = 0; + customMiningMessageCounters[657] = 0; + customMiningMessageCounters[658] = 0; + customMiningMessageCounters[659] = 0; + customMiningMessageCounters[660] = 0; + customMiningMessageCounters[661] = 0; + customMiningMessageCounters[662] = 0; + customMiningMessageCounters[663] = 0; + customMiningMessageCounters[664] = 0; + customMiningMessageCounters[665] = 0; + customMiningMessageCounters[666] = 0; + customMiningMessageCounters[667] = 0; + customMiningMessageCounters[668] = 0; + customMiningMessageCounters[669] = 0; + customMiningMessageCounters[670] = 0; + customMiningMessageCounters[671] = 0; + customMiningMessageCounters[672] = 0; + customMiningMessageCounters[673] = 0; + customMiningMessageCounters[674] = 0; + customMiningMessageCounters[675] = 0; } @@ -5400,6 +6091,19 @@ static void processKeyPresses() appendNumber(message, minimumCandidateScore, TRUE); appendText(message, L")."); logToConsole(message); + + setText(message, L"Custom mining solution counters:"); + const CHAR16 alphabet[26][2] = { L"A", L"B", L"C", L"D", L"E", L"F", L"G", L"H", L"I", L"J", L"K", L"L", L"M", L"N", L"O", L"P", L"Q", L"R", L"S", L"T", L"U", L"V", L"W", L"X", L"Y", L"Z" }; + for (unsigned int i = 0; i < NUMBER_OF_COMPUTORS; i++) + { + appendText(message, L" "); + appendText(message, alphabet[i / 26]); + appendText(message, alphabet[i % 26]); + appendText(message, L"="); + appendNumber(message, customMiningMessageCounters[i], FALSE); + } + appendText(message, L"."); + logToConsole(message); } break; diff --git a/test/assets.cpp b/test/assets.cpp index 5699bfd6..f5685669 100644 --- a/test/assets.cpp +++ b/test/assets.cpp @@ -126,7 +126,7 @@ class AssetsTest : public AssetStorage issuanceIdx = indexLists.issuancesFirstIdx; while (issuanceIdx != NO_ASSET_INDEX) { - AssetIssuanceId issuanceId(assets[issuanceIdx].varStruct.issuance.publicKey, assetNameFromString(assets[issuanceIdx].varStruct.issuance.name)); + Asset issuanceId(assets[issuanceIdx].varStruct.issuance.publicKey, assetNameFromString(assets[issuanceIdx].varStruct.issuance.name)); long long numOfSharesOwned = 0, numOfSharesPossessed = 0; for (AssetOwnershipIterator iter(issuanceId); !iter.reachedEnd(); iter.next()) { @@ -159,7 +159,7 @@ TEST(TestCoreAssets, CheckLoadFile) struct IssuanceTestData { - AssetIssuanceId id; + Asset id; unsigned short managingContract; unsigned int universeIdx; long long numOfShares; diff --git a/test/contract_qearn.cpp b/test/contract_qearn.cpp index d23f9266..e3ca52ea 100644 --- a/test/contract_qearn.cpp +++ b/test/contract_qearn.cpp @@ -1,3 +1,899 @@ +#define QEARN_UPDATE + +#ifdef QEARN_UPDATE + +#define NO_UEFI + +#include +#include + +#include "contract_testing.h" + +#define PRINT_TEST_INFO 0 + +// test config: +// - 0 is fastest +// - 1 to enable more tests with random lock/unlock +// - 2 to enable even more tests with random lock/unlock +// - 3 to also check values more often (expensive functions) +// - 4 to also test out-of-user error +#define LARGE_SCALE_TEST 0 + + +static const id QEARN_CONTRACT_ID(QEARN_CONTRACT_INDEX, 0, 0, 0); + +static std::mt19937_64 rand64; + +static id getUser(unsigned long long i); +static unsigned long long random(unsigned long long maxValue); + +static std::vector fullyUnlockedAmount; +static std::vector fullyUnlockedUser; + + +class QearnChecker : public QEARN +{ +public: + void checkLockerArray(bool beforeEndEpoch, bool printInfo = false) + { + // check that locker array is in consistent state + std::map epochTotalLocked; + uint32 minEpoch = 0xffff; + uint32 maxEpoch = 0; + for (uint64 idx = 0; idx < locker.capacity(); ++idx) + { + const QEARN::LockInfo& lock = locker.get(idx); + if (lock._lockedAmount == 0) + { + EXPECT_TRUE(isZero(lock.ID)); + EXPECT_EQ(lock._lockedEpoch, 0); + } + else + { + EXPECT_GT(lock._lockedAmount, QEARN_MINIMUM_LOCKING_AMOUNT); + EXPECT_LE(lock._lockedAmount, QEARN_MAX_LOCK_AMOUNT); + EXPECT_FALSE(isZero(lock.ID)); + const QEARN::EpochIndexInfo& epochRange = _epochIndex.get(lock._lockedEpoch); + EXPECT_GE(idx, epochRange.startIndex); + EXPECT_LT(idx, epochRange.endIndex); + epochTotalLocked[lock._lockedEpoch] += lock._lockedAmount; + + minEpoch = std::min(minEpoch, lock._lockedEpoch); + maxEpoch = std::max(minEpoch, lock._lockedEpoch); + } + } + + const uint32 beginEpoch = std::max((int)contractDescriptions[QEARN_CONTRACT_INDEX].constructionEpoch, system.epoch - 52); + EXPECT_LE(beginEpoch, minEpoch); + EXPECT_LE(maxEpoch, uint32(system.epoch)); + + if (PRINT_TEST_INFO) + { + const char * beforeAfterStr = (beforeEndEpoch) ? "Before" : "After"; + std::cout << "--- " << beforeAfterStr << " END_EPOCH in epoch " << system.epoch << std::endl; + } + + for (uint32 epoch = beginEpoch; epoch <= system.epoch; ++epoch) + { + const QEARN::RoundInfo& currentRoundInfo = _currentRoundInfo.get(epoch); + //if (!currentRoundInfo._Epoch_Bonus_Amount && !currentRoundInfo._Total_Locked_Amount) + // continue; + unsigned long long totalLocked = epochTotalLocked[epoch]; + if (printInfo) + { + std::cout << "Total locked amount in epoch " << epoch << " = " << totalLocked << ", total bonus " << currentRoundInfo._epochBonusAmount << std::endl; + } + if (beforeEndEpoch || epoch != system.epoch - 52) + EXPECT_EQ(currentRoundInfo._totalLockedAmount, totalLocked); + } + + // check that old epoch indices have been reset + for (uint32 epoch = contractDescriptions[QEARN_CONTRACT_INDEX].constructionEpoch; epoch < beginEpoch; ++epoch) + { + EXPECT_EQ(this->_epochIndex.get(epoch).startIndex, this->_epochIndex.get(epoch).endIndex); + } + } + + void checkGetUnlockedInfo(uint32 epoch) + { + fullyUnlockedAmount.clear(); + fullyUnlockedUser.clear(); + + const QEARN::EpochIndexInfo& epochIndex = _epochIndex.get(epoch); + for(uint64 idx = epochIndex.startIndex; idx < epochIndex.endIndex; ++idx) + { + if(locker.get(idx)._lockedAmount != 0) + { + fullyUnlockedAmount.push_back(locker.get(idx)._lockedAmount); + fullyUnlockedUser.push_back(locker.get(idx).ID); + } + } + } + + void checkFullyUnlockedAmount() + { + for(uint32 idx = 0; idx < _fullyUnlockedCnt; idx++) + { + const QEARN::HistoryInfo& FullyUnlockedInfo = fullyUnlocker.get(idx); + + EXPECT_EQ(fullyUnlockedAmount[idx], FullyUnlockedInfo._unlockedAmount); + EXPECT_EQ(fullyUnlockedUser[idx], FullyUnlockedInfo._unlockedID); + } + } + + void checkStatsPerEpoch(getBurnedAndBoostedStatsPerEpoch_output result, uint16 epoch) + { + EXPECT_EQ(result.boostedAmount, statsInfo.get(epoch).boostedAmount); + EXPECT_EQ(result.burnedAmount, statsInfo.get(epoch).burnedAmount); + EXPECT_EQ(result.rewardedAmount, statsInfo.get(epoch).rewardedAmount); + EXPECT_EQ(result.boostedPercent, div(result.boostedAmount * 10000000, _initialRoundInfo.get(epoch)._epochBonusAmount)); + EXPECT_EQ(result.burnedPercent, div(result.burnedAmount * 10000000, _initialRoundInfo.get(epoch)._epochBonusAmount)); + EXPECT_EQ(result.rewardedPercent, div(result.rewardedAmount * 10000000, _initialRoundInfo.get(epoch)._epochBonusAmount)); + } + + void checkStatsForAll(getBurnedAndBoostedStats_output result) + { + uint64 totalBurnedAmountInSC = 0; + uint64 totalBoostedAmountInSC = 0; + uint64 totalRewardedAmountInSC = 0; + uint64 sumBurnedPercent = 0; + uint64 sumBoostedPercent = 0; + uint64 sumRewardedPercent = 0; + + for(uint32 epoch = 138 ; epoch < system.epoch; epoch++) + { + totalBurnedAmountInSC += statsInfo.get(epoch).burnedAmount; + totalBoostedAmountInSC += statsInfo.get(epoch).boostedAmount; + totalRewardedAmountInSC += statsInfo.get(epoch).rewardedAmount; + + sumBurnedPercent += div(statsInfo.get(epoch).burnedAmount * 10000000, _initialRoundInfo.get(epoch)._epochBonusAmount); + sumBoostedPercent += div(statsInfo.get(epoch).boostedAmount * 10000000, _initialRoundInfo.get(epoch)._epochBonusAmount); + sumRewardedPercent += div(statsInfo.get(epoch).rewardedAmount * 10000000, _initialRoundInfo.get(epoch)._epochBonusAmount); + } + + EXPECT_EQ(result.boostedAmount, totalBoostedAmountInSC); + EXPECT_EQ(result.burnedAmount, totalBurnedAmountInSC); + EXPECT_EQ(result.rewardedAmount, totalRewardedAmountInSC); + EXPECT_EQ(result.averageBoostedPercent, div(sumBoostedPercent, system.epoch - 138ULL)); + EXPECT_EQ(result.averageBurnedPercent, div(sumBurnedPercent, system.epoch - 138ULL)); + EXPECT_EQ(result.averageRewardedPercent, div(sumRewardedPercent, system.epoch - 138ULL)); + } +}; + +class ContractTestingQearn : protected ContractTesting +{ + struct UnlockTableEntry + { + unsigned long long rewardPercent; + unsigned long long burnPercent; + }; + std::vector epochChangesToUnlockParams; + +public: + ContractTestingQearn() + { + INIT_CONTRACT(QEARN); + initEmptySpectrum(); + qLogger::initLogging(); + rand64.seed(42); + + for (unsigned int epChanges = 0; epChanges <= 52; ++epChanges) + { + if (epChanges <= 4) + epochChangesToUnlockParams.push_back(UnlockTableEntry{ 0, 0 }); + else if (epChanges <= 12) + epochChangesToUnlockParams.push_back(UnlockTableEntry{ 5, 45 }); + else if (epChanges <= 16) + epochChangesToUnlockParams.push_back(UnlockTableEntry{ 10, 45 }); + else if (epChanges <= 20) + epochChangesToUnlockParams.push_back(UnlockTableEntry{ 15, 40 }); + else if (epChanges <= 24) + epochChangesToUnlockParams.push_back(UnlockTableEntry{ 20, 40 }); + else if (epChanges <= 28) + epochChangesToUnlockParams.push_back(UnlockTableEntry{ 25, 35 }); + else if (epChanges <= 32) + epochChangesToUnlockParams.push_back(UnlockTableEntry{ 30, 35 }); + else if (epChanges <= 36) + epochChangesToUnlockParams.push_back(UnlockTableEntry{ 35, 35 }); + else if (epChanges <= 40) + epochChangesToUnlockParams.push_back(UnlockTableEntry{ 40, 30 }); + else if (epChanges <= 44) + epochChangesToUnlockParams.push_back(UnlockTableEntry{ 45, 30 }); + else if (epChanges <= 48) + epochChangesToUnlockParams.push_back(UnlockTableEntry{ 50, 30 }); + else if (epChanges <= 52) + epochChangesToUnlockParams.push_back(UnlockTableEntry{ 55, 25 }); + else + epochChangesToUnlockParams.push_back(UnlockTableEntry{ 100, 0 }); + } + } + + ~ContractTestingQearn() + { + qLogger::deinitLogging(); + } + + QearnChecker* getState() + { + return (QearnChecker*)contractStates[QEARN_CONTRACT_INDEX]; + } + + void beginEpoch(bool expectSuccess = true) + { + callSystemProcedure(QEARN_CONTRACT_INDEX, BEGIN_EPOCH, expectSuccess); + + // If there is no entry for this epoch in allEpochData, create one with default init (all 0) + allEpochData[system.epoch]; + } + + void endEpoch(bool expectSuccess = true) + { + callSystemProcedure(QEARN_CONTRACT_INDEX, END_EPOCH, expectSuccess); + } + + QEARN::getLockInfoPerEpoch_output getLockInfoPerEpoch(uint16 epoch) const + { + QEARN::getLockInfoPerEpoch_input input{ epoch }; + QEARN::getLockInfoPerEpoch_output output; + callFunction(QEARN_CONTRACT_INDEX, 1, input, output); + return output; + } + + uint64 getUserLockedInfo(uint16 epoch, const id& user) const + { + QEARN::getUserLockedInfo_input input; + input.epoch = epoch; + input.user = user; + QEARN::getUserLockedInfo_output output; + callFunction(QEARN_CONTRACT_INDEX, 2, input, output); + return output.lockedAmount; + } + + uint32 getStateOfRound(uint16 epoch) const + { + QEARN::getStateOfRound_input input{ epoch }; + QEARN::getStateOfRound_output output; + callFunction(QEARN_CONTRACT_INDEX, 3, input, output); + return output.state; + } + + uint64 getUserLockStatus(const id& user) const + { + QEARN::getUserLockStatus_input input{ user }; + QEARN::getUserLockStatus_output output; + callFunction(QEARN_CONTRACT_INDEX, 4, input, output); + return output.status; + } + + QEARN::getEndedStatus_output getEndedStatus(const id& user) const + { + QEARN::getEndedStatus_input input{ user }; + QEARN::getEndedStatus_output output; + callFunction(QEARN_CONTRACT_INDEX, 5, input, output); + return output; + } + + QEARN::getStatsPerEpoch_output getStatsPerEpoch(uint16 epoch) const + { + QEARN::getStatsPerEpoch_input input{ epoch }; + QEARN::getStatsPerEpoch_output output; + callFunction(QEARN_CONTRACT_INDEX, 6, input, output); + return output; + } + + QEARN::getBurnedAndBoostedStats_output getBurnedAndBoostedStats() const + { + QEARN::getBurnedAndBoostedStats_input input; + QEARN::getBurnedAndBoostedStats_output output; + callFunction(QEARN_CONTRACT_INDEX, 7, input, output); + return output; + } + + QEARN::getBurnedAndBoostedStatsPerEpoch_output getBurnedAndBoostedStatsPerEpoch(uint16 epoch) const + { + QEARN::getBurnedAndBoostedStatsPerEpoch_input input{ epoch }; + QEARN::getBurnedAndBoostedStatsPerEpoch_output output; + callFunction(QEARN_CONTRACT_INDEX, 8, input, output); + return output; + } + + sint32 lock(const id& user, long long amount, bool expectSuccess = true) + { + QEARN::lock_input input; + QEARN::lock_output output; + EXPECT_EQ(invokeUserProcedure(QEARN_CONTRACT_INDEX, 1, input, output, user, amount), expectSuccess); + return output.returnCode; + } + + sint32 unlock(const id& user, long long amount, uint16 lockedEpoch, bool expectSuccess = true) + { + QEARN::unlock_input input; + input.amount = amount; + input.lockedEpoch = lockedEpoch; + QEARN::unlock_output output; + EXPECT_EQ(invokeUserProcedure(QEARN_CONTRACT_INDEX, 2, input, output, user, 0), expectSuccess); + return output.returnCode; + } + + struct UserData + { + std::map locked; + }; + + std::map allUserData; + + struct EpochData + { + unsigned long long initialBonusAmount; + unsigned long long initialTotalLockedAmount; + unsigned long long bonusAmount; + unsigned long long amountCurrentlyLocked; + }; + + std::map allEpochData; + + std::map amountUnlockPerUser; + + void simulateDonation(const unsigned long long donationAmount) + { + increaseEnergy(QEARN_CONTRACT_ID, donationAmount); + + unsigned long long& totalBonusAmount = allEpochData[system.epoch + 1].bonusAmount; + totalBonusAmount += donationAmount; + if (totalBonusAmount > QEARN_MAX_BONUS_AMOUNT) + totalBonusAmount = QEARN_MAX_BONUS_AMOUNT; + } + + bool lockAndCheck(const id& user, uint64 amountLock, bool expectSuccess = true) + { + // check consistency of epoch info expected vs returned by contract + checkEpochInfo(system.epoch); + + // get amount and balances before action +#if LARGE_SCALE_TEST >= 3 + uint64 amountBefore = getUserLockedInfo(system.epoch, user); + EXPECT_EQ(allUserData[user].locked[system.epoch], amountBefore); +#else + uint64 amountBefore = allUserData[user].locked[system.epoch]; +#endif + sint64 userBalanceBefore = getBalance(user); + sint64 contractBalanceBefore = getBalance(QEARN_CONTRACT_ID); + + // call lock prcoedure + uint32 retCode = lock(user, amountLock, expectSuccess); + + // check new amount and balances + uint64 amountAfter = getUserLockedInfo(system.epoch, user); + sint64 userBalanceAfter = getBalance(user); + sint64 contractBalanceAfter = getBalance(QEARN_CONTRACT_ID); + if (retCode == QEARN_LOCK_SUCCESS && expectSuccess) + { + EXPECT_EQ(amountAfter, amountBefore + amountLock); + EXPECT_EQ(userBalanceAfter, userBalanceBefore - amountLock); + EXPECT_EQ(contractBalanceAfter, contractBalanceBefore + amountLock); + + allUserData[user].locked[system.epoch] += amountLock; + allEpochData[system.epoch].amountCurrentlyLocked += amountLock; + allEpochData[system.epoch].initialTotalLockedAmount += amountLock; + } + else + { + EXPECT_EQ(amountAfter, amountBefore); + EXPECT_EQ(userBalanceAfter, userBalanceBefore); + EXPECT_EQ(contractBalanceAfter, contractBalanceBefore); + } + + if (!expectSuccess) + return false; + + // check return code + if (retCode != QEARN_OVERFLOW_USER) + { + if (amountLock < QEARN_MINIMUM_LOCKING_AMOUNT || system.epoch < QEARN_INITIAL_EPOCH) + { + EXPECT_EQ(retCode, QEARN_INVALID_INPUT_AMOUNT); + } + else if (amountBefore + amountLock > QEARN_MAX_LOCK_AMOUNT) + { + EXPECT_EQ(retCode, QEARN_LIMIT_LOCK); + } + } + + return retCode == QEARN_LOCK_SUCCESS; + } + + unsigned long long getAndCheckRewardFactorTenmillionth(uint16 epoch) const + { + auto edIt = allEpochData.find(epoch); + EXPECT_NE(edIt, allEpochData.end()); + const EpochData& ed = edIt->second; + const unsigned long long rewardFactorTenmillionth = QPI::div(ed.bonusAmount * 10000000ULL, ed.amountCurrentlyLocked); + if (rewardFactorTenmillionth) + { + // detect overflow in computation of rewardFactorTenmillionth + const double rewardFactorTenmillionthDouble = ed.bonusAmount * 10000000.0 / ed.amountCurrentlyLocked; + double arthmeticError = double(rewardFactorTenmillionth) - rewardFactorTenmillionthDouble; + EXPECT_LT(fabs(arthmeticError), 1e5); + } + + return rewardFactorTenmillionth; + } + + void checkEpochInfo(uint16 epoch) + { + const auto scEpochInfo = getLockInfoPerEpoch(epoch); + EXPECT_LE(scEpochInfo.currentBonusAmount, QEARN_MAX_BONUS_AMOUNT); + if (epoch < QEARN_INITIAL_EPOCH) + return; + auto edIt = allEpochData.find(epoch); + EXPECT_NE(edIt, allEpochData.end()); + const EpochData& ed = edIt->second; + EXPECT_EQ(getAndCheckRewardFactorTenmillionth(epoch), scEpochInfo.yield); + EXPECT_EQ(ed.bonusAmount, scEpochInfo.currentBonusAmount); + EXPECT_EQ(ed.amountCurrentlyLocked, scEpochInfo.currentLockedAmount); + + const auto scStatsInfo = getStatsPerEpoch(epoch); + + EXPECT_EQ(scStatsInfo.earlyUnlockedAmount, ed.initialTotalLockedAmount - ed.amountCurrentlyLocked); + EXPECT_EQ(scStatsInfo.earlyUnlockedPercent, QPI::div((ed.initialTotalLockedAmount - ed.amountCurrentlyLocked) * 10000, ed.initialTotalLockedAmount)); + + const auto scBurnedAndBoostedStatsPerEpoch = getBurnedAndBoostedStatsPerEpoch(epoch); + const auto scBurnedAndBoostedStatsForAllEpoch = getBurnedAndBoostedStats(); + + getState()->checkStatsPerEpoch(scBurnedAndBoostedStatsPerEpoch, epoch); + getState()->checkStatsForAll(scBurnedAndBoostedStatsForAllEpoch); + + uint64 averageAPY = 0; + uint32 cnt = 0; + for(uint16 t = system.epoch - 1; t >= system.epoch - 52; t--) + { + auto preEdIt = allEpochData.find(t); + const EpochData& preED = preEdIt->second; + if (t < QEARN_INITIAL_EPOCH) + { + break; + } + if(preED.amountCurrentlyLocked == 0) + { + continue; + } + + cnt++; + EXPECT_EQ(getLockInfoPerEpoch(t).currentLockedAmount, preED.amountCurrentlyLocked); + averageAPY += QPI::div(preED.bonusAmount * 10000000ULL, preED.amountCurrentlyLocked); + } + EXPECT_EQ(scStatsInfo.totalLockedAmount, getBalance(QEARN_CONTRACT_ID)); + EXPECT_EQ(scStatsInfo.averageAPY, QPI::div(averageAPY, cnt * 1ULL)); + } + + bool unlockAndCheck(const id& user, uint16 lockingEpoch, uint64 amountUnlock, bool expectSuccess = true) + { + // make sure that user exists in spectrum + increaseEnergy(user, 1); + + // get old locked amount +#if LARGE_SCALE_TEST >= 3 + uint64 amountBefore = getUserLockedInfo(lockingEpoch, user); + EXPECT_EQ(allUserData[user].locked[lockingEpoch], amountBefore); +#else + uint64 amountBefore = allUserData[user].locked[lockingEpoch]; +#endif + sint64 userBalanceBefore = getBalance(user); + sint64 contractBalanceBefore = getBalance(QEARN_CONTRACT_ID); + + // call unlock prcoedure + uint32 retCode = unlock(user, amountUnlock, lockingEpoch); + + // check new locked amount and balances + uint64 amountAfter = getUserLockedInfo(lockingEpoch, user); + sint64 userBalanceAfter = getBalance(user); + sint64 contractBalanceAfter = getBalance(QEARN_CONTRACT_ID); + if (retCode == QEARN_UNLOCK_SUCCESS && expectSuccess) + { + EXPECT_GE(amountBefore, amountUnlock); + uint64 expectedAmountAfter = amountBefore - amountUnlock; + if (expectedAmountAfter < QEARN_MINIMUM_LOCKING_AMOUNT) + { + expectedAmountAfter = 0; + } + EXPECT_EQ(amountAfter, expectedAmountAfter); + uint64 amountUnlocked = amountBefore - amountAfter; + + uint16 epochTransitions = system.epoch - lockingEpoch; + unsigned long long rewardFactorTenmillionth = getAndCheckRewardFactorTenmillionth(lockingEpoch); + unsigned long long commonFactor = QPI::div(rewardFactorTenmillionth * amountUnlocked, 100ULL); + unsigned long long amountReward = QPI::div(commonFactor * epochChangesToUnlockParams[epochTransitions].rewardPercent, 10000000ULL); + unsigned long long amountBurn = QPI::div(commonFactor * epochChangesToUnlockParams[epochTransitions].burnPercent, 10000000ULL); + { + // Check for overflows + double commonFactorError = fabs((double(rewardFactorTenmillionth) * double(amountUnlocked) / 100.0) - commonFactor); + EXPECT_LT(commonFactorError, 1e3); + double amountRewardError = fabs((double(commonFactor) * double(epochChangesToUnlockParams[epochTransitions].rewardPercent) / 10000000.0) - amountReward); + EXPECT_LE(amountRewardError, 1); + double amountBurnError = fabs((double(commonFactor) * double(epochChangesToUnlockParams[epochTransitions].burnPercent) / 10000000.0) - amountBurn); + EXPECT_LE(amountBurnError, 1); + } + + allUserData[user].locked[lockingEpoch] -= amountUnlocked; + if(system.epoch == lockingEpoch) + { + allEpochData[lockingEpoch].initialTotalLockedAmount -= amountUnlocked; + } + allEpochData[lockingEpoch].amountCurrentlyLocked -= amountUnlocked; + allEpochData[lockingEpoch].bonusAmount -= amountReward + amountBurn; + + // Edge case of unlocking of all locked funds in previous epoch -> bonus added to next round + if (lockingEpoch != system.epoch && !allEpochData[lockingEpoch].amountCurrentlyLocked) + { + amountBurn += allEpochData[lockingEpoch].bonusAmount; + allEpochData[lockingEpoch].bonusAmount = 0; + } + + EXPECT_EQ(userBalanceAfter, userBalanceBefore + amountUnlocked + amountReward); + EXPECT_EQ(contractBalanceAfter, contractBalanceBefore - amountUnlocked - amountReward - amountBurn); + + // Check consistency of epoch info expected vs returned by contract + checkEpochInfo(lockingEpoch); + + // getEndedStatus() only included Early_Unlocked_Amount if unlocked after locking epoch + if (lockingEpoch != system.epoch) + { + amountUnlockPerUser[user] += amountUnlocked; + } + } + else + { + EXPECT_EQ(amountAfter, amountBefore); + EXPECT_EQ(userBalanceAfter, userBalanceBefore); + EXPECT_EQ(contractBalanceAfter, contractBalanceBefore); + } + + return retCode == QEARN_UNLOCK_SUCCESS; + } + + void endEpochAndCheck() + { + // check getStateOfRound + uint16 payoutEpoch = system.epoch - 52; + EXPECT_EQ(getStateOfRound(QEARN_INITIAL_EPOCH - 1), 2); + EXPECT_EQ(getStateOfRound(payoutEpoch - 1), 2); + EXPECT_EQ(getStateOfRound(payoutEpoch), (payoutEpoch >= QEARN_INITIAL_EPOCH) ? 1 : 2); + EXPECT_EQ(getStateOfRound(system.epoch - 1), (system.epoch - 1 >= QEARN_INITIAL_EPOCH) ? 1 : 2); + EXPECT_EQ(getStateOfRound(system.epoch), (system.epoch >= QEARN_INITIAL_EPOCH) ? 1 : 2); + EXPECT_EQ(getStateOfRound(system.epoch + 1), (system.epoch + 1 >= QEARN_INITIAL_EPOCH) ? 0 : 2); + + // test getUserLockStatus() + { + id user = getUser(random(10)); + uint64 lockStatus = getUserLockStatus(user); + const auto userDataIt = allUserData.find(user); + if (userDataIt == allUserData.end()) + { + EXPECT_EQ(lockStatus, 0); + } + else + { + auto& userData = userDataIt->second; + for (int i = 0; i <= 52; ++i) + { + if (lockStatus & 1) + { + EXPECT_GT(userData.locked[system.epoch - i], 0ll); + } + else + { + EXPECT_EQ(userData.locked[system.epoch - i], 0ll); + } + lockStatus >>= 1; + } + } + } + + // check unlocked amounts returned by getEndedStatus() + for (const auto& userAmountPairs : amountUnlockPerUser) + { + QEARN::getEndedStatus_output endedStatus = getEndedStatus(userAmountPairs.first); + EXPECT_EQ(userAmountPairs.second, endedStatus.earlyUnlockedAmount); + } + + checkEpochInfo(system.epoch); + + bool beforeEndEpoch = true; + getState()->checkLockerArray(beforeEndEpoch, PRINT_TEST_INFO); + getState()->checkGetUnlockedInfo(payoutEpoch); + + // get entity balances to check payout in END_EPOCH + std::map oldUserBalance; + long long oldContractBalance = getBalance(QEARN_CONTRACT_ID); + for (const auto& userIdDataPair : allUserData) + { + const id& user = userIdDataPair.first; + oldUserBalance[user] = getBalance(user); + } + checkEpochInfo(payoutEpoch); + + amountUnlockPerUser.clear(); + endEpoch(); + + // check payout after END_EPOCH + bool expectPayout = (allEpochData.find(payoutEpoch) != allEpochData.end()); + checkEpochInfo(payoutEpoch); + if (expectPayout) + { + // compute and check expected payouts + unsigned long long rewardFactorTenmillionth = getAndCheckRewardFactorTenmillionth(payoutEpoch); + unsigned long long totalRewardsPaid = 0; + const EpochData& ed = allEpochData[payoutEpoch]; + + for (const auto& userIdBalancePair : oldUserBalance) + { + const id& user = userIdBalancePair.first; + const long long oldUserBalance = userIdBalancePair.second; + const UserData& userData = allUserData[user]; + + auto userLockedAmountIter = userData.locked.find(payoutEpoch); + if (userLockedAmountIter == userData.locked.end() || userLockedAmountIter->second == 0) + continue; + const long long userLockedAmount = userLockedAmountIter->second; + const unsigned long long userReward = userLockedAmount * rewardFactorTenmillionth / 10000000ULL; + if (rewardFactorTenmillionth) + EXPECT_EQ((userLockedAmount * rewardFactorTenmillionth) / rewardFactorTenmillionth, userLockedAmount); + totalRewardsPaid += userReward; + + EXPECT_EQ(oldUserBalance + userLockedAmount + userReward, getBalance(user)); + } + EXPECT_EQ(oldContractBalance - ed.bonusAmount - ed.amountCurrentlyLocked, getBalance(QEARN_CONTRACT_ID)); + + + // all the bonus that has not been paid is burned (remainder due to inaccurate arithmetic and full bonus if nothing is locked until the end) + EXPECT_LE(totalRewardsPaid, ed.bonusAmount); + if (ed.amountCurrentlyLocked && ed.bonusAmount) + EXPECT_GE(QPI::div(totalRewardsPaid * 1000, ed.bonusAmount), 998); // only small part of bonus should be burned + else + EXPECT_EQ(totalRewardsPaid, 0ull); + } + else + { + // no payout expected + for (const auto& userIdBalancePair : oldUserBalance) + { + const id& user = userIdBalancePair.first; + const long long oldUserBalance = userIdBalancePair.second; + const long long currentUserBalance = getBalance(user); + EXPECT_EQ(oldUserBalance, currentUserBalance); + } + EXPECT_EQ(oldContractBalance, getBalance(QEARN_CONTRACT_ID)); + } + + beforeEndEpoch = false; + getState()->checkLockerArray(beforeEndEpoch, PRINT_TEST_INFO); + getState()->checkFullyUnlockedAmount(); + } +}; + + +static id getUser(unsigned long long i) +{ + return id(i, i / 2 + 4, i + 10, i * 3 + 8); +} + +static unsigned long long random(unsigned long long maxValue) +{ + return rand64() % (maxValue + 1); +} + +static std::vector getRandomUsers(unsigned int totalUsers, unsigned int maxNum) +{ + unsigned long long userCount = random(maxNum); + std::vector users; + users.reserve(userCount); + for (unsigned int i = 0; i < userCount; ++i) + { + unsigned long long userIdx = random(totalUsers - 1); + users.push_back(getUser(userIdx)); + } + return users; +} + + +TEST(TestContractQearn, ErrorChecking) +{ + ContractTestingQearn qearn; + id user(1, 2, 3, 4); + + system.epoch = QEARN_INITIAL_EPOCH - 1; + + qearn.beginEpoch(); + + // special test case: trying to lock/unlock before QEARN_INITIAL_EPOCH must fail + { + id user2(98765, 43, 2, 1); + increaseEnergy(user2, QEARN_MAX_LOCK_AMOUNT); + EXPECT_FALSE(qearn.lockAndCheck(user2, QEARN_MAX_LOCK_AMOUNT)); + EXPECT_EQ(qearn.unlock(user2, QEARN_MAX_LOCK_AMOUNT, system.epoch), QEARN_INVALID_INPUT_LOCKED_EPOCH); + } + + qearn.endEpoch(); + + system.epoch = QEARN_INITIAL_EPOCH; + + qearn.beginEpoch(); + + // test cases, for which procedures is not executed: + { + // 1. non-existing entities = invalid ID) + EXPECT_FALSE(qearn.lockAndCheck(id::zero(), QEARN_MAX_LOCK_AMOUNT, false)); + EXPECT_FALSE(qearn.lockAndCheck(user, QEARN_MAX_LOCK_AMOUNT, false)); + + // 2. valid ID but negative amount / insufficient balance + increaseEnergy(user, 1); + EXPECT_FALSE(qearn.lockAndCheck(user, -10, false)); + EXPECT_FALSE(qearn.lockAndCheck(user, QEARN_MINIMUM_LOCKING_AMOUNT, false)); + } + + // test cases, for which procedure is executed (valid ID, enough balance) + increaseEnergy(user, QEARN_MAX_LOCK_AMOUNT * 1000); + { + EXPECT_FALSE(qearn.lockAndCheck(user, 0)); + EXPECT_FALSE(qearn.lockAndCheck(user, QEARN_MINIMUM_LOCKING_AMOUNT / 2)); + EXPECT_FALSE(qearn.lockAndCheck(user, QEARN_MINIMUM_LOCKING_AMOUNT - 1)); + + EXPECT_FALSE(qearn.lockAndCheck(user, QEARN_MAX_LOCK_AMOUNT + 1)); + EXPECT_FALSE(qearn.lockAndCheck(user, QEARN_MAX_LOCK_AMOUNT * 2)); + } + + // in order trigger out-of-lock-slots error, lock with many users +#if LARGE_SCALE_TEST >= 4 + // notes: - disabled by default because it takes long + // - seems like the last locker slot is never used in QEARN (FIXME) + for (uint64 i = 0; i < QEARN_MAX_LOCKS - 1; ++i) + { + id otherUser(i, 42, 1234, 642); + long long amount = QEARN_MINIMUM_LOCKING_AMOUNT + (7 * i) % (QEARN_MAX_LOCK_AMOUNT - QEARN_MINIMUM_LOCKING_AMOUNT); + increaseEnergy(otherUser, amount); + EXPECT_TRUE(qearn.lockAndCheck(otherUser, amount)); + } + EXPECT_FALSE(qearn.lockAndCheck(user, QEARN_MINIMUM_LOCKING_AMOUNT)); +#endif + + // note: lock implements no checking of system.epoch + + // for unlock, successfully lock some funds + id otherUser(1, 42, 1234, 642); + long long amount = QEARN_MINIMUM_LOCKING_AMOUNT; + increaseEnergy(otherUser, amount); + EXPECT_TRUE(qearn.lockAndCheck(otherUser, amount)); + + // unlock with too high amount + EXPECT_EQ(qearn.unlock(otherUser, QEARN_MAX_LOCK_AMOUNT + 1, system.epoch), QEARN_INVALID_INPUT_UNLOCK_AMOUNT); + + // unlock with too low amount + EXPECT_EQ(qearn.unlock(otherUser, QEARN_MINIMUM_LOCKING_AMOUNT - 1, system.epoch), QEARN_INVALID_INPUT_AMOUNT); + + // unlock with wrong user + EXPECT_EQ(qearn.unlock(user, QEARN_MINIMUM_LOCKING_AMOUNT, system.epoch), QEARN_EMPTY_LOCKED); + + // unlock with wrong epoch + EXPECT_EQ(qearn.unlock(otherUser, QEARN_MINIMUM_LOCKING_AMOUNT, 1), QEARN_INVALID_INPUT_LOCKED_EPOCH); + EXPECT_EQ(qearn.unlock(otherUser, QEARN_MINIMUM_LOCKING_AMOUNT, QEARN_MAX_EPOCHS + 1), QEARN_INVALID_INPUT_LOCKED_EPOCH); + + // finally, test success case + EXPECT_EQ(qearn.unlock(otherUser, QEARN_MINIMUM_LOCKING_AMOUNT, system.epoch), QEARN_UNLOCK_SUCCESS); +} + +void testRandomLockWithoutUnlock(const uint16 numEpochs, const unsigned int totalUsers, const unsigned int maxUserLocking) +{ + std::cout << "random test without early unlock for " << numEpochs << " epochs with " << totalUsers << " total users and up to " << maxUserLocking << " lock calls per epoch" << std::endl; + ContractTestingQearn qearn; + + const uint16 firstEpoch = contractDescriptions[QEARN_CONTRACT_INDEX].constructionEpoch; + const uint16 lastEpoch = firstEpoch + numEpochs; + + // first epoch is without donation/bonus + for (system.epoch = firstEpoch; system.epoch <= lastEpoch; ++system.epoch) + { + // invoke BEGIN_EPOCH + qearn.beginEpoch(); + + // simulate a random additional donation during the epoch + qearn.simulateDonation(random(ISSUANCE_RATE / 2)); + + // locking + auto lockUsers = getRandomUsers(totalUsers, maxUserLocking); + for (const auto& user : lockUsers) + { + // get random amount for locking and make sure that user has enough qus (may be invalid amount for locking) + uint64 amountLock = random(QEARN_MAX_LOCK_AMOUNT * 4 / 3); + increaseEnergy(user, amountLock); + + qearn.lockAndCheck(user, amountLock); + } + + // invoke END_EPOCH and check correct payouts + qearn.endEpochAndCheck(); + + // send revenue donation to qearn contract (happens after END_EPOCH but before system.epoch is incremented and before BEGIN_EPOCH + qearn.simulateDonation(random(ISSUANCE_RATE)); + } +} + +TEST(TestContractQearn, RandomLockWithoutUnlock) +{ + // params: epochs, total number of users, max users locking in epoch + testRandomLockWithoutUnlock(100, 40, 10); + testRandomLockWithoutUnlock(100, 20, 20); +#if LARGE_SCALE_TEST >= 1 + testRandomLockWithoutUnlock(300, 1000, 1000); +#endif +#if LARGE_SCALE_TEST >= 2 + testRandomLockWithoutUnlock(100, 20000, 10000); +#endif +} + +void testRandomLockWithUnlock(const uint16 numEpochs, const unsigned int totalUsers, const unsigned int maxUserLocking, const unsigned int maxUserUnlocking) +{ + std::cout << "random test with early unlock for " << numEpochs << " epochs with " << totalUsers << " total users, up to " << maxUserLocking << " lock calls (per epoch), and up to " << maxUserUnlocking << " unlock calls (per running round)" << std::endl; + ContractTestingQearn qearn; + + const uint16 firstEpoch = contractDescriptions[QEARN_CONTRACT_INDEX].constructionEpoch; + const uint16 lastEpoch = firstEpoch + numEpochs; + + for (system.epoch = firstEpoch; system.epoch <= lastEpoch; ++system.epoch) + { + // invoke BEGIN_EPOCH + qearn.beginEpoch(); + + // simulate a random additional donation during the epoch + qearn.simulateDonation(random(ISSUANCE_RATE / 2)); + + // locking + auto lockUsers = getRandomUsers(totalUsers, maxUserLocking); + for (const auto& user : lockUsers) + { + // get random amount for locking and make sure that user has enough qus (may be invalid amount for locking) + uint64 amountLock = random(QEARN_MAX_LOCK_AMOUNT * 4 / 3); + increaseEnergy(user, amountLock); + + qearn.lockAndCheck(user, amountLock); + } + + // unlocking + auto unlockUsers = getRandomUsers(totalUsers, maxUserUnlocking); + for (const auto& user : unlockUsers) + { + for (sint32 lockedEpoch = system.epoch; lockedEpoch >= system.epoch - 52; lockedEpoch--) + { + uint64 amountUnlock = random(qearn.allUserData[user].locked[lockedEpoch] * 11 / 10); + qearn.unlockAndCheck(user, lockedEpoch, amountUnlock); + } + } + + // invoke END_EPOCH and check correct payouts + qearn.endEpochAndCheck(); + + // send revenue donation to qearn contract (happens after END_EPOCH but before system.epoch is incremented and before BEGIN_EPOCH + qearn.simulateDonation(random(ISSUANCE_RATE)); + } +} + +TEST(TestContractQearn, RandomLockAndUnlock) +{ + // params: epochs, total number of users, max users locking in epoch, maxUserUnlocking + testRandomLockWithUnlock(100, 40, 10, 10); + testRandomLockWithUnlock(100, 40, 10, 8); // less early unlocking + testRandomLockWithUnlock(100, 40, 20, 19); // more user activity +#if LARGE_SCALE_TEST >= 1 + testRandomLockWithUnlock(300, 1000, 1000, 1000); + testRandomLockWithUnlock(300, 1000, 1000, 800); +#endif +#if LARGE_SCALE_TEST >= 2 + testRandomLockWithUnlock(400, 2000, 1500, 1200); + testRandomLockWithUnlock(100, 20000, 10000, 8000); +#endif +} + +#else + #define NO_UEFI #include @@ -65,7 +961,7 @@ class QearnChecker : public QEARN if (PRINT_TEST_INFO) { - const char * beforeAfterStr = (beforeEndEpoch) ? "Before" : "After"; + const char* beforeAfterStr = (beforeEndEpoch) ? "Before" : "After"; std::cout << "--- " << beforeAfterStr << " END_EPOCH in epoch " << system.epoch << std::endl; } @@ -90,15 +986,15 @@ class QearnChecker : public QEARN } } - void checkGetUnlockedInfo(uint32 epoch) + void checkGetUnlockedInfo(uint32 epoch) { fullyUnlockedAmount.clear(); fullyUnlockedUser.clear(); const QEARN::EpochIndexInfo& epochIndex = _epochIndex.get(epoch); - for(uint64 idx = epochIndex.startIndex; idx < epochIndex.endIndex; ++idx) + for (uint64 idx = epochIndex.startIndex; idx < epochIndex.endIndex; ++idx) { - if(locker.get(idx)._lockedAmount != 0) + if (locker.get(idx)._lockedAmount != 0) { fullyUnlockedAmount.push_back(locker.get(idx)._lockedAmount); fullyUnlockedUser.push_back(locker.get(idx).ID); @@ -108,7 +1004,7 @@ class QearnChecker : public QEARN void checkFullyUnlockedAmount() { - for(uint32 idx = 0; idx < _fullyUnlockedCnt; idx++) + for (uint32 idx = 0; idx < _fullyUnlockedCnt; idx++) { const QEARN::HistoryInfo& FullyUnlockedInfo = fullyUnlocker.get(idx); @@ -379,7 +1275,7 @@ class ContractTestingQearn : protected ContractTesting we can't test at epoch 139 because the value of state in QEarn SC was assigned by hardcoding. To test the epoch 139, please remove the line 776~779 in QEarn SC. */ - if(epoch != 139) + if (epoch != 139) { EXPECT_EQ(scStatsInfo.earlyUnlockedAmount, ed.initialTotalLockedAmount - ed.amountCurrentlyLocked); EXPECT_EQ(scStatsInfo.earlyUnlockedPercent, QPI::div((ed.initialTotalLockedAmount - ed.amountCurrentlyLocked) * 10000, ed.initialTotalLockedAmount)); @@ -387,7 +1283,7 @@ class ContractTestingQearn : protected ContractTesting uint64 totalLockedInSC = 0; uint64 averageAPY = 0; uint32 cnt = 0; - for(uint16 t = system.epoch; t >= system.epoch - 52; t--) + for (uint16 t = system.epoch; t >= system.epoch - 52; t--) { auto preEdIt = allEpochData.find(t); const EpochData& preED = preEdIt->second; @@ -395,7 +1291,7 @@ class ContractTestingQearn : protected ContractTesting { break; } - if(preED.amountCurrentlyLocked == 0) + if (preED.amountCurrentlyLocked == 0) { continue; } @@ -458,7 +1354,7 @@ class ContractTestingQearn : protected ContractTesting } allUserData[user].locked[lockingEpoch] -= amountUnlocked; - if(system.epoch == lockingEpoch) + if (system.epoch == lockingEpoch) { allEpochData[lockingEpoch].initialTotalLockedAmount -= amountUnlocked; } @@ -834,3 +1730,5 @@ TEST(TestContractQearn, RandomLockAndUnlock) testRandomLockWithUnlock(100, 20000, 10000, 8000); #endif } + +#endif diff --git a/test/qpi.cpp b/test/qpi.cpp index 641ca2ad..b75f074e 100644 --- a/test/qpi.cpp +++ b/test/qpi.cpp @@ -28,7 +28,7 @@ TEST(TestCoreQPI, Array) { //QPI::array mustFail; // should raise compile error - QPI::array uint8_4; + QPI::Array uint8_4; EXPECT_EQ(uint8_4.capacity(), 4); //uint8_4.setMem(QPI::id(1, 2, 3, 4)); // should raise compile error uint8_4.setAll(2); @@ -58,13 +58,13 @@ TEST(TestCoreQPI, Array) EXPECT_TRUE(isArraySorted(uint8_4)); EXPECT_TRUE(isArraySortedWithoutDuplicates(uint8_4)); - QPI::array uint64_4; + QPI::Array uint64_4; uint64_4.setMem(QPI::id(101, 102, 103, 104)); for (int i = 0; i < uint64_4.capacity(); ++i) EXPECT_EQ(uint64_4.get(i), i + 101); //uint64_4.setMem(uint8_4); // should raise compile error - QPI::array uint16_2; + QPI::Array uint16_2; EXPECT_EQ(uint8_4.capacity(), 4); //uint16_2.setMem(QPI::id(1, 2, 3, 4)); // should raise compile error uint16_2.setAll(12345); @@ -81,9 +81,9 @@ TEST(TestCoreQPI, Array) TEST(TestCoreQPI, BitArray) { - //QPI::bit_array<0> mustFail; + //QPI::BitArray<0> mustFail; - QPI::bit_array<1> b1; + QPI::BitArray<1> b1; EXPECT_EQ(b1.capacity(), 1); b1.setAll(0); EXPECT_EQ(b1.get(0), 0); @@ -98,7 +98,7 @@ TEST(TestCoreQPI, BitArray) b1.set(0, true); EXPECT_EQ(b1.get(0), 1); - QPI::bit_array<64> b64; + QPI::BitArray<64> b64; EXPECT_EQ(b64.capacity(), 64); b64.setMem(0x11llu); EXPECT_EQ(b64.get(0), 1); @@ -109,7 +109,7 @@ TEST(TestCoreQPI, BitArray) EXPECT_EQ(b64.get(5), 0); EXPECT_EQ(b64.get(6), 0); EXPECT_EQ(b64.get(7), 0); - QPI::array llu1; + QPI::Array llu1; llu1.setMem(b64); EXPECT_EQ(llu1.get(0), 0x11llu); b64.setAll(0); @@ -119,11 +119,11 @@ TEST(TestCoreQPI, BitArray) llu1.setMem(b64); EXPECT_EQ(llu1.get(0), 0xffffffffffffffffllu); - //QPI::bit_array<96> b96; // must trigger compile error + //QPI::BitArray<96> b96; // must trigger compile error - QPI::bit_array<128> b128; + QPI::BitArray<128> b128; EXPECT_EQ(b128.capacity(), 128); - QPI::array llu2; + QPI::Array llu2; llu2.setAll(0x4llu); EXPECT_EQ(llu2.get(0), 0x4llu); EXPECT_EQ(llu2.get(1), 0x4llu); diff --git a/test/qpi_collection.cpp b/test/qpi_collection.cpp index 9c12b42b..364d3108 100644 --- a/test/qpi_collection.cpp +++ b/test/qpi_collection.cpp @@ -26,7 +26,7 @@ typedef void (*USER_PROCEDURE)(const QPI::QpiContextProcedureCall&, void* state, #include template -void checkPriorityQueue(const QPI::collection& coll, const QPI::id& pov, bool print = false) +void checkPriorityQueue(const QPI::Collection& coll, const QPI::id& pov, bool print = false) { if (print) { @@ -79,7 +79,7 @@ void printPovElementCounts(const std::map& povEleme // return sorted set of PoVs template -std::map getPovElementCounts(const QPI::collection& coll) +std::map getPovElementCounts(const QPI::Collection& coll) { // use that in current implementation elements are always in range 0 to N-1 std::map povs; @@ -100,13 +100,13 @@ std::map getPovElementCounts(const QPI::collection< } template -bool isCompletelySame(const QPI::collection& coll1, const QPI::collection& coll2) +bool isCompletelySame(const QPI::Collection& coll1, const QPI::Collection& coll2) { return memcmp(&coll1, &coll2, sizeof(coll1)) == 0; } template -bool haveSameContent(const QPI::collection& coll1, const QPI::collection& coll2, bool verbose = true) +bool haveSameContent(const QPI::Collection& coll1, const QPI::Collection& coll2, bool verbose = true) { // check that both contain the same PoVs, each with the same number of elements auto coll1PovCounts = getPovElementCounts(coll1); @@ -153,7 +153,7 @@ bool haveSameContent(const QPI::collection& coll1, const QPI::colle } template -void cleanupCollectionReferenceImplementation(const QPI::collection& coll, QPI::collection& newColl) +void cleanupCollectionReferenceImplementation(const QPI::Collection& coll, QPI::Collection& newColl) { newColl.reset(); @@ -172,10 +172,10 @@ void cleanupCollectionReferenceImplementation(const QPI::collection } template -void cleanupCollection(QPI::collection& coll) +void cleanupCollection(QPI::Collection& coll) { // save original data for checking - QPI::collection origColl; + QPI::Collection origColl; copyMem(&origColl, &coll, sizeof(coll)); // run reference cleanup and test that cleanup did not change any relevant content @@ -197,7 +197,7 @@ TEST(TestCoreQPI, CollectionMultiPovMultiElements) constexpr unsigned long long capacity = 8; // for valid init you either need to call reset or load the data from a file (in SC, state is zeroed before INITIALIZE is called) - QPI::collection coll; + QPI::Collection coll; coll.reset(); // test behavior of empty collection @@ -584,7 +584,7 @@ TEST(TestCoreQPI, CollectionMultiPovMultiElements) EXPECT_EQ(coll.population(), 8); // test comparison function of full collection - QPI::collection empty_coll; + QPI::Collection empty_coll; empty_coll.reset(); EXPECT_TRUE(isCompletelySame(coll, coll)); EXPECT_TRUE(haveSameContent(coll, coll)); @@ -614,7 +614,7 @@ template void testCollectionOnePovMultiElements(int prioAmpFactor, int prioFreqDiv) { // for valid init you either need to call reset or load the data from a file (in SC, state is zeroed before INITIALIZE is called) - QPI::collection coll; + QPI::Collection coll; coll.reset(); // these tests support changing the implementation of the element array filling to non-sequential @@ -814,7 +814,7 @@ void testCollectionOnePovMultiElements(int prioAmpFactor, int prioFreqDiv) } // check that cleanup after removing all elements leads to same as reset() in terms of memory - QPI::collection resetColl; + QPI::Collection resetColl; resetColl.reset(); EXPECT_FALSE(isCompletelySame(resetColl, coll)); coll.cleanup(); @@ -835,7 +835,7 @@ TEST(TestCoreQPI, CollectionOnePovMultiElementsSamePrioOrder) constexpr unsigned long long capacity = 16; // for valid init you either need to call reset or load the data from a file (in SC, state is zeroed before INITIALIZE is called) - QPI::collection coll; + QPI::Collection coll; coll.reset(); // these tests support changing the implementation of the element array filling to non-sequential @@ -881,7 +881,7 @@ template void testCollectionMultiPovOneElement(bool cleanupAfterEachRemove) { // for valid init you either need to call reset or load the data from a file (in SC, state is zeroed before INITIALIZE is called) - QPI::collection coll; + QPI::Collection coll; coll.reset(); for (int i = 0; i < capacity; ++i) @@ -944,7 +944,7 @@ void testCollectionMultiPovOneElement(bool cleanupAfterEachRemove) } // check that cleanup after removing all elements leads to same as reset() in terms of memory - QPI::collection resetColl; + QPI::Collection resetColl; resetColl.reset(); if (!cleanupAfterEachRemove) EXPECT_FALSE(isCompletelySame(resetColl, coll)); @@ -971,7 +971,7 @@ TEST(TestCoreQPI, CollectionOneRemoveLastHeadTail) constexpr unsigned long long capacity = 4; // for valid init you either need to call reset or load the data from a file (in SC, state is zeroed before INITIALIZE is called) - QPI::collection coll; + QPI::Collection coll; coll.reset(); bool print = false; @@ -993,7 +993,7 @@ TEST(TestCoreQPI, CollectionSubCollections) { QPI::id pov(1, 2, 3, 4); - QPI::collection coll; + QPI::Collection coll; coll.reset(); // test empty @@ -1073,7 +1073,7 @@ TEST(TestCoreQPI, CollectionSubCollectionsRandom) { QPI::id pov(1, 2, 3, 4); - QPI::collection coll; + QPI::Collection coll; coll.reset(); const int seed = 246357; @@ -1170,7 +1170,7 @@ TEST(TestCoreQPI, CollectionReplaceElements) { QPI::id pov(1, 2, 3, 4); - QPI::collection coll; + QPI::Collection coll; coll.reset(); const int seed = 246357; @@ -1251,7 +1251,7 @@ void testCollectionCleanupPseudoRandom(int povs, int seed, bool povCollisions) // add and remove entries with pseudo-random sequence std::mt19937_64 gen64(seed); - QPI::collection coll; + QPI::Collection coll; coll.reset(); // test cleanup of empty collection @@ -1345,7 +1345,7 @@ T genNumber( // TODO: move all performance tests into a separate project!? template QPI::uint64 testCollectionPerformance( - QPI::collection& coll, + QPI::Collection& coll, const QPI::uint64 povs, const QPI::sint64* genBuffer, const QPI::uint64 genSize, @@ -1423,7 +1423,7 @@ QPI::uint64 testCollectionPerformance( gen_buffers[i] = gen64(); } - QPI::collection* coll = new QPI::collection(); + QPI::Collection* coll = new QPI::Collection(); coll->reset(); auto t0 = std::chrono::high_resolution_clock::now();