diff --git a/src/Qubic.vcxproj b/src/Qubic.vcxproj
index 33a7a5721..e97da8a88 100644
--- a/src/Qubic.vcxproj
+++ b/src/Qubic.vcxproj
@@ -36,7 +36,8 @@
-
+
+
diff --git a/src/Qubic.vcxproj.filters b/src/Qubic.vcxproj.filters
index 6229be64e..674abb48b 100644
--- a/src/Qubic.vcxproj.filters
+++ b/src/Qubic.vcxproj.filters
@@ -98,6 +98,9 @@
contracts
+
+
+ contracts
contracts
diff --git a/src/contract_core/contract_def.h b/src/contract_core/contract_def.h
index e4bc14548..18c2f331f 100644
--- a/src/contract_core/contract_def.h
+++ b/src/contract_core/contract_def.h
@@ -1,632 +1,644 @@
-#pragma once
-
-////////// Smart contracts \\\\\\\\\\
-
-// The order in this file is very important, because it restricts what is available to the contracts.
-// For example, a contract may only call a contract with lower index, which is enforced by order of
-// include / availability of definition.
-// Additionally, most types, functions, and variables of the core have to be defined after including
-// the contract to keep them unavailable in the contract code.
-
-
-// With no other includes before, the following are the only headers available to contracts.
-// When adding something, be cautious to keep access of contracts limited to safe features only.
-#include "pre_qpi_def.h"
-#include "contracts/qpi.h"
-#include "qpi_proposal_voting.h"
-
-// make interfaces to oracles available for all contracts
-#include "oracle_core/oracle_interfaces_def.h"
-
-#define QX_CONTRACT_INDEX 1
-#define CONTRACT_INDEX QX_CONTRACT_INDEX
-#define CONTRACT_STATE_TYPE QX
-#define CONTRACT_STATE2_TYPE QX2
-#include "contracts/Qx.h"
-
-#undef CONTRACT_INDEX
-#undef CONTRACT_STATE_TYPE
-#undef CONTRACT_STATE2_TYPE
-
-#define QUOTTERY_CONTRACT_INDEX 2
-#define CONTRACT_INDEX QUOTTERY_CONTRACT_INDEX
-#define CONTRACT_STATE_TYPE QUOTTERY
-#define CONTRACT_STATE2_TYPE QUOTTERY2
-#include "contracts/Quottery.h"
-
-#undef CONTRACT_INDEX
-#undef CONTRACT_STATE_TYPE
-#undef CONTRACT_STATE2_TYPE
-
-#define RANDOM_CONTRACT_INDEX 3
-#define CONTRACT_INDEX RANDOM_CONTRACT_INDEX
-#define CONTRACT_STATE_TYPE RANDOM
-#define CONTRACT_STATE2_TYPE RANDOM2
-#include "contracts/Random.h"
-
-#undef CONTRACT_INDEX
-#undef CONTRACT_STATE_TYPE
-#undef CONTRACT_STATE2_TYPE
-
-#define QUTIL_CONTRACT_INDEX 4
-#define CONTRACT_INDEX QUTIL_CONTRACT_INDEX
-#define CONTRACT_STATE_TYPE QUTIL
-#define CONTRACT_STATE2_TYPE QUTIL2
-#include "contracts/QUtil.h"
-
-#undef CONTRACT_INDEX
-#undef CONTRACT_STATE_TYPE
-#undef CONTRACT_STATE2_TYPE
-
-#define MLM_CONTRACT_INDEX 5
-#define CONTRACT_INDEX MLM_CONTRACT_INDEX
-#define CONTRACT_STATE_TYPE MLM
-#define CONTRACT_STATE2_TYPE MLM2
-#include "contracts/MyLastMatch.h"
-
-#undef CONTRACT_INDEX
-#undef CONTRACT_STATE_TYPE
-#undef CONTRACT_STATE2_TYPE
-
-#define GQMPROP_CONTRACT_INDEX 6
-#define CONTRACT_INDEX GQMPROP_CONTRACT_INDEX
-#define CONTRACT_STATE_TYPE GQMPROP
-#define CONTRACT_STATE2_TYPE GQMPROP2
-#include "contracts/GeneralQuorumProposal.h"
-
-#undef CONTRACT_INDEX
-#undef CONTRACT_STATE_TYPE
-#undef CONTRACT_STATE2_TYPE
-
-#define SWATCH_CONTRACT_INDEX 7
-#define CONTRACT_INDEX SWATCH_CONTRACT_INDEX
-#define CONTRACT_STATE_TYPE SWATCH
-#define CONTRACT_STATE2_TYPE SWATCH2
-#include "contracts/SupplyWatcher.h"
-
-#undef CONTRACT_INDEX
-#undef CONTRACT_STATE_TYPE
-#undef CONTRACT_STATE2_TYPE
-
-#define CCF_CONTRACT_INDEX 8
-#define CONTRACT_INDEX CCF_CONTRACT_INDEX
-#define CONTRACT_STATE_TYPE CCF
-#define CONTRACT_STATE2_TYPE CCF2
-#include "contracts/ComputorControlledFund.h"
-
-#undef CONTRACT_INDEX
-#undef CONTRACT_STATE_TYPE
-#undef CONTRACT_STATE2_TYPE
-
-#define QEARN_CONTRACT_INDEX 9
-#define CONTRACT_INDEX QEARN_CONTRACT_INDEX
-#define CONTRACT_STATE_TYPE QEARN
-#define CONTRACT_STATE2_TYPE QEARN2
-#include "contracts/Qearn.h"
-
-#undef CONTRACT_INDEX
-#undef CONTRACT_STATE_TYPE
-#undef CONTRACT_STATE2_TYPE
-
-#define QVAULT_CONTRACT_INDEX 10
-#define CONTRACT_INDEX QVAULT_CONTRACT_INDEX
-#define CONTRACT_STATE_TYPE QVAULT
-#define CONTRACT_STATE2_TYPE QVAULT2
-#include "contracts/QVAULT.h"
-
-#undef CONTRACT_INDEX
-#undef CONTRACT_STATE_TYPE
-#undef CONTRACT_STATE2_TYPE
-
-#define MSVAULT_CONTRACT_INDEX 11
-#define CONTRACT_INDEX MSVAULT_CONTRACT_INDEX
-#define CONTRACT_STATE_TYPE MSVAULT
-#define CONTRACT_STATE2_TYPE MSVAULT2
-#include "contracts/MsVault.h"
-
-#undef CONTRACT_INDEX
-#undef CONTRACT_STATE_TYPE
-#undef CONTRACT_STATE2_TYPE
-
-#define QBAY_CONTRACT_INDEX 12
-#define CONTRACT_INDEX QBAY_CONTRACT_INDEX
-#define CONTRACT_STATE_TYPE QBAY
-#define CONTRACT_STATE2_TYPE QBAY2
-#ifdef OLD_QBAY
-#include "contracts/Qbay_old.h"
-#else
-#include "contracts/Qbay.h"
-#endif
-
-#undef CONTRACT_INDEX
-#undef CONTRACT_STATE_TYPE
-#undef CONTRACT_STATE2_TYPE
-
-#define QSWAP_CONTRACT_INDEX 13
-#define CONTRACT_INDEX QSWAP_CONTRACT_INDEX
-#define CONTRACT_STATE_TYPE QSWAP
-#define CONTRACT_STATE2_TYPE QSWAP2
-#ifdef OLD_QSWAP
-#include "contracts/Qswap_old.h"
-#else
-#include "contracts/Qswap.h"
-#endif
-
-#undef CONTRACT_INDEX
-#undef CONTRACT_STATE_TYPE
-#undef CONTRACT_STATE2_TYPE
-
-#define NOST_CONTRACT_INDEX 14
-#define CONTRACT_INDEX NOST_CONTRACT_INDEX
-#define CONTRACT_STATE_TYPE NOST
-#define CONTRACT_STATE2_TYPE NOST2
-#include "contracts/Nostromo.h"
-
-#undef CONTRACT_INDEX
-#undef CONTRACT_STATE_TYPE
-#undef CONTRACT_STATE2_TYPE
-
-#define QDRAW_CONTRACT_INDEX 15
-#define CONTRACT_INDEX QDRAW_CONTRACT_INDEX
-#define CONTRACT_STATE_TYPE QDRAW
-#define CONTRACT_STATE2_TYPE QDRAW2
-#include "contracts/Qdraw.h"
-
-#undef CONTRACT_INDEX
-#undef CONTRACT_STATE_TYPE
-#undef CONTRACT_STATE2_TYPE
-
-#define RL_CONTRACT_INDEX 16
-#define CONTRACT_INDEX RL_CONTRACT_INDEX
-#define CONTRACT_STATE_TYPE RL
-#define CONTRACT_STATE2_TYPE RL2
-#include "contracts/RandomLottery.h"
-
-#undef CONTRACT_INDEX
-#undef CONTRACT_STATE_TYPE
-#undef CONTRACT_STATE2_TYPE
-
-#define QBOND_CONTRACT_INDEX 17
-#define CONTRACT_INDEX QBOND_CONTRACT_INDEX
-#define CONTRACT_STATE_TYPE QBOND
-#define CONTRACT_STATE2_TYPE QBOND2
-#include "contracts/QBond.h"
-
-#undef CONTRACT_INDEX
-#undef CONTRACT_STATE_TYPE
-#undef CONTRACT_STATE2_TYPE
-
-#define QIP_CONTRACT_INDEX 18
-#define CONTRACT_INDEX QIP_CONTRACT_INDEX
-#define CONTRACT_STATE_TYPE QIP
-#define CONTRACT_STATE2_TYPE QIP2
-#include "contracts/QIP.h"
-
-#undef CONTRACT_INDEX
-#undef CONTRACT_STATE_TYPE
-#undef CONTRACT_STATE2_TYPE
-
-#define QRAFFLE_CONTRACT_INDEX 19
-#define CONTRACT_INDEX QRAFFLE_CONTRACT_INDEX
-#define CONTRACT_STATE_TYPE QRAFFLE
-#define CONTRACT_STATE2_TYPE QRAFFLE2
-#include "contracts/QRaffle.h"
-
-#undef CONTRACT_INDEX
-#undef CONTRACT_STATE_TYPE
-#undef CONTRACT_STATE2_TYPE
-
-#define QRWA_CONTRACT_INDEX 20
-#define CONTRACT_INDEX QRWA_CONTRACT_INDEX
-#define CONTRACT_STATE_TYPE QRWA
-#define CONTRACT_STATE2_TYPE QRWA2
-#include "contracts/qRWA.h"
-
-#undef CONTRACT_INDEX
-#undef CONTRACT_STATE_TYPE
-#undef CONTRACT_STATE2_TYPE
-
-#define QRP_CONTRACT_INDEX 21
-#define CONTRACT_INDEX QRP_CONTRACT_INDEX
-#define CONTRACT_STATE_TYPE QRP
-#define CONTRACT_STATE2_TYPE QRP2
-#include "contracts/QReservePool.h"
-
-#undef CONTRACT_INDEX
-#undef CONTRACT_STATE_TYPE
-#undef CONTRACT_STATE2_TYPE
-
-#define QTF_CONTRACT_INDEX 22
-#define CONTRACT_INDEX QTF_CONTRACT_INDEX
-#define CONTRACT_STATE_TYPE QTF
-#define CONTRACT_STATE2_TYPE QTF2
-#include "contracts/QThirtyFour.h"
-
-#undef CONTRACT_INDEX
-#undef CONTRACT_STATE_TYPE
-#undef CONTRACT_STATE2_TYPE
-
-#define QDUEL_CONTRACT_INDEX 23
-#define CONTRACT_INDEX QDUEL_CONTRACT_INDEX
-#define CONTRACT_STATE_TYPE QDUEL
-#define CONTRACT_STATE2_TYPE QDUEL2
-#include "contracts/QDuel.h"
-
-#undef CONTRACT_INDEX
-#undef CONTRACT_STATE_TYPE
-#undef CONTRACT_STATE2_TYPE
-
-#define PULSE_CONTRACT_INDEX 24
-#define CONTRACT_INDEX PULSE_CONTRACT_INDEX
-#define CONTRACT_STATE_TYPE PULSE
-#define CONTRACT_STATE2_TYPE PULSE2
-#include "contracts/Pulse.h"
-
-#undef CONTRACT_INDEX
-#undef CONTRACT_STATE_TYPE
-#undef CONTRACT_STATE2_TYPE
-
-#define VOTTUNBRIDGE_CONTRACT_INDEX 25
-#define CONTRACT_INDEX VOTTUNBRIDGE_CONTRACT_INDEX
-#define CONTRACT_STATE_TYPE VOTTUNBRIDGE
-#define CONTRACT_STATE2_TYPE VOTTUNBRIDGE2
-#include "contracts/VottunBridge.h"
-
-#undef CONTRACT_INDEX
-#undef CONTRACT_STATE_TYPE
-#undef CONTRACT_STATE2_TYPE
-
-#define QUSINO_CONTRACT_INDEX 26
-#define CONTRACT_INDEX QUSINO_CONTRACT_INDEX
-#define CONTRACT_STATE_TYPE QUSINO
-#define CONTRACT_STATE2_TYPE QUSINO2
-#include "contracts/Qusino.h"
-
-#undef CONTRACT_INDEX
-#undef CONTRACT_STATE_TYPE
-#undef CONTRACT_STATE2_TYPE
-
-#define ESCROW_CONTRACT_INDEX 27
-#define CONTRACT_INDEX ESCROW_CONTRACT_INDEX
-#define CONTRACT_STATE_TYPE ESCROW
-#define CONTRACT_STATE2_TYPE ESCROW2
-#include "contracts/Escrow.h"
-
-#undef CONTRACT_INDEX
-#undef CONTRACT_STATE_TYPE
-#undef CONTRACT_STATE2_TYPE
-
-#define WOLFPACK_CONTRACT_INDEX 28
-#define CONTRACT_INDEX WOLFPACK_CONTRACT_INDEX
-#define CONTRACT_STATE_TYPE WOLFPACK
-#define CONTRACT_STATE2_TYPE WOLFPACK2
-#include "contracts/GGWP.h"
-
-// new contracts should be added above this line
-
-#ifdef INCLUDE_CONTRACT_TEST_EXAMPLES
-
-constexpr unsigned short TESTEXA_CONTRACT_INDEX = (CONTRACT_INDEX + 1);
-#undef CONTRACT_INDEX
-#undef CONTRACT_STATE_TYPE
-#undef CONTRACT_STATE2_TYPE
-#define CONTRACT_INDEX TESTEXA_CONTRACT_INDEX
-#define CONTRACT_STATE_TYPE TESTEXA
-#define CONTRACT_STATE2_TYPE TESTEXA2
-#include "contracts/TestExampleA.h"
-constexpr unsigned short TESTEXB_CONTRACT_INDEX = (CONTRACT_INDEX + 1);
-#undef CONTRACT_INDEX
-#undef CONTRACT_STATE_TYPE
-#undef CONTRACT_STATE2_TYPE
-#define CONTRACT_INDEX TESTEXB_CONTRACT_INDEX
-#define CONTRACT_STATE_TYPE TESTEXB
-#define CONTRACT_STATE2_TYPE TESTEXB2
-#include "contracts/TestExampleB.h"
-constexpr unsigned short TESTEXC_CONTRACT_INDEX = (CONTRACT_INDEX + 1);
-#undef CONTRACT_INDEX
-#undef CONTRACT_STATE_TYPE
-#undef CONTRACT_STATE2_TYPE
-#define CONTRACT_INDEX TESTEXC_CONTRACT_INDEX
-#define CONTRACT_STATE_TYPE TESTEXC
-#define CONTRACT_STATE2_TYPE TESTEXC2
-#include "contracts/TestExampleC.h"
-constexpr unsigned short TESTEXD_CONTRACT_INDEX = (CONTRACT_INDEX + 1);
-#undef CONTRACT_INDEX
-#undef CONTRACT_STATE_TYPE
-#undef CONTRACT_STATE2_TYPE
-#define CONTRACT_INDEX TESTEXD_CONTRACT_INDEX
-#define CONTRACT_STATE_TYPE TESTEXD
-#define CONTRACT_STATE2_TYPE TESTEXD2
-#include "contracts/TestExampleD.h"
-#endif
-
-#define MAX_CONTRACT_ITERATION_DURATION 0 // In milliseconds, must be above 0; for now set to 0 to disable timeout, because a rollback mechanism needs to be implemented to properly handle timeout
-
-#undef INITIALIZE
-#undef BEGIN_EPOCH
-#undef END_EPOCH
-#undef BEGIN_TICK
-#undef END_TICK
-#undef PRE_RELEASE_SHARES
-#undef PRE_ACQUIRE_SHARES
-#undef POST_RELEASE_SHARES
-#undef POST_ACQUIRE_SHARES
-#undef POST_INCOMING_TRANSFER
-#undef SET_SHAREHOLDER_PROPOSAL
-#undef SET_SHAREHOLDER_VOTES
-
-
-// The following are included after the contracts to keep their definitions and dependencies
-// inaccessible for contracts
-#include "qpi_collection_impl.h"
-#include "qpi_trivial_impl.h"
-#include "qpi_hash_map_impl.h"
-#include "qpi_linked_list_impl.h"
-
-#include "platform/global_var.h"
-
-#include "network_messages/common_def.h"
-
-struct Contract0State
-{
- long long contractFeeReserves[MAX_NUMBER_OF_CONTRACTS];
-};
-
-struct IPO
-{
- m256i publicKeys[NUMBER_OF_COMPUTORS];
- long long prices[NUMBER_OF_COMPUTORS];
-};
-
-static_assert(sizeof(IPO) == 32 * NUMBER_OF_COMPUTORS + 8 * NUMBER_OF_COMPUTORS, "Something is wrong with the struct size.");
-
-
-constexpr struct ContractDescription
-{
- char assetName[8];
- // constructionEpoch needs to be set to after IPO (IPO is before construction)
- unsigned short constructionEpoch, destructionEpoch;
- unsigned long long stateSize;
-} contractDescriptions[] = {
- {"", 0, 0, sizeof(Contract0State)},
- {"QX", 66, 10000, sizeof(QX::StateData)},
- {"QTRY", 72, 10000, sizeof(QUOTTERY::StateData)},
- {"RANDOM", 88, 10000, sizeof(RANDOM::StateData)},
- {"QUTIL", 99, 10000, sizeof(QUTIL::StateData)},
- {"MLM", 112, 10000, sizeof(IPO)},
- {"GQMPROP", 123, 10000, sizeof(GQMPROP::StateData)},
- {"SWATCH", 123, 10000, sizeof(IPO)},
- {"CCF", 127, 10000, sizeof(CCF::StateData)}, // proposal in epoch 125, IPO in 126, construction and first use in 127
- {"QEARN", 137, 10000, sizeof(QEARN::StateData)}, // proposal in epoch 135, IPO in 136, construction in 137 / first donation after END_EPOCH, first round in epoch 138
- {"QVAULT", 138, 10000, sizeof(QVAULT::StateData)}, // proposal in epoch 136, IPO in 137, construction and first use in 138
- {"MSVAULT", 149, 10000, sizeof(MSVAULT::StateData)}, // proposal in epoch 147, IPO in 148, construction and first use in 149
- {"QBAY", 154, 10000, sizeof(QBAY::StateData)}, // proposal in epoch 152, IPO in 153, construction and first use in 154
- {"QSWAP", 171, 10000, sizeof(QSWAP::StateData)}, // proposal in epoch 169, IPO in 170, construction and first use in 171
- {"NOST", 172, 10000, sizeof(NOST::StateData)}, // proposal in epoch 170, IPO in 171, construction and first use in 172
- {"QDRAW", 179, 10000, sizeof(QDRAW::StateData)}, // proposal in epoch 177, IPO in 178, construction and first use in 179
- {"RL", 182, 10000, sizeof(RL::StateData)}, // proposal in epoch 180, IPO in 181, construction and first use in 182
- {"QBOND", 182, 10000, sizeof(QBOND::StateData)}, // proposal in epoch 180, IPO in 181, construction and first use in 182
- {"QIP", 189, 10000, sizeof(QIP::StateData)}, // proposal in epoch 187, IPO in 188, construction and first use in 189
- {"QRAFFLE", 192, 10000, sizeof(QRAFFLE::StateData)}, // proposal in epoch 190, IPO in 191, construction and first use in 192
- {"QRWA", 197, 10000, sizeof(QRWA::StateData)}, // proposal in epoch 195, IPO in 196, construction and first use in 197
- {"QRP", 199, 10000, sizeof(IPO)}, // proposal in epoch 197, IPO in 198, construction and first use in 199
- {"QTF", 199, 10000, sizeof(QTF::StateData)}, // proposal in epoch 197, IPO in 198, construction and first use in 199
- {"QDUEL", 199, 10000, sizeof(QDUEL::StateData)}, // proposal in epoch 197, IPO in 198, construction and first use in 199
- {"PULSE", 204, 10000, sizeof(PULSE::StateData)}, // proposal in epoch 202, IPO in 203, construction and first use in 204
- {"VOTTUN", 206, 10000, sizeof(VOTTUNBRIDGE::StateData)}, // proposal in epoch 204, IPO in 205, construction and first use in 206
- {"QUSINO", 208, 10000, sizeof(QUSINO::StateData)}, // proposal in epoch 206, IPO in 207, construction and first use in 208
- {"ESCROW", 210, 10000, sizeof(ESCROW::StateData)}, // proposal in epoch 208, IPO in 209, construction and first use in 210
- {"GGWP", 218, 10000, sizeof(WOLFPACK::StateData)}, // proposal in epoch 216, IPO in 217, construction and first use in 218
- // new contracts should be added above this line
-#ifdef INCLUDE_CONTRACT_TEST_EXAMPLES
- {"TESTEXA", 138, 10000, sizeof(TESTEXA::StateData)},
- {"TESTEXB", 138, 10000, sizeof(TESTEXB::StateData)},
- {"TESTEXC", 138, 10000, sizeof(IPO)},
- {"TESTEXD", 155, 10000, sizeof(IPO)},
-#endif
-};
-
-constexpr unsigned int contractCount = sizeof(contractDescriptions) / sizeof(contractDescriptions[0]);
-
-GLOBAL_VAR_DECL EXPAND_PROCEDURE contractExpandProcedures[contractCount];
-
-GLOBAL_VAR_DECL MIGRATE_PROCEDURE contractMigrateProcedures[contractCount];
-GLOBAL_VAR_DECL unsigned long long contractMigrateOldStateSizes[contractCount];
-GLOBAL_VAR_DECL unsigned short contractMigrateLocalsSizes[contractCount];
-
-// TODO: all below are filled very sparsely, so a better data structure could save almost all the memory
-GLOBAL_VAR_DECL USER_FUNCTION contractUserFunctions[contractCount][65536];
-GLOBAL_VAR_DECL unsigned short contractUserFunctionInputSizes[contractCount][65536];
-GLOBAL_VAR_DECL unsigned short contractUserFunctionOutputSizes[contractCount][65536];
-// This has been changed to unsigned short to avoid the misalignment issue happening in epochs 109 and 110,
-// probably due to too high numbers in contractUserProcedureLocalsSizes causing stack buffer alloc to fail
-// probably due to buffer overflow that is difficult to reproduce in test net
-// TODO: change back to unsigned int
-GLOBAL_VAR_DECL unsigned short contractUserFunctionLocalsSizes[contractCount][65536];
-GLOBAL_VAR_DECL USER_PROCEDURE contractUserProcedures[contractCount][65536];
-GLOBAL_VAR_DECL unsigned short contractUserProcedureInputSizes[contractCount][65536];
-GLOBAL_VAR_DECL unsigned short contractUserProcedureOutputSizes[contractCount][65536];
-// This has been changed to unsigned short to avoid the misalignment issue happening in epochs 109 and 110,
-// probably due to too high numbers in contractUserProcedureLocalsSizes causing stack buffer alloc to fail
-// probably due to buffer overflow that is difficult to reproduce in test net
-// TODO: change back to unsigned int
-GLOBAL_VAR_DECL unsigned short contractUserProcedureLocalsSizes[contractCount][65536];
-
-enum SystemProcedureID
-{
- INITIALIZE = 0,
- BEGIN_EPOCH,
- END_EPOCH,
- BEGIN_TICK,
- END_TICK,
- PRE_RELEASE_SHARES,
- PRE_ACQUIRE_SHARES,
- POST_RELEASE_SHARES,
- POST_ACQUIRE_SHARES,
- POST_INCOMING_TRANSFER,
- SET_SHAREHOLDER_PROPOSAL,
- SET_SHAREHOLDER_VOTES,
- contractSystemProcedureCount,
-};
-
-enum OtherEntryPointIDs
-{
- // Used together with SystemProcedureID values, so there must be no overlap!
- USER_PROCEDURE_CALL = contractSystemProcedureCount + 1,
- USER_FUNCTION_CALL = contractSystemProcedureCount + 2,
- REGISTER_USER_FUNCTIONS_AND_PROCEDURES_CALL = contractSystemProcedureCount + 3,
- USER_PROCEDURE_NOTIFICATION_CALL = contractSystemProcedureCount + 4,
- MIGRATE_PROCEDURE_CALL = contractSystemProcedureCount + 5,
-};
-
-GLOBAL_VAR_DECL SYSTEM_PROCEDURE contractSystemProcedures[contractCount][contractSystemProcedureCount];
-GLOBAL_VAR_DECL unsigned short contractSystemProcedureLocalsSizes[contractCount][contractSystemProcedureCount];
-
-
-#define REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(contractName) { \
-constexpr unsigned int contractIndex = contractName##_CONTRACT_INDEX; \
-if (!contractName::__initializeEmpty) contractSystemProcedures[contractIndex][INITIALIZE] = (SYSTEM_PROCEDURE)contractName::__initialize;\
-contractSystemProcedureLocalsSizes[contractIndex][INITIALIZE] = contractName::__initializeLocalsSize; \
-if (!contractName::__beginEpochEmpty) contractSystemProcedures[contractIndex][BEGIN_EPOCH] = (SYSTEM_PROCEDURE)contractName::__beginEpoch;\
-contractSystemProcedureLocalsSizes[contractIndex][BEGIN_EPOCH] = contractName::__beginEpochLocalsSize; \
-if (!contractName::__endEpochEmpty) contractSystemProcedures[contractIndex][END_EPOCH] = (SYSTEM_PROCEDURE)contractName::__endEpoch;\
-contractSystemProcedureLocalsSizes[contractIndex][END_EPOCH] = contractName::__endEpochLocalsSize; \
-if (!contractName::__beginTickEmpty) contractSystemProcedures[contractIndex][BEGIN_TICK] = (SYSTEM_PROCEDURE)contractName::__beginTick;\
-contractSystemProcedureLocalsSizes[contractIndex][BEGIN_TICK] = contractName::__beginTickLocalsSize; \
-if (!contractName::__endTickEmpty) contractSystemProcedures[contractIndex][END_TICK] = (SYSTEM_PROCEDURE)contractName::__endTick;\
-contractSystemProcedureLocalsSizes[contractIndex][END_TICK] = contractName::__endTickLocalsSize; \
-if (!contractName::__preAcquireSharesEmpty) contractSystemProcedures[contractIndex][PRE_ACQUIRE_SHARES] = (SYSTEM_PROCEDURE)contractName::__preAcquireShares;\
-contractSystemProcedureLocalsSizes[contractIndex][PRE_ACQUIRE_SHARES] = contractName::__preAcquireSharesLocalsSize; \
-if (!contractName::__preReleaseSharesEmpty) contractSystemProcedures[contractIndex][PRE_RELEASE_SHARES] = (SYSTEM_PROCEDURE)contractName::__preReleaseShares;\
-contractSystemProcedureLocalsSizes[contractIndex][PRE_RELEASE_SHARES] = contractName::__preReleaseSharesLocalsSize; \
-if (!contractName::__postAcquireSharesEmpty) contractSystemProcedures[contractIndex][POST_ACQUIRE_SHARES] = (SYSTEM_PROCEDURE)contractName::__postAcquireShares;\
-contractSystemProcedureLocalsSizes[contractIndex][POST_ACQUIRE_SHARES] = contractName::__postAcquireSharesLocalsSize; \
-if (!contractName::__postReleaseSharesEmpty) contractSystemProcedures[contractIndex][POST_RELEASE_SHARES] = (SYSTEM_PROCEDURE)contractName::__postReleaseShares;\
-contractSystemProcedureLocalsSizes[contractIndex][POST_RELEASE_SHARES] = contractName::__postReleaseSharesLocalsSize; \
-if (!contractName::__postIncomingTransferEmpty) contractSystemProcedures[contractIndex][POST_INCOMING_TRANSFER] = (SYSTEM_PROCEDURE)contractName::__postIncomingTransfer;\
-contractSystemProcedureLocalsSizes[contractIndex][POST_INCOMING_TRANSFER] = contractName::__postIncomingTransferLocalsSize; \
-if (!contractName::__setShareholderProposalEmpty) contractSystemProcedures[contractIndex][SET_SHAREHOLDER_PROPOSAL] = (SYSTEM_PROCEDURE)contractName::__setShareholderProposal;\
-contractSystemProcedureLocalsSizes[contractIndex][SET_SHAREHOLDER_PROPOSAL] = contractName::__setShareholderProposalLocalsSize; \
-if (!contractName::__setShareholderVotesEmpty) contractSystemProcedures[contractIndex][SET_SHAREHOLDER_VOTES] = (SYSTEM_PROCEDURE)contractName::__setShareholderVotes;\
-contractSystemProcedureLocalsSizes[contractIndex][SET_SHAREHOLDER_VOTES] = contractName::__setShareholderVotesLocalsSize; \
-if (!contractName::__expandEmpty) contractExpandProcedures[contractIndex] = (EXPAND_PROCEDURE)contractName::__expand;\
-if (!contractName::__migrateEmpty) contractMigrateProcedures[contractIndex] = (MIGRATE_PROCEDURE)contractName::__migrate;\
-contractMigrateOldStateSizes[contractIndex] = contractName::__migrateOldStateSize;\
-contractMigrateLocalsSizes[contractIndex] = contractName::__migrateLocalsSize;\
-QpiContextForInit qpi(contractIndex); \
-contractName::__registerUserFunctionsAndProcedures(qpi); \
-static_assert(sizeof(contractName::StateData) <= MAX_CONTRACT_STATE_SIZE, "Size of contract state " #contractName " is too large!"); \
-}
-
-
-static void initializeContracts()
-{
- REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(QX);
- REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(QUOTTERY);
- REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(RANDOM);
- REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(QUTIL);
- REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(MLM);
- REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(GQMPROP);
- REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(SWATCH);
- REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(CCF);
- REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(QEARN);
- REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(QVAULT);
- REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(MSVAULT);
- REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(QBAY);
- REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(QSWAP);
- REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(NOST);
- REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(QDRAW);
- REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(RL);
- REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(QBOND);
- REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(QIP);
- REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(QRAFFLE);
- REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(QRWA);
- REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(QRP);
- REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(QTF);
- REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(QDUEL);
- REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(PULSE);
- REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(VOTTUNBRIDGE);
- REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(QUSINO);
- REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(ESCROW);
- REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(WOLFPACK);
- // new contracts should be added above this line
-#ifdef INCLUDE_CONTRACT_TEST_EXAMPLES
- REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(TESTEXA);
- REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(TESTEXB);
- REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(TESTEXC);
- REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(TESTEXD);
-#endif
-}
-
-// ----- Automatic Contract State Changes -----
-// NOTE: All state changes are currently only triggered during loading if the loaded size does not match the expected size.
-// If we ever need a reset or migrate where the state size remains the same, we have to change the implementation in loadContractStateFiles.
-enum ContractStateChangeType
-{
- // Keeps the saved state's old bytes, only zero-fills the new bytes at the end (used when struct grew; old fields preserved)
- PADDING,
- // Discards the saved state entirely, zeros the whole buffer
- RESET,
- // Migrate data from an old to a new state struct
- MIGRATE,
-};
-struct ContractStateChangeInfo
-{
- unsigned int contractIndex;
- ContractStateChangeType changeType;
- unsigned short changeEpoch; // extra safeguard to prevent accidental state change
-};
-// Contracts whose state struct changed this epoch. Update this list each epoch as needed.
-// Each entry is { CONTRACT_INDEX, PADDING or RESET or MIGRATE, EPOCH }
-// When enabling, replace both lines below, e.g.:
-//constexpr ContractStateChangeInfo contractStateChangeInfos[] = { { DUMMY_CONTRACT_INDEX, MIGRATE, 219 } };
-//constexpr unsigned int contractStateChangeCount = sizeof(contractStateChangeInfos) / sizeof(contractStateChangeInfos[0]);
- constexpr const ContractStateChangeInfo* contractStateChangeInfos = nullptr;
- constexpr unsigned int contractStateChangeCount = 0;
-
-
-// Class for registering and looking up user procedures independently of input type, for example for notifications
-class UserProcedureRegistry
-{
-public:
- struct UserProcedureData
- {
- USER_PROCEDURE procedure;
- unsigned int contractIndex;
- unsigned int localsSize;
- unsigned short inputSize;
- unsigned short outputSize;
- };
-
- void init()
- {
- setMemory(*this, 0);
- }
-
- bool add(unsigned int procedureId, const UserProcedureData& data)
- {
- const unsigned int cnt = (unsigned int)idToIndex.population();
- if (cnt >= idToIndex.capacity())
- return false;
-
- copyMemory(userProcData[cnt], data);
- idToIndex.set(procedureId, cnt);
-
- return true;
- }
-
- const UserProcedureData* get(unsigned int procedureId) const
- {
- unsigned int idx;
- if (!idToIndex.get(procedureId, idx))
- return nullptr;
- return userProcData + idx;
- }
-
-protected:
- UserProcedureData userProcData[MAX_CONTRACT_PROCEDURES_REGISTERED];
- QPI::HashMap idToIndex;
-};
-
-// For registering and looking up user procedures independently of input type (for notifications), initialized by initContractExec()
-GLOBAL_VAR_DECL UserProcedureRegistry* userProcedureRegistry GLOBAL_VAR_INIT(nullptr);
+#pragma once
+
+////////// Smart contracts \\\\\\\\\\
+
+// The order in this file is very important, because it restricts what is available to the contracts.
+// For example, a contract may only call a contract with lower index, which is enforced by order of
+// include / availability of definition.
+// Additionally, most types, functions, and variables of the core have to be defined after including
+// the contract to keep them unavailable in the contract code.
+
+
+// With no other includes before, the following are the only headers available to contracts.
+// When adding something, be cautious to keep access of contracts limited to safe features only.
+#include "pre_qpi_def.h"
+#include "contracts/qpi.h"
+#include "qpi_proposal_voting.h"
+
+// make interfaces to oracles available for all contracts
+#include "oracle_core/oracle_interfaces_def.h"
+
+#define QX_CONTRACT_INDEX 1
+#define CONTRACT_INDEX QX_CONTRACT_INDEX
+#define CONTRACT_STATE_TYPE QX
+#define CONTRACT_STATE2_TYPE QX2
+#include "contracts/Qx.h"
+
+#undef CONTRACT_INDEX
+#undef CONTRACT_STATE_TYPE
+#undef CONTRACT_STATE2_TYPE
+
+#define QUOTTERY_CONTRACT_INDEX 2
+#define CONTRACT_INDEX QUOTTERY_CONTRACT_INDEX
+#define CONTRACT_STATE_TYPE QUOTTERY
+#define CONTRACT_STATE2_TYPE QUOTTERY2
+#include "contracts/Quottery.h"
+
+#undef CONTRACT_INDEX
+#undef CONTRACT_STATE_TYPE
+#undef CONTRACT_STATE2_TYPE
+
+#define RANDOM_CONTRACT_INDEX 3
+#define CONTRACT_INDEX RANDOM_CONTRACT_INDEX
+#define CONTRACT_STATE_TYPE RANDOM
+#define CONTRACT_STATE2_TYPE RANDOM2
+#include "contracts/Random.h"
+
+#undef CONTRACT_INDEX
+#undef CONTRACT_STATE_TYPE
+#undef CONTRACT_STATE2_TYPE
+
+#define QUTIL_CONTRACT_INDEX 4
+#define CONTRACT_INDEX QUTIL_CONTRACT_INDEX
+#define CONTRACT_STATE_TYPE QUTIL
+#define CONTRACT_STATE2_TYPE QUTIL2
+#include "contracts/QUtil.h"
+
+#undef CONTRACT_INDEX
+#undef CONTRACT_STATE_TYPE
+#undef CONTRACT_STATE2_TYPE
+
+#define MLM_CONTRACT_INDEX 5
+#define CONTRACT_INDEX MLM_CONTRACT_INDEX
+#define CONTRACT_STATE_TYPE MLM
+#define CONTRACT_STATE2_TYPE MLM2
+#include "contracts/MyLastMatch.h"
+
+#undef CONTRACT_INDEX
+#undef CONTRACT_STATE_TYPE
+#undef CONTRACT_STATE2_TYPE
+
+#define GQMPROP_CONTRACT_INDEX 6
+#define CONTRACT_INDEX GQMPROP_CONTRACT_INDEX
+#define CONTRACT_STATE_TYPE GQMPROP
+#define CONTRACT_STATE2_TYPE GQMPROP2
+#include "contracts/GeneralQuorumProposal.h"
+
+#undef CONTRACT_INDEX
+#undef CONTRACT_STATE_TYPE
+#undef CONTRACT_STATE2_TYPE
+
+#define SWATCH_CONTRACT_INDEX 7
+#define CONTRACT_INDEX SWATCH_CONTRACT_INDEX
+#define CONTRACT_STATE_TYPE SWATCH
+#define CONTRACT_STATE2_TYPE SWATCH2
+#include "contracts/SupplyWatcher.h"
+
+#undef CONTRACT_INDEX
+#undef CONTRACT_STATE_TYPE
+#undef CONTRACT_STATE2_TYPE
+
+#define CCF_CONTRACT_INDEX 8
+#define CONTRACT_INDEX CCF_CONTRACT_INDEX
+#define CONTRACT_STATE_TYPE CCF
+#define CONTRACT_STATE2_TYPE CCF2
+#include "contracts/ComputorControlledFund.h"
+
+#undef CONTRACT_INDEX
+#undef CONTRACT_STATE_TYPE
+#undef CONTRACT_STATE2_TYPE
+
+#define QEARN_CONTRACT_INDEX 9
+#define CONTRACT_INDEX QEARN_CONTRACT_INDEX
+#define CONTRACT_STATE_TYPE QEARN
+#define CONTRACT_STATE2_TYPE QEARN2
+#include "contracts/Qearn.h"
+
+#undef CONTRACT_INDEX
+#undef CONTRACT_STATE_TYPE
+#undef CONTRACT_STATE2_TYPE
+
+#define QVAULT_CONTRACT_INDEX 10
+#define CONTRACT_INDEX QVAULT_CONTRACT_INDEX
+#define CONTRACT_STATE_TYPE QVAULT
+#define CONTRACT_STATE2_TYPE QVAULT2
+#include "contracts/QVAULT.h"
+
+#undef CONTRACT_INDEX
+#undef CONTRACT_STATE_TYPE
+#undef CONTRACT_STATE2_TYPE
+
+#define MSVAULT_CONTRACT_INDEX 11
+#define CONTRACT_INDEX MSVAULT_CONTRACT_INDEX
+#define CONTRACT_STATE_TYPE MSVAULT
+#define CONTRACT_STATE2_TYPE MSVAULT2
+#include "contracts/MsVault.h"
+
+#undef CONTRACT_INDEX
+#undef CONTRACT_STATE_TYPE
+#undef CONTRACT_STATE2_TYPE
+
+#define QBAY_CONTRACT_INDEX 12
+#define CONTRACT_INDEX QBAY_CONTRACT_INDEX
+#define CONTRACT_STATE_TYPE QBAY
+#define CONTRACT_STATE2_TYPE QBAY2
+#ifdef OLD_QBAY
+#include "contracts/Qbay_old.h"
+#else
+#include "contracts/Qbay.h"
+#endif
+
+#undef CONTRACT_INDEX
+#undef CONTRACT_STATE_TYPE
+#undef CONTRACT_STATE2_TYPE
+
+#define QSWAP_CONTRACT_INDEX 13
+#define CONTRACT_INDEX QSWAP_CONTRACT_INDEX
+#define CONTRACT_STATE_TYPE QSWAP
+#define CONTRACT_STATE2_TYPE QSWAP2
+#ifdef OLD_QSWAP
+#include "contracts/Qswap_old.h"
+#else
+#include "contracts/Qswap.h"
+#endif
+
+#undef CONTRACT_INDEX
+#undef CONTRACT_STATE_TYPE
+#undef CONTRACT_STATE2_TYPE
+
+#define NOST_CONTRACT_INDEX 14
+#define CONTRACT_INDEX NOST_CONTRACT_INDEX
+#define CONTRACT_STATE_TYPE NOST
+#define CONTRACT_STATE2_TYPE NOST2
+#include "contracts/Nostromo.h"
+
+#undef CONTRACT_INDEX
+#undef CONTRACT_STATE_TYPE
+#undef CONTRACT_STATE2_TYPE
+
+#define QDRAW_CONTRACT_INDEX 15
+#define CONTRACT_INDEX QDRAW_CONTRACT_INDEX
+#define CONTRACT_STATE_TYPE QDRAW
+#define CONTRACT_STATE2_TYPE QDRAW2
+#include "contracts/Qdraw.h"
+
+#undef CONTRACT_INDEX
+#undef CONTRACT_STATE_TYPE
+#undef CONTRACT_STATE2_TYPE
+
+#define RL_CONTRACT_INDEX 16
+#define CONTRACT_INDEX RL_CONTRACT_INDEX
+#define CONTRACT_STATE_TYPE RL
+#define CONTRACT_STATE2_TYPE RL2
+#include "contracts/RandomLottery.h"
+
+#undef CONTRACT_INDEX
+#undef CONTRACT_STATE_TYPE
+#undef CONTRACT_STATE2_TYPE
+
+#define QBOND_CONTRACT_INDEX 17
+#define CONTRACT_INDEX QBOND_CONTRACT_INDEX
+#define CONTRACT_STATE_TYPE QBOND
+#define CONTRACT_STATE2_TYPE QBOND2
+#include "contracts/QBond.h"
+
+#undef CONTRACT_INDEX
+#undef CONTRACT_STATE_TYPE
+#undef CONTRACT_STATE2_TYPE
+
+#define QIP_CONTRACT_INDEX 18
+#define CONTRACT_INDEX QIP_CONTRACT_INDEX
+#define CONTRACT_STATE_TYPE QIP
+#define CONTRACT_STATE2_TYPE QIP2
+#include "contracts/QIP.h"
+
+#undef CONTRACT_INDEX
+#undef CONTRACT_STATE_TYPE
+#undef CONTRACT_STATE2_TYPE
+
+#define QRAFFLE_CONTRACT_INDEX 19
+#define CONTRACT_INDEX QRAFFLE_CONTRACT_INDEX
+#define CONTRACT_STATE_TYPE QRAFFLE
+#define CONTRACT_STATE2_TYPE QRAFFLE2
+#include "contracts/QRaffle.h"
+
+#undef CONTRACT_INDEX
+#undef CONTRACT_STATE_TYPE
+#undef CONTRACT_STATE2_TYPE
+
+#define QRWA_CONTRACT_INDEX 20
+#define CONTRACT_INDEX QRWA_CONTRACT_INDEX
+#define CONTRACT_STATE_TYPE QRWA
+#define CONTRACT_STATE2_TYPE QRWA2
+#include "contracts/qRWA.h"
+
+#undef CONTRACT_INDEX
+#undef CONTRACT_STATE_TYPE
+#undef CONTRACT_STATE2_TYPE
+
+#define QRP_CONTRACT_INDEX 21
+#define CONTRACT_INDEX QRP_CONTRACT_INDEX
+#define CONTRACT_STATE_TYPE QRP
+#define CONTRACT_STATE2_TYPE QRP2
+#include "contracts/QReservePool.h"
+
+#undef CONTRACT_INDEX
+#undef CONTRACT_STATE_TYPE
+#undef CONTRACT_STATE2_TYPE
+
+#define QTF_CONTRACT_INDEX 22
+#define CONTRACT_INDEX QTF_CONTRACT_INDEX
+#define CONTRACT_STATE_TYPE QTF
+#define CONTRACT_STATE2_TYPE QTF2
+#include "contracts/QThirtyFour.h"
+
+#undef CONTRACT_INDEX
+#undef CONTRACT_STATE_TYPE
+#undef CONTRACT_STATE2_TYPE
+
+#define QDUEL_CONTRACT_INDEX 23
+#define CONTRACT_INDEX QDUEL_CONTRACT_INDEX
+#define CONTRACT_STATE_TYPE QDUEL
+#define CONTRACT_STATE2_TYPE QDUEL2
+#include "contracts/QDuel.h"
+
+#undef CONTRACT_INDEX
+#undef CONTRACT_STATE_TYPE
+#undef CONTRACT_STATE2_TYPE
+
+#define PULSE_CONTRACT_INDEX 24
+#define CONTRACT_INDEX PULSE_CONTRACT_INDEX
+#define CONTRACT_STATE_TYPE PULSE
+#define CONTRACT_STATE2_TYPE PULSE2
+#include "contracts/Pulse.h"
+
+#undef CONTRACT_INDEX
+#undef CONTRACT_STATE_TYPE
+#undef CONTRACT_STATE2_TYPE
+
+#define VOTTUNBRIDGE_CONTRACT_INDEX 25
+#define CONTRACT_INDEX VOTTUNBRIDGE_CONTRACT_INDEX
+#define CONTRACT_STATE_TYPE VOTTUNBRIDGE
+#define CONTRACT_STATE2_TYPE VOTTUNBRIDGE2
+#include "contracts/VottunBridge.h"
+
+#undef CONTRACT_INDEX
+#undef CONTRACT_STATE_TYPE
+#undef CONTRACT_STATE2_TYPE
+
+#define QUSINO_CONTRACT_INDEX 26
+#define CONTRACT_INDEX QUSINO_CONTRACT_INDEX
+#define CONTRACT_STATE_TYPE QUSINO
+#define CONTRACT_STATE2_TYPE QUSINO2
+#include "contracts/Qusino.h"
+
+#undef CONTRACT_INDEX
+#undef CONTRACT_STATE_TYPE
+#undef CONTRACT_STATE2_TYPE
+
+#define ESCROW_CONTRACT_INDEX 27
+#define CONTRACT_INDEX ESCROW_CONTRACT_INDEX
+#define CONTRACT_STATE_TYPE ESCROW
+#define CONTRACT_STATE2_TYPE ESCROW2
+#include "contracts/Escrow.h"
+
+#undef CONTRACT_INDEX
+#undef CONTRACT_STATE_TYPE
+#undef CONTRACT_STATE2_TYPE
+
+#define WOLFPACK_CONTRACT_INDEX 28
+#define CONTRACT_INDEX WOLFPACK_CONTRACT_INDEX
+#define CONTRACT_STATE_TYPE WOLFPACK
+#define CONTRACT_STATE2_TYPE WOLFPACK2
+#include "contracts/GGWP.h"
+
+#undef CONTRACT_INDEX
+#undef CONTRACT_STATE_TYPE
+#undef CONTRACT_STATE2_TYPE
+
+#define QASSAND_CONTRACT_INDEX 29
+#define CONTRACT_INDEX QASSAND_CONTRACT_INDEX
+#define CONTRACT_STATE_TYPE QASSAND
+#define CONTRACT_STATE2_TYPE QASSAND2
+#include "contracts/Qassand.h"
+
+// new contracts should be added above this line
+
+#ifdef INCLUDE_CONTRACT_TEST_EXAMPLES
+
+constexpr unsigned short TESTEXA_CONTRACT_INDEX = (CONTRACT_INDEX + 1);
+#undef CONTRACT_INDEX
+#undef CONTRACT_STATE_TYPE
+#undef CONTRACT_STATE2_TYPE
+#define CONTRACT_INDEX TESTEXA_CONTRACT_INDEX
+#define CONTRACT_STATE_TYPE TESTEXA
+#define CONTRACT_STATE2_TYPE TESTEXA2
+#include "contracts/TestExampleA.h"
+constexpr unsigned short TESTEXB_CONTRACT_INDEX = (CONTRACT_INDEX + 1);
+#undef CONTRACT_INDEX
+#undef CONTRACT_STATE_TYPE
+#undef CONTRACT_STATE2_TYPE
+#define CONTRACT_INDEX TESTEXB_CONTRACT_INDEX
+#define CONTRACT_STATE_TYPE TESTEXB
+#define CONTRACT_STATE2_TYPE TESTEXB2
+#include "contracts/TestExampleB.h"
+constexpr unsigned short TESTEXC_CONTRACT_INDEX = (CONTRACT_INDEX + 1);
+#undef CONTRACT_INDEX
+#undef CONTRACT_STATE_TYPE
+#undef CONTRACT_STATE2_TYPE
+#define CONTRACT_INDEX TESTEXC_CONTRACT_INDEX
+#define CONTRACT_STATE_TYPE TESTEXC
+#define CONTRACT_STATE2_TYPE TESTEXC2
+#include "contracts/TestExampleC.h"
+constexpr unsigned short TESTEXD_CONTRACT_INDEX = (CONTRACT_INDEX + 1);
+#undef CONTRACT_INDEX
+#undef CONTRACT_STATE_TYPE
+#undef CONTRACT_STATE2_TYPE
+#define CONTRACT_INDEX TESTEXD_CONTRACT_INDEX
+#define CONTRACT_STATE_TYPE TESTEXD
+#define CONTRACT_STATE2_TYPE TESTEXD2
+#include "contracts/TestExampleD.h"
+#endif
+
+#define MAX_CONTRACT_ITERATION_DURATION 0 // In milliseconds, must be above 0; for now set to 0 to disable timeout, because a rollback mechanism needs to be implemented to properly handle timeout
+
+#undef INITIALIZE
+#undef BEGIN_EPOCH
+#undef END_EPOCH
+#undef BEGIN_TICK
+#undef END_TICK
+#undef PRE_RELEASE_SHARES
+#undef PRE_ACQUIRE_SHARES
+#undef POST_RELEASE_SHARES
+#undef POST_ACQUIRE_SHARES
+#undef POST_INCOMING_TRANSFER
+#undef SET_SHAREHOLDER_PROPOSAL
+#undef SET_SHAREHOLDER_VOTES
+
+
+// The following are included after the contracts to keep their definitions and dependencies
+// inaccessible for contracts
+#include "qpi_collection_impl.h"
+#include "qpi_trivial_impl.h"
+#include "qpi_hash_map_impl.h"
+#include "qpi_linked_list_impl.h"
+
+#include "platform/global_var.h"
+
+#include "network_messages/common_def.h"
+
+struct Contract0State
+{
+ long long contractFeeReserves[MAX_NUMBER_OF_CONTRACTS];
+};
+
+struct IPO
+{
+ m256i publicKeys[NUMBER_OF_COMPUTORS];
+ long long prices[NUMBER_OF_COMPUTORS];
+};
+
+static_assert(sizeof(IPO) == 32 * NUMBER_OF_COMPUTORS + 8 * NUMBER_OF_COMPUTORS, "Something is wrong with the struct size.");
+
+
+constexpr struct ContractDescription
+{
+ char assetName[8];
+ // constructionEpoch needs to be set to after IPO (IPO is before construction)
+ unsigned short constructionEpoch, destructionEpoch;
+ unsigned long long stateSize;
+} contractDescriptions[] = {
+ {"", 0, 0, sizeof(Contract0State)},
+ {"QX", 66, 10000, sizeof(QX::StateData)},
+ {"QTRY", 72, 10000, sizeof(QUOTTERY::StateData)},
+ {"RANDOM", 88, 10000, sizeof(RANDOM::StateData)},
+ {"QUTIL", 99, 10000, sizeof(QUTIL::StateData)},
+ {"MLM", 112, 10000, sizeof(IPO)},
+ {"GQMPROP", 123, 10000, sizeof(GQMPROP::StateData)},
+ {"SWATCH", 123, 10000, sizeof(IPO)},
+ {"CCF", 127, 10000, sizeof(CCF::StateData)}, // proposal in epoch 125, IPO in 126, construction and first use in 127
+ {"QEARN", 137, 10000, sizeof(QEARN::StateData)}, // proposal in epoch 135, IPO in 136, construction in 137 / first donation after END_EPOCH, first round in epoch 138
+ {"QVAULT", 138, 10000, sizeof(QVAULT::StateData)}, // proposal in epoch 136, IPO in 137, construction and first use in 138
+ {"MSVAULT", 149, 10000, sizeof(MSVAULT::StateData)}, // proposal in epoch 147, IPO in 148, construction and first use in 149
+ {"QBAY", 154, 10000, sizeof(QBAY::StateData)}, // proposal in epoch 152, IPO in 153, construction and first use in 154
+ {"QSWAP", 171, 10000, sizeof(QSWAP::StateData)}, // proposal in epoch 169, IPO in 170, construction and first use in 171
+ {"NOST", 172, 10000, sizeof(NOST::StateData)}, // proposal in epoch 170, IPO in 171, construction and first use in 172
+ {"QDRAW", 179, 10000, sizeof(QDRAW::StateData)}, // proposal in epoch 177, IPO in 178, construction and first use in 179
+ {"RL", 182, 10000, sizeof(RL::StateData)}, // proposal in epoch 180, IPO in 181, construction and first use in 182
+ {"QBOND", 182, 10000, sizeof(QBOND::StateData)}, // proposal in epoch 180, IPO in 181, construction and first use in 182
+ {"QIP", 189, 10000, sizeof(QIP::StateData)}, // proposal in epoch 187, IPO in 188, construction and first use in 189
+ {"QRAFFLE", 192, 10000, sizeof(QRAFFLE::StateData)}, // proposal in epoch 190, IPO in 191, construction and first use in 192
+ {"QRWA", 197, 10000, sizeof(QRWA::StateData)}, // proposal in epoch 195, IPO in 196, construction and first use in 197
+ {"QRP", 199, 10000, sizeof(IPO)}, // proposal in epoch 197, IPO in 198, construction and first use in 199
+ {"QTF", 199, 10000, sizeof(QTF::StateData)}, // proposal in epoch 197, IPO in 198, construction and first use in 199
+ {"QDUEL", 199, 10000, sizeof(QDUEL::StateData)}, // proposal in epoch 197, IPO in 198, construction and first use in 199
+ {"PULSE", 204, 10000, sizeof(PULSE::StateData)}, // proposal in epoch 202, IPO in 203, construction and first use in 204
+ {"VOTTUN", 206, 10000, sizeof(VOTTUNBRIDGE::StateData)}, // proposal in epoch 204, IPO in 205, construction and first use in 206
+ {"QUSINO", 208, 10000, sizeof(QUSINO::StateData)}, // proposal in epoch 206, IPO in 207, construction and first use in 208
+ {"ESCROW", 210, 10000, sizeof(ESCROW::StateData)}, // proposal in epoch 208, IPO in 209, construction and first use in 210
+ {"GGWP", 218, 10000, sizeof(WOLFPACK::StateData)}, // proposal in epoch 216, IPO in 217, construction and first use in 218
+ {"QASSAND", QASSAND_CONSTRUCTION_EPOCH_PLACEHOLDER, 10000, sizeof(QASSAND::StateData)},
+ // new contracts should be added above this line
+#ifdef INCLUDE_CONTRACT_TEST_EXAMPLES
+ {"TESTEXA", 138, 10000, sizeof(TESTEXA::StateData)},
+ {"TESTEXB", 138, 10000, sizeof(TESTEXB::StateData)},
+ {"TESTEXC", 138, 10000, sizeof(IPO)},
+ {"TESTEXD", 155, 10000, sizeof(IPO)},
+#endif
+};
+
+constexpr unsigned int contractCount = sizeof(contractDescriptions) / sizeof(contractDescriptions[0]);
+
+GLOBAL_VAR_DECL EXPAND_PROCEDURE contractExpandProcedures[contractCount];
+
+GLOBAL_VAR_DECL MIGRATE_PROCEDURE contractMigrateProcedures[contractCount];
+GLOBAL_VAR_DECL unsigned long long contractMigrateOldStateSizes[contractCount];
+GLOBAL_VAR_DECL unsigned short contractMigrateLocalsSizes[contractCount];
+
+// TODO: all below are filled very sparsely, so a better data structure could save almost all the memory
+GLOBAL_VAR_DECL USER_FUNCTION contractUserFunctions[contractCount][65536];
+GLOBAL_VAR_DECL unsigned short contractUserFunctionInputSizes[contractCount][65536];
+GLOBAL_VAR_DECL unsigned short contractUserFunctionOutputSizes[contractCount][65536];
+// This has been changed to unsigned short to avoid the misalignment issue happening in epochs 109 and 110,
+// probably due to too high numbers in contractUserProcedureLocalsSizes causing stack buffer alloc to fail
+// probably due to buffer overflow that is difficult to reproduce in test net
+// TODO: change back to unsigned int
+GLOBAL_VAR_DECL unsigned short contractUserFunctionLocalsSizes[contractCount][65536];
+GLOBAL_VAR_DECL USER_PROCEDURE contractUserProcedures[contractCount][65536];
+GLOBAL_VAR_DECL unsigned short contractUserProcedureInputSizes[contractCount][65536];
+GLOBAL_VAR_DECL unsigned short contractUserProcedureOutputSizes[contractCount][65536];
+// This has been changed to unsigned short to avoid the misalignment issue happening in epochs 109 and 110,
+// probably due to too high numbers in contractUserProcedureLocalsSizes causing stack buffer alloc to fail
+// probably due to buffer overflow that is difficult to reproduce in test net
+// TODO: change back to unsigned int
+GLOBAL_VAR_DECL unsigned short contractUserProcedureLocalsSizes[contractCount][65536];
+
+enum SystemProcedureID
+{
+ INITIALIZE = 0,
+ BEGIN_EPOCH,
+ END_EPOCH,
+ BEGIN_TICK,
+ END_TICK,
+ PRE_RELEASE_SHARES,
+ PRE_ACQUIRE_SHARES,
+ POST_RELEASE_SHARES,
+ POST_ACQUIRE_SHARES,
+ POST_INCOMING_TRANSFER,
+ SET_SHAREHOLDER_PROPOSAL,
+ SET_SHAREHOLDER_VOTES,
+ contractSystemProcedureCount,
+};
+
+enum OtherEntryPointIDs
+{
+ // Used together with SystemProcedureID values, so there must be no overlap!
+ USER_PROCEDURE_CALL = contractSystemProcedureCount + 1,
+ USER_FUNCTION_CALL = contractSystemProcedureCount + 2,
+ REGISTER_USER_FUNCTIONS_AND_PROCEDURES_CALL = contractSystemProcedureCount + 3,
+ USER_PROCEDURE_NOTIFICATION_CALL = contractSystemProcedureCount + 4,
+ MIGRATE_PROCEDURE_CALL = contractSystemProcedureCount + 5,
+};
+
+GLOBAL_VAR_DECL SYSTEM_PROCEDURE contractSystemProcedures[contractCount][contractSystemProcedureCount];
+GLOBAL_VAR_DECL unsigned short contractSystemProcedureLocalsSizes[contractCount][contractSystemProcedureCount];
+
+
+#define REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(contractName) { \
+constexpr unsigned int contractIndex = contractName##_CONTRACT_INDEX; \
+if (!contractName::__initializeEmpty) contractSystemProcedures[contractIndex][INITIALIZE] = (SYSTEM_PROCEDURE)contractName::__initialize;\
+contractSystemProcedureLocalsSizes[contractIndex][INITIALIZE] = contractName::__initializeLocalsSize; \
+if (!contractName::__beginEpochEmpty) contractSystemProcedures[contractIndex][BEGIN_EPOCH] = (SYSTEM_PROCEDURE)contractName::__beginEpoch;\
+contractSystemProcedureLocalsSizes[contractIndex][BEGIN_EPOCH] = contractName::__beginEpochLocalsSize; \
+if (!contractName::__endEpochEmpty) contractSystemProcedures[contractIndex][END_EPOCH] = (SYSTEM_PROCEDURE)contractName::__endEpoch;\
+contractSystemProcedureLocalsSizes[contractIndex][END_EPOCH] = contractName::__endEpochLocalsSize; \
+if (!contractName::__beginTickEmpty) contractSystemProcedures[contractIndex][BEGIN_TICK] = (SYSTEM_PROCEDURE)contractName::__beginTick;\
+contractSystemProcedureLocalsSizes[contractIndex][BEGIN_TICK] = contractName::__beginTickLocalsSize; \
+if (!contractName::__endTickEmpty) contractSystemProcedures[contractIndex][END_TICK] = (SYSTEM_PROCEDURE)contractName::__endTick;\
+contractSystemProcedureLocalsSizes[contractIndex][END_TICK] = contractName::__endTickLocalsSize; \
+if (!contractName::__preAcquireSharesEmpty) contractSystemProcedures[contractIndex][PRE_ACQUIRE_SHARES] = (SYSTEM_PROCEDURE)contractName::__preAcquireShares;\
+contractSystemProcedureLocalsSizes[contractIndex][PRE_ACQUIRE_SHARES] = contractName::__preAcquireSharesLocalsSize; \
+if (!contractName::__preReleaseSharesEmpty) contractSystemProcedures[contractIndex][PRE_RELEASE_SHARES] = (SYSTEM_PROCEDURE)contractName::__preReleaseShares;\
+contractSystemProcedureLocalsSizes[contractIndex][PRE_RELEASE_SHARES] = contractName::__preReleaseSharesLocalsSize; \
+if (!contractName::__postAcquireSharesEmpty) contractSystemProcedures[contractIndex][POST_ACQUIRE_SHARES] = (SYSTEM_PROCEDURE)contractName::__postAcquireShares;\
+contractSystemProcedureLocalsSizes[contractIndex][POST_ACQUIRE_SHARES] = contractName::__postAcquireSharesLocalsSize; \
+if (!contractName::__postReleaseSharesEmpty) contractSystemProcedures[contractIndex][POST_RELEASE_SHARES] = (SYSTEM_PROCEDURE)contractName::__postReleaseShares;\
+contractSystemProcedureLocalsSizes[contractIndex][POST_RELEASE_SHARES] = contractName::__postReleaseSharesLocalsSize; \
+if (!contractName::__postIncomingTransferEmpty) contractSystemProcedures[contractIndex][POST_INCOMING_TRANSFER] = (SYSTEM_PROCEDURE)contractName::__postIncomingTransfer;\
+contractSystemProcedureLocalsSizes[contractIndex][POST_INCOMING_TRANSFER] = contractName::__postIncomingTransferLocalsSize; \
+if (!contractName::__setShareholderProposalEmpty) contractSystemProcedures[contractIndex][SET_SHAREHOLDER_PROPOSAL] = (SYSTEM_PROCEDURE)contractName::__setShareholderProposal;\
+contractSystemProcedureLocalsSizes[contractIndex][SET_SHAREHOLDER_PROPOSAL] = contractName::__setShareholderProposalLocalsSize; \
+if (!contractName::__setShareholderVotesEmpty) contractSystemProcedures[contractIndex][SET_SHAREHOLDER_VOTES] = (SYSTEM_PROCEDURE)contractName::__setShareholderVotes;\
+contractSystemProcedureLocalsSizes[contractIndex][SET_SHAREHOLDER_VOTES] = contractName::__setShareholderVotesLocalsSize; \
+if (!contractName::__expandEmpty) contractExpandProcedures[contractIndex] = (EXPAND_PROCEDURE)contractName::__expand;\
+if (!contractName::__migrateEmpty) contractMigrateProcedures[contractIndex] = (MIGRATE_PROCEDURE)contractName::__migrate;\
+contractMigrateOldStateSizes[contractIndex] = contractName::__migrateOldStateSize;\
+contractMigrateLocalsSizes[contractIndex] = contractName::__migrateLocalsSize;\
+QpiContextForInit qpi(contractIndex); \
+contractName::__registerUserFunctionsAndProcedures(qpi); \
+static_assert(sizeof(contractName::StateData) <= MAX_CONTRACT_STATE_SIZE, "Size of contract state " #contractName " is too large!"); \
+}
+
+
+static void initializeContracts()
+{
+ REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(QX);
+ REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(QUOTTERY);
+ REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(RANDOM);
+ REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(QUTIL);
+ REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(MLM);
+ REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(GQMPROP);
+ REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(SWATCH);
+ REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(CCF);
+ REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(QEARN);
+ REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(QVAULT);
+ REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(MSVAULT);
+ REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(QBAY);
+ REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(QSWAP);
+ REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(NOST);
+ REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(QDRAW);
+ REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(RL);
+ REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(QBOND);
+ REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(QIP);
+ REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(QRAFFLE);
+ REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(QRWA);
+ REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(QRP);
+ REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(QTF);
+ REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(QDUEL);
+ REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(PULSE);
+ REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(VOTTUNBRIDGE);
+ REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(QUSINO);
+ REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(ESCROW);
+ REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(WOLFPACK);
+ REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(QASSAND);
+ // new contracts should be added above this line
+#ifdef INCLUDE_CONTRACT_TEST_EXAMPLES
+ REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(TESTEXA);
+ REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(TESTEXB);
+ REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(TESTEXC);
+ REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(TESTEXD);
+#endif
+}
+
+// ----- Automatic Contract State Changes -----
+// NOTE: All state changes are currently only triggered during loading if the loaded size does not match the expected size.
+// If we ever need a reset or migrate where the state size remains the same, we have to change the implementation in loadContractStateFiles.
+enum ContractStateChangeType
+{
+ // Keeps the saved state's old bytes, only zero-fills the new bytes at the end (used when struct grew; old fields preserved)
+ PADDING,
+ // Discards the saved state entirely, zeros the whole buffer
+ RESET,
+ // Migrate data from an old to a new state struct
+ MIGRATE,
+};
+struct ContractStateChangeInfo
+{
+ unsigned int contractIndex;
+ ContractStateChangeType changeType;
+ unsigned short changeEpoch; // extra safeguard to prevent accidental state change
+};
+// Contracts whose state struct changed this epoch. Update this list each epoch as needed.
+// Each entry is { CONTRACT_INDEX, PADDING or RESET or MIGRATE, EPOCH }
+// When enabling, replace both lines below, e.g.:
+//constexpr ContractStateChangeInfo contractStateChangeInfos[] = { { DUMMY_CONTRACT_INDEX, MIGRATE, 219 } };
+//constexpr unsigned int contractStateChangeCount = sizeof(contractStateChangeInfos) / sizeof(contractStateChangeInfos[0]);
+ constexpr const ContractStateChangeInfo* contractStateChangeInfos = nullptr;
+ constexpr unsigned int contractStateChangeCount = 0;
+
+
+// Class for registering and looking up user procedures independently of input type, for example for notifications
+class UserProcedureRegistry
+{
+public:
+ struct UserProcedureData
+ {
+ USER_PROCEDURE procedure;
+ unsigned int contractIndex;
+ unsigned int localsSize;
+ unsigned short inputSize;
+ unsigned short outputSize;
+ };
+
+ void init()
+ {
+ setMemory(*this, 0);
+ }
+
+ bool add(unsigned int procedureId, const UserProcedureData& data)
+ {
+ const unsigned int cnt = (unsigned int)idToIndex.population();
+ if (cnt >= idToIndex.capacity())
+ return false;
+
+ copyMemory(userProcData[cnt], data);
+ idToIndex.set(procedureId, cnt);
+
+ return true;
+ }
+
+ const UserProcedureData* get(unsigned int procedureId) const
+ {
+ unsigned int idx;
+ if (!idToIndex.get(procedureId, idx))
+ return nullptr;
+ return userProcData + idx;
+ }
+
+protected:
+ UserProcedureData userProcData[MAX_CONTRACT_PROCEDURES_REGISTERED];
+ QPI::HashMap idToIndex;
+};
+
+// For registering and looking up user procedures independently of input type (for notifications), initialized by initContractExec()
+GLOBAL_VAR_DECL UserProcedureRegistry* userProcedureRegistry GLOBAL_VAR_INIT(nullptr);
diff --git a/src/contracts/Qassand.h b/src/contracts/Qassand.h
new file mode 100644
index 000000000..9053351db
--- /dev/null
+++ b/src/contracts/Qassand.h
@@ -0,0 +1,256 @@
+using namespace QPI;
+
+// QASSAND is the Qubic contract implementation name for the Qassandra protocol v0.
+constexpr uint16 QASSAND_VERSION = 0;
+constexpr uint16 QASSAND_CONSTRUCTION_EPOCH_PLACEHOLDER = 10000;
+constexpr uint64 QASSAND_PROTOCOL_FEE = 75000;
+constexpr uint64 QASSAND_BURN_FEE = 25000;
+constexpr uint64 QASSAND_PING_FEE = QASSAND_PROTOCOL_FEE + QASSAND_BURN_FEE;
+constexpr uint8 QASSAND_SUCCESS = 0;
+constexpr uint8 QASSAND_UNDERPAID = 1;
+constexpr uint8 QASSAND_UNKNOWN_LANE = 2;
+constexpr uint16 QASSAND_LANE_UNKNOWN = 0;
+constexpr uint16 QASSAND_LANE_FORECASTING = 1;
+constexpr uint16 QASSAND_LANE_STABLE_OPERATIONS = 2;
+constexpr uint16 QASSAND_LANE_DATA_ATTESTATION = 3;
+
+struct QASSAND2
+{
+};
+
+struct QASSAND : public ContractBase
+{
+ struct StateData
+ {
+ uint16 version;
+ uint16 constructionEpoch;
+ uint64 protocolFee;
+ uint64 burnFee;
+ uint64 totalPingCount;
+ uint64 protocolEarnedFee;
+ uint64 burnEarnedFee;
+ uint64 pendingBurnAmount;
+ uint64 totalBurnedAmount;
+ };
+
+ struct Ping_input
+ {
+ };
+
+ struct Ping_output
+ {
+ uint8 returnCode;
+ uint64 acceptedFee;
+ uint64 refundedAmount;
+ uint64 protocolEarnedFee;
+ uint64 burnEarnedFee;
+ uint64 totalPingCount;
+ };
+
+ struct Ping_locals
+ {
+ uint64 paidAmount;
+ uint64 refundAmount;
+ };
+
+ struct GetInfo_input
+ {
+ };
+
+ struct GetInfo_output
+ {
+ Array protocolName;
+ uint16 version;
+ uint16 constructionEpoch;
+ uint64 totalPingCount;
+ };
+
+ struct GetFeeInfo_input
+ {
+ };
+
+ struct GetFeeInfo_output
+ {
+ uint64 pingFee;
+ uint64 protocolFee;
+ uint64 burnFee;
+ uint64 protocolEarnedFee;
+ uint64 burnEarnedFee;
+ };
+
+ struct GetBurnInfo_input
+ {
+ };
+
+ struct GetBurnInfo_output
+ {
+ uint64 pendingBurnAmount;
+ uint64 totalBurnedAmount;
+ };
+
+ struct GetLaneInfo_input
+ {
+ uint16 laneId;
+ };
+
+ struct GetLaneInfo_output
+ {
+ uint8 returnCode;
+ uint16 laneId;
+ Array laneName;
+ uint64 requiredFee;
+ };
+
+ struct END_TICK_locals
+ {
+ uint64 burnAmount;
+ };
+
+ INITIALIZE()
+ {
+ state.mut().version = QASSAND_VERSION;
+ state.mut().constructionEpoch = QASSAND_CONSTRUCTION_EPOCH_PLACEHOLDER;
+ state.mut().protocolFee = QASSAND_PROTOCOL_FEE;
+ state.mut().burnFee = QASSAND_BURN_FEE;
+ }
+
+ REGISTER_USER_FUNCTIONS_AND_PROCEDURES()
+ {
+ REGISTER_USER_PROCEDURE(Ping, 1);
+ REGISTER_USER_FUNCTION(GetInfo, 1);
+ REGISTER_USER_FUNCTION(GetFeeInfo, 2);
+ REGISTER_USER_FUNCTION(GetBurnInfo, 3);
+ REGISTER_USER_FUNCTION(GetLaneInfo, 4);
+ }
+
+ END_TICK_WITH_LOCALS()
+ {
+ locals.burnAmount = state.get().pendingBurnAmount;
+ if (locals.burnAmount == 0)
+ {
+ return;
+ }
+
+ qpi.burn(locals.burnAmount);
+ state.mut().pendingBurnAmount = 0;
+ state.mut().totalBurnedAmount += locals.burnAmount;
+ }
+
+ PUBLIC_PROCEDURE_WITH_LOCALS(Ping)
+ {
+ locals.paidAmount = qpi.invocationReward();
+
+ if (locals.paidAmount < (state.get().protocolFee + state.get().burnFee))
+ {
+ if (locals.paidAmount != 0)
+ {
+ qpi.transfer(qpi.invocator(), locals.paidAmount);
+ }
+ output.returnCode = QASSAND_UNDERPAID;
+ output.refundedAmount = locals.paidAmount;
+ return;
+ }
+
+ locals.refundAmount = locals.paidAmount - (state.get().protocolFee + state.get().burnFee);
+ if (locals.refundAmount != 0)
+ {
+ qpi.transfer(qpi.invocator(), locals.refundAmount);
+ }
+
+ state.mut().totalPingCount += 1;
+ state.mut().protocolEarnedFee += state.get().protocolFee;
+ state.mut().burnEarnedFee += state.get().burnFee;
+ state.mut().pendingBurnAmount += state.get().burnFee;
+
+ output.returnCode = QASSAND_SUCCESS;
+ output.acceptedFee = (state.get().protocolFee + state.get().burnFee);
+ output.refundedAmount = locals.refundAmount;
+ output.protocolEarnedFee = state.get().protocolFee;
+ output.burnEarnedFee = state.get().burnFee;
+ output.totalPingCount = state.get().totalPingCount;
+ }
+
+ PUBLIC_FUNCTION(GetInfo)
+ {
+ output.protocolName.set(0, 81);
+ output.protocolName.set(1, 97);
+ output.protocolName.set(2, 115);
+ output.protocolName.set(3, 115);
+ output.protocolName.set(4, 97);
+ output.protocolName.set(5, 110);
+ output.protocolName.set(6, 100);
+ output.protocolName.set(7, 114);
+ output.protocolName.set(8, 97);
+ output.version = state.get().version;
+ output.constructionEpoch = state.get().constructionEpoch;
+ output.totalPingCount = state.get().totalPingCount;
+ }
+
+ PUBLIC_FUNCTION(GetFeeInfo)
+ {
+ output.pingFee = (state.get().protocolFee + state.get().burnFee);
+ output.protocolFee = state.get().protocolFee;
+ output.burnFee = state.get().burnFee;
+ output.protocolEarnedFee = state.get().protocolEarnedFee;
+ output.burnEarnedFee = state.get().burnEarnedFee;
+ }
+
+ PUBLIC_FUNCTION(GetBurnInfo)
+ {
+ output.pendingBurnAmount = state.get().pendingBurnAmount;
+ output.totalBurnedAmount = state.get().totalBurnedAmount;
+ }
+
+ PUBLIC_FUNCTION(GetLaneInfo)
+ {
+ if (input.laneId == QASSAND_LANE_FORECASTING)
+ {
+ output.returnCode = QASSAND_SUCCESS;
+ output.laneId = QASSAND_LANE_FORECASTING;
+ output.laneName.set(0, 70);
+ output.laneName.set(1, 111);
+ output.laneName.set(2, 114);
+ output.laneName.set(3, 101);
+ output.laneName.set(4, 99);
+ output.laneName.set(5, 97);
+ output.laneName.set(6, 115);
+ output.laneName.set(7, 116);
+ output.laneName.set(8, 105);
+ output.laneName.set(9, 110);
+ output.laneName.set(10, 103);
+ output.requiredFee = 0;
+ return;
+ }
+
+ if (input.laneId == QASSAND_LANE_STABLE_OPERATIONS)
+ {
+ output.returnCode = QASSAND_SUCCESS;
+ output.laneId = QASSAND_LANE_STABLE_OPERATIONS;
+ output.laneName.set(0, 83);
+ output.laneName.set(1, 116);
+ output.laneName.set(2, 97);
+ output.laneName.set(3, 98);
+ output.laneName.set(4, 108);
+ output.laneName.set(5, 101);
+ output.laneName.set(6, 79);
+ output.laneName.set(7, 112);
+ output.laneName.set(8, 115);
+ output.requiredFee = 0;
+ return;
+ }
+
+ if (input.laneId == QASSAND_LANE_DATA_ATTESTATION)
+ {
+ output.returnCode = QASSAND_SUCCESS;
+ output.laneId = QASSAND_LANE_DATA_ATTESTATION;
+ output.laneName.set(0, 68);
+ output.laneName.set(1, 97);
+ output.laneName.set(2, 116);
+ output.laneName.set(3, 97);
+ output.requiredFee = 0;
+ return;
+ }
+
+ output.returnCode = QASSAND_UNKNOWN_LANE;
+ }
+};
diff --git a/test/contract_qassand.cpp b/test/contract_qassand.cpp
new file mode 100644
index 000000000..6b4ee58e6
--- /dev/null
+++ b/test/contract_qassand.cpp
@@ -0,0 +1,230 @@
+#define NO_UEFI
+
+#include "contract_testing.h"
+
+constexpr uint16 QASSAND_PROC_PING = 1;
+constexpr uint16 QASSAND_FUNC_GET_INFO = 1;
+constexpr uint16 QASSAND_FUNC_GET_FEE_INFO = 2;
+constexpr uint16 QASSAND_FUNC_GET_BURN_INFO = 3;
+constexpr uint16 QASSAND_FUNC_GET_LANE_INFO = 4;
+
+static const id QASSAND_CONTRACT_ID(QASSAND_CONTRACT_INDEX, 0, 0, 0);
+
+class QassandChecker : public QASSAND, public QASSAND::StateData
+{
+public:
+ uint16 versionValue() const { return version; }
+ uint16 constructionEpochValue() const { return constructionEpoch; }
+ uint64 totalPingCountValue() const { return totalPingCount; }
+ uint64 protocolEarnedFeeValue() const { return protocolEarnedFee; }
+ uint64 burnEarnedFeeValue() const { return burnEarnedFee; }
+ uint64 pendingBurnAmountValue() const { return pendingBurnAmount; }
+ uint64 totalBurnedAmountValue() const { return totalBurnedAmount; }
+};
+
+class ContractTestingQassand : protected ContractTesting
+{
+public:
+ ContractTestingQassand()
+ {
+ initEmptySpectrum();
+ initEmptyUniverse();
+ INIT_CONTRACT(QASSAND);
+ callSystemProcedure(QASSAND_CONTRACT_INDEX, INITIALIZE);
+ }
+
+ QassandChecker* state() { return reinterpret_cast(contractStates[QASSAND_CONTRACT_INDEX]); }
+
+ uint64 balanceOf(const id& account) const { return static_cast(getBalance(account)); }
+ uint64 balanceQassand() const { return balanceOf(QASSAND_CONTRACT_ID); }
+ void fund(const id& account, uint64 amount) { increaseEnergy(account, amount); }
+
+ QASSAND::Ping_output ping(const id& invocator, uint64 amount)
+ {
+ QASSAND::Ping_input input{};
+ QASSAND::Ping_output output{};
+ invokeUserProcedure(QASSAND_CONTRACT_INDEX, QASSAND_PROC_PING, input, output, invocator, amount);
+ return output;
+ }
+
+ QASSAND::GetInfo_output getInfo() const
+ {
+ QASSAND::GetInfo_input input{};
+ QASSAND::GetInfo_output output{};
+ callFunction(QASSAND_CONTRACT_INDEX, QASSAND_FUNC_GET_INFO, input, output);
+ return output;
+ }
+
+ QASSAND::GetFeeInfo_output getFeeInfo() const
+ {
+ QASSAND::GetFeeInfo_input input{};
+ QASSAND::GetFeeInfo_output output{};
+ callFunction(QASSAND_CONTRACT_INDEX, QASSAND_FUNC_GET_FEE_INFO, input, output);
+ return output;
+ }
+
+ QASSAND::GetBurnInfo_output getBurnInfo() const
+ {
+ QASSAND::GetBurnInfo_input input{};
+ QASSAND::GetBurnInfo_output output{};
+ callFunction(QASSAND_CONTRACT_INDEX, QASSAND_FUNC_GET_BURN_INFO, input, output);
+ return output;
+ }
+
+ QASSAND::GetLaneInfo_output getLaneInfo(uint16 laneId) const
+ {
+ QASSAND::GetLaneInfo_input input{laneId};
+ QASSAND::GetLaneInfo_output output{};
+ callFunction(QASSAND_CONTRACT_INDEX, QASSAND_FUNC_GET_LANE_INFO, input, output);
+ return output;
+ }
+
+ void endTick() { callSystemProcedure(QASSAND_CONTRACT_INDEX, END_TICK); }
+};
+
+static bool arrayStartsWith(const Array& value, const char* expected)
+{
+ for (uint16 i = 0; expected[i] != 0; ++i)
+ {
+ if (value.get(i) != static_cast(expected[i]))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+TEST(ContractQassand, InitializeSetsPingV0Metadata)
+{
+ ContractTestingQassand qassand;
+ QassandChecker* state = qassand.state();
+
+ EXPECT_EQ(static_cast(state->versionValue()), static_cast(QASSAND_VERSION));
+ EXPECT_EQ(static_cast(state->constructionEpochValue()), static_cast(QASSAND_CONSTRUCTION_EPOCH_PLACEHOLDER));
+ EXPECT_EQ(state->totalPingCountValue(), 0ull);
+ EXPECT_EQ(state->protocolEarnedFeeValue(), 0ull);
+ EXPECT_EQ(state->burnEarnedFeeValue(), 0ull);
+ EXPECT_EQ(state->pendingBurnAmountValue(), 0ull);
+ EXPECT_EQ(state->totalBurnedAmountValue(), 0ull);
+}
+
+TEST(ContractQassand, ReadsMetadataAndFeeState)
+{
+ ContractTestingQassand qassand;
+
+ const QASSAND::GetInfo_output info = qassand.getInfo();
+ EXPECT_TRUE(arrayStartsWith(info.protocolName, "Qassandra"));
+ EXPECT_EQ(static_cast(info.version), static_cast(QASSAND_VERSION));
+ EXPECT_EQ(static_cast(info.constructionEpoch), static_cast(QASSAND_CONSTRUCTION_EPOCH_PLACEHOLDER));
+ EXPECT_EQ(info.totalPingCount, 0ull);
+
+ const QASSAND::GetFeeInfo_output fees = qassand.getFeeInfo();
+ EXPECT_EQ(fees.pingFee, QASSAND_PING_FEE);
+ EXPECT_EQ(fees.protocolFee, QASSAND_PROTOCOL_FEE);
+ EXPECT_EQ(fees.burnFee, QASSAND_BURN_FEE);
+ EXPECT_EQ(fees.protocolEarnedFee, 0ull);
+ EXPECT_EQ(fees.burnEarnedFee, 0ull);
+
+ const QASSAND::GetBurnInfo_output burn = qassand.getBurnInfo();
+ EXPECT_EQ(burn.pendingBurnAmount, 0ull);
+ EXPECT_EQ(burn.totalBurnedAmount, 0ull);
+}
+
+TEST(ContractQassand, ReadsLaneTaxonomy)
+{
+ ContractTestingQassand qassand;
+
+ const QASSAND::GetLaneInfo_output forecastingLane = qassand.getLaneInfo(QASSAND_LANE_FORECASTING);
+ EXPECT_EQ(static_cast(forecastingLane.returnCode), static_cast(QASSAND_SUCCESS));
+ EXPECT_EQ(static_cast(forecastingLane.laneId), static_cast(QASSAND_LANE_FORECASTING));
+ EXPECT_TRUE(arrayStartsWith(forecastingLane.laneName, "Forecasting"));
+ EXPECT_EQ(forecastingLane.requiredFee, 0ull);
+
+ const QASSAND::GetLaneInfo_output stableLane = qassand.getLaneInfo(QASSAND_LANE_STABLE_OPERATIONS);
+ EXPECT_EQ(static_cast(stableLane.returnCode), static_cast(QASSAND_SUCCESS));
+ EXPECT_EQ(static_cast(stableLane.laneId), static_cast(QASSAND_LANE_STABLE_OPERATIONS));
+ EXPECT_TRUE(arrayStartsWith(stableLane.laneName, "StableOps"));
+ EXPECT_EQ(stableLane.requiredFee, 0ull);
+
+ const QASSAND::GetLaneInfo_output attestationLane = qassand.getLaneInfo(QASSAND_LANE_DATA_ATTESTATION);
+ EXPECT_EQ(static_cast(attestationLane.returnCode), static_cast(QASSAND_SUCCESS));
+ EXPECT_EQ(static_cast(attestationLane.laneId), static_cast(QASSAND_LANE_DATA_ATTESTATION));
+ EXPECT_TRUE(arrayStartsWith(attestationLane.laneName, "Data"));
+ EXPECT_EQ(attestationLane.requiredFee, 0ull);
+
+ const QASSAND::GetLaneInfo_output unknownLane = qassand.getLaneInfo(QASSAND_LANE_UNKNOWN);
+ EXPECT_EQ(static_cast(unknownLane.returnCode), static_cast(QASSAND_UNKNOWN_LANE));
+}
+
+TEST(ContractQassand, UnderpaymentRefundsAndDoesNotAccountFee)
+{
+ ContractTestingQassand qassand;
+ const id user = id::randomValue();
+ qassand.fund(user, QASSAND_PING_FEE);
+
+ const uint64 underpaidAmount = QASSAND_PING_FEE - 1;
+ const QASSAND::Ping_output underpaid = qassand.ping(user, underpaidAmount);
+ EXPECT_EQ(static_cast(underpaid.returnCode), static_cast(QASSAND_UNDERPAID));
+ EXPECT_EQ(underpaid.refundedAmount, underpaidAmount);
+ EXPECT_EQ(qassand.balanceOf(user), QASSAND_PING_FEE);
+ EXPECT_EQ(qassand.balanceQassand(), 0ull);
+ EXPECT_EQ(qassand.state()->totalPingCountValue(), 0ull);
+ EXPECT_EQ(qassand.state()->protocolEarnedFeeValue(), 0ull);
+ EXPECT_EQ(qassand.state()->burnEarnedFeeValue(), 0ull);
+ EXPECT_EQ(qassand.state()->pendingBurnAmountValue(), 0ull);
+}
+
+TEST(ContractQassand, ExactFeeAccountsProtocolAndDeferredBurn)
+{
+ ContractTestingQassand qassand;
+ const id user = id::randomValue();
+ qassand.fund(user, QASSAND_PING_FEE);
+
+ const QASSAND::Ping_output ping = qassand.ping(user, QASSAND_PING_FEE);
+ EXPECT_EQ(static_cast(ping.returnCode), static_cast(QASSAND_SUCCESS));
+ EXPECT_EQ(ping.acceptedFee, QASSAND_PING_FEE);
+ EXPECT_EQ(ping.refundedAmount, 0ull);
+ EXPECT_EQ(ping.protocolEarnedFee, QASSAND_PROTOCOL_FEE);
+ EXPECT_EQ(ping.burnEarnedFee, QASSAND_BURN_FEE);
+ EXPECT_EQ(ping.totalPingCount, 1ull);
+
+ EXPECT_EQ(qassand.balanceOf(user), 0ull);
+ EXPECT_EQ(qassand.balanceQassand(), QASSAND_PING_FEE);
+ EXPECT_EQ(qassand.state()->totalPingCountValue(), 1ull);
+ EXPECT_EQ(qassand.state()->protocolEarnedFeeValue(), QASSAND_PROTOCOL_FEE);
+ EXPECT_EQ(qassand.state()->burnEarnedFeeValue(), QASSAND_BURN_FEE);
+ EXPECT_EQ(qassand.state()->pendingBurnAmountValue(), QASSAND_BURN_FEE);
+}
+
+TEST(ContractQassand, ExcessFeeRefundsOnlyOverage)
+{
+ ContractTestingQassand qassand;
+ const id user = id::randomValue();
+ const uint64 paidAmount = QASSAND_PING_FEE + 12345;
+ qassand.fund(user, paidAmount);
+
+ const QASSAND::Ping_output ping = qassand.ping(user, paidAmount);
+ EXPECT_EQ(static_cast(ping.returnCode), static_cast(QASSAND_SUCCESS));
+ EXPECT_EQ(ping.acceptedFee, QASSAND_PING_FEE);
+ EXPECT_EQ(ping.refundedAmount, 12345ull);
+ EXPECT_EQ(qassand.balanceOf(user), 12345ull);
+ EXPECT_EQ(qassand.balanceQassand(), QASSAND_PING_FEE);
+ EXPECT_EQ(qassand.state()->totalPingCountValue(), 1ull);
+ EXPECT_EQ(qassand.state()->pendingBurnAmountValue(), QASSAND_BURN_FEE);
+}
+
+TEST(ContractQassand, EndTickBurnsDeferredAmount)
+{
+ ContractTestingQassand qassand;
+ const id user = id::randomValue();
+ qassand.fund(user, QASSAND_PING_FEE);
+
+ qassand.ping(user, QASSAND_PING_FEE);
+ EXPECT_EQ(qassand.state()->pendingBurnAmountValue(), QASSAND_BURN_FEE);
+ EXPECT_EQ(qassand.state()->totalBurnedAmountValue(), 0ull);
+
+ qassand.endTick();
+ EXPECT_EQ(qassand.state()->pendingBurnAmountValue(), 0ull);
+ EXPECT_EQ(qassand.state()->totalBurnedAmountValue(), QASSAND_BURN_FEE);
+ EXPECT_EQ(qassand.balanceQassand(), QASSAND_PROTOCOL_FEE);
+}
diff --git a/test/test.vcxproj b/test/test.vcxproj
index 2372e73de..c9d15ba7e 100644
--- a/test/test.vcxproj
+++ b/test/test.vcxproj
@@ -148,6 +148,7 @@
+
diff --git a/test/test.vcxproj.filters b/test/test.vcxproj.filters
index 8eebbc6fb..683c43bb8 100644
--- a/test/test.vcxproj.filters
+++ b/test/test.vcxproj.filters
@@ -22,6 +22,7 @@
+