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 @@ +