Skip to content

Commit ca072f5

Browse files
Merge pull request #612 from qubic/develop (Release v1.266.0)
Release v1.266.0
2 parents e8c43c3 + bb58800 commit ca072f5

40 files changed

+6325
-683
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,8 @@ We cannot support you in any case. You are welcome to provide updates, bug fixes
162162

163163
## More Documentation
164164
- [How to contribute](doc/contributing.md)
165-
- [Developing a smart contract ](doc/contracts.md)
165+
- [Developing a smart contract](doc/contracts.md)
166166
- [Qubic protocol](doc/protocol.md)
167167
- [Custom mining](doc/custom_mining.md)
168168
- [Seamless epoch transition](SEAMLESS.md)
169+
- [Proposals and voting](doc/contracts_proposals.md)

doc/contracts.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,9 +243,12 @@ They are defined with the following macros:
243243
8. `POST_RELEASE_SHARES()`: Called after asset management rights were transferred from this contract to another contract that called `qpi.acquireShare()`
244244
9. `POST_ACQUIRE_SHARES()`: Called after asset management rights were transferred to this contract from another contract that called `qpi.releaseShare()`.
245245
10. `POST_INCOMING_TRANSFER()`: Called after QUs have been transferred to this contract. [More details...](#callback-post_incoming_transfer)
246+
11. `SET_SHAREHOLDER_PROPOSAL()`: Called if another contracts tries to set a shareholder proposal in this contract by calling `qpi.setShareholderProposal()`.
247+
12. `SET_SHAREHOLDER_VOTES()`: Called if another contracts tries to set a shareholder proposal in this contract by calling `qpi.setShareholderVotes()`.
246248

247249
System procedures 1 to 5 have no input and output.
248250
The input and output of system procedures 6 to 9 are discussed in the section about [management rights transfer](#management-rights-transfer).
251+
The system procedure 11 and 12 are discussed in the section about [contracts as shareholder of other contracts](contracts_proposals.md#contracts-as-shareholders-of-other-contracts)
249252

250253
The contract state is passed to each of the procedures as a reference named `state`.
251254
And it can be modified (in contrast to contract functions).
@@ -295,6 +298,10 @@ QX rejects all attempts (`qpi.acquireShares()`) of other contracts to acquire ri
295298

296299
TODO
297300

301+
In the universe, NULL_ID is only used for owner / possessor for temporary entries during the IPO between issuing a contract asset and transferring the ownership/possession.
302+
Further, NULL_ID is used for burning asset shares by transferring ownership / possession to NULL_ID.
303+
304+
298305
### Management rights transfer
299306

300307
There are two ways of transferring asset management rights:
@@ -546,6 +553,16 @@ That is, calls to these QPI procedures will fail to prevent nested callbacks.
546553
If you invoke a user procedure from the callback, the fee / invocation reward cannot be transferred.
547554
In consequence, the procedure is executed but with `qpi.invocationReward() == 0`.
548555

556+
### Proposals and voting
557+
558+
Proposals and voting are the on-chain way of decision-making, implemented in contracts.
559+
The function, macros, and data structures provided by the QPI for implementing proposal voting in smart contracts are quite complex.
560+
That is why they are described in a [separate document](contracts_proposals.md).
561+
562+
CFB has an alternative idea for proposal-free voting on shareholder variables that is supposed to be used in the contracts QX, RANDOM, and MLM.
563+
It has not been implemented yet.
564+
https://github.com/qubic/core/issues/574
565+
549566

550567
## Restrictions of C++ Language Features
551568

doc/contracts_proposals.md

Lines changed: 702 additions & 0 deletions
Large diffs are not rendered by default.

src/Qubic.vcxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
<ClInclude Include="contracts\Qdraw.h" />
2727
<ClInclude Include="contracts\Qswap.h" />
2828
<ClInclude Include="contracts\RandomLottery.h" />
29+
<ClInclude Include="contracts\RandomLottery_v1.h" />
2930
<ClInclude Include="contracts\SupplyWatcher.h" />
3031
<ClInclude Include="contracts\EmptyTemplate.h" />
3132
<ClInclude Include="contracts\GeneralQuorumProposal.h" />
@@ -50,6 +51,7 @@
5051
<ClInclude Include="contract_core\contract_def.h" />
5152
<ClInclude Include="contract_core\contract_exec.h" />
5253
<ClInclude Include="contract_core\ipo.h" />
54+
<ClInclude Include="contract_core\pre_qpi_def.h" />
5355
<ClInclude Include="contract_core\qpi_asset_impl.h" />
5456
<ClInclude Include="contract_core\qpi_collection_impl.h" />
5557
<ClInclude Include="contract_core\qpi_ipo_impl.h" />

src/Qubic.vcxproj.filters

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,12 @@
285285
<ClInclude Include="contracts\RandomLottery.h">
286286
<Filter>contracts</Filter>
287287
</ClInclude>
288+
<ClInclude Include="contract_core\pre_qpi_def.h">
289+
<Filter>contract_core</Filter>
290+
</ClInclude>
291+
<ClInclude Include="contracts\RandomLottery_v1.h">
292+
<Filter>contracts</Filter>
293+
</ClInclude>
288294
</ItemGroup>
289295
<ItemGroup>
290296
<Filter Include="platform">

src/assets/assets.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ struct AssetStorage
112112
PROFILE_SCOPE();
113113

114114
reset();
115-
for (int index = 0; index < ASSETS_CAPACITY; index++)
115+
for (int index = ASSETS_CAPACITY - 1; index >= 0; index--)
116116
{
117117
switch (assets[index].varStruct.issuance.type)
118118
{

src/common_buffers.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "platform/global_var.h"
44
#include "platform/memory_util.h"
5+
#include "platform/assert.h"
56

67
#include "network_messages/entity.h"
78
#include "network_messages/assets.h"
@@ -35,7 +36,10 @@ static void deinitCommonBuffers()
3536
}
3637
}
3738

38-
static void* __scratchpad()
39+
static void* __scratchpad(unsigned long long sizeToMemsetZero)
3940
{
41+
ASSERT(sizeToMemsetZero <= reorgBufferSize);
42+
if (sizeToMemsetZero)
43+
setMem(reorgBuffer, sizeToMemsetZero, 0);
4044
return reorgBuffer;
4145
}

src/contract_core/contract_def.h

Lines changed: 16 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
#pragma once
2-
#include "network_messages/common_def.h"
3-
#include "platform/m256.h"
42

53
////////// Smart contracts \\\\\\\\\\
64
@@ -10,61 +8,10 @@
108
// Additionally, most types, functions, and variables of the core have to be defined after including
119
// the contract to keep them unavailable in the contract code.
1210

13-
namespace QPI
14-
{
15-
struct QpiContextProcedureCall;
16-
struct QpiContextFunctionCall;
17-
}
18-
19-
// TODO: add option for having locals to SYSTEM and EXPAND procedures
20-
typedef void (*SYSTEM_PROCEDURE)(const QPI::QpiContextProcedureCall&, void* state, void* input, void* output, void* locals);
21-
typedef void (*EXPAND_PROCEDURE)(const QPI::QpiContextFunctionCall&, void*, void*); // cannot not change anything except state
22-
typedef void (*USER_FUNCTION)(const QPI::QpiContextFunctionCall&, void* state, void* input, void* output, void* locals);
23-
typedef void (*USER_PROCEDURE)(const QPI::QpiContextProcedureCall&, void* state, void* input, void* output, void* locals);
24-
25-
constexpr unsigned long long MAX_CONTRACT_STATE_SIZE = 1073741824;
26-
27-
// Maximum size of local variables that may be used by a contract function or procedure
28-
// If increased, the size of contractLocalsStack should be increased as well.
29-
constexpr unsigned int MAX_SIZE_OF_CONTRACT_LOCALS = 32 * 1024;
30-
31-
// TODO: make sure the limit of nested calls is not violated
32-
constexpr unsigned short MAX_NESTED_CONTRACT_CALLS = 10;
33-
34-
// Size of the contract action tracker, limits the number of transfers that one contract call can execute.
35-
constexpr unsigned long long CONTRACT_ACTION_TRACKER_SIZE = 16 * 1024 * 1024;
36-
37-
38-
static void __beginFunctionOrProcedure(const unsigned int); // TODO: more human-readable form of function ID?
39-
static void __endFunctionOrProcedure(const unsigned int);
40-
template <typename T> static m256i __K12(T);
41-
template <typename T> static void __logContractDebugMessage(unsigned int, T&);
42-
template <typename T> static void __logContractErrorMessage(unsigned int, T&);
43-
template <typename T> static void __logContractInfoMessage(unsigned int, T&);
44-
template <typename T> static void __logContractWarningMessage(unsigned int, T&);
45-
static void* __scratchpad(); // TODO: concurrency support (n buffers for n allowed concurrent contract executions)
46-
// static void* __tryAcquireScratchpad(unsigned int size); // Thread-safe, may return nullptr if no appropriate buffer is available
47-
// static void __ReleaseScratchpad(void*);
48-
49-
template <unsigned int functionOrProcedureId>
50-
struct __FunctionOrProcedureBeginEndGuard
51-
{
52-
// Constructor calling __beginFunctionOrProcedure()
53-
__FunctionOrProcedureBeginEndGuard()
54-
{
55-
__beginFunctionOrProcedure(functionOrProcedureId);
56-
}
57-
58-
// Destructor making sure __endFunctionOrProcedure() is called for every return path
59-
~__FunctionOrProcedureBeginEndGuard()
60-
{
61-
__endFunctionOrProcedure(functionOrProcedureId);
62-
}
63-
};
64-
6511

6612
// With no other includes before, the following are the only headers available to contracts.
6713
// When adding something, be cautious to keep access of contracts limited to safe features only.
14+
#include "pre_qpi_def.h"
6815
#include "contracts/qpi.h"
6916
#include "qpi_proposal_voting.h"
7017

@@ -222,7 +169,11 @@ struct __FunctionOrProcedureBeginEndGuard
222169
#define CONTRACT_INDEX RL_CONTRACT_INDEX
223170
#define CONTRACT_STATE_TYPE RL
224171
#define CONTRACT_STATE2_TYPE RL2
225-
#include "contracts/RandomLottery.h"
172+
#ifdef RL_V1
173+
#include "contracts/RandomLottery_v1.h"
174+
#else
175+
#include "contracts/RandomLottery.h"
176+
#endif
226177

227178
#undef CONTRACT_INDEX
228179
#undef CONTRACT_STATE_TYPE
@@ -283,6 +234,8 @@ constexpr unsigned short TESTEXD_CONTRACT_INDEX = (CONTRACT_INDEX + 1);
283234
#undef POST_RELEASE_SHARES
284235
#undef POST_ACQUIRE_SHARES
285236
#undef POST_INCOMING_TRANSFER
237+
#undef SET_SHAREHOLDER_PROPOSAL
238+
#undef SET_SHAREHOLDER_VOTES
286239

287240

288241
// The following are included after the contracts to keep their definitions and dependencies
@@ -336,8 +289,8 @@ constexpr struct ContractDescription
336289
{"QBOND", 182, 10000, sizeof(QBOND)}, // proposal in epoch 180, IPO in 181, construction and first use in 182
337290
// new contracts should be added above this line
338291
#ifdef INCLUDE_CONTRACT_TEST_EXAMPLES
339-
{"TESTEXA", 138, 10000, sizeof(IPO)},
340-
{"TESTEXB", 138, 10000, sizeof(IPO)},
292+
{"TESTEXA", 138, 10000, sizeof(TESTEXA)},
293+
{"TESTEXB", 138, 10000, sizeof(TESTEXB)},
341294
{"TESTEXC", 138, 10000, sizeof(IPO)},
342295
{"TESTEXD", 155, 10000, sizeof(IPO)},
343296
#endif
@@ -377,6 +330,8 @@ enum SystemProcedureID
377330
POST_RELEASE_SHARES,
378331
POST_ACQUIRE_SHARES,
379332
POST_INCOMING_TRANSFER,
333+
SET_SHAREHOLDER_PROPOSAL,
334+
SET_SHAREHOLDER_VOTES,
380335
contractSystemProcedureCount,
381336
};
382337

@@ -414,6 +369,10 @@ if (!contractName::__postReleaseSharesEmpty) contractSystemProcedures[contractIn
414369
contractSystemProcedureLocalsSizes[contractIndex][POST_RELEASE_SHARES] = contractName::__postReleaseSharesLocalsSize; \
415370
if (!contractName::__postIncomingTransferEmpty) contractSystemProcedures[contractIndex][POST_INCOMING_TRANSFER] = (SYSTEM_PROCEDURE)contractName::__postIncomingTransfer;\
416371
contractSystemProcedureLocalsSizes[contractIndex][POST_INCOMING_TRANSFER] = contractName::__postIncomingTransferLocalsSize; \
372+
if (!contractName::__setShareholderProposalEmpty) contractSystemProcedures[contractIndex][SET_SHAREHOLDER_PROPOSAL] = (SYSTEM_PROCEDURE)contractName::__setShareholderProposal;\
373+
contractSystemProcedureLocalsSizes[contractIndex][SET_SHAREHOLDER_PROPOSAL] = contractName::__setShareholderProposalLocalsSize; \
374+
if (!contractName::__setShareholderVotesEmpty) contractSystemProcedures[contractIndex][SET_SHAREHOLDER_VOTES] = (SYSTEM_PROCEDURE)contractName::__setShareholderVotes;\
375+
contractSystemProcedureLocalsSizes[contractIndex][SET_SHAREHOLDER_VOTES] = contractName::__setShareholderVotesLocalsSize; \
417376
if (!contractName::__expandEmpty) contractExpandProcedures[contractIndex] = (EXPAND_PROCEDURE)contractName::__expand;\
418377
QpiContextForInit qpi(contractIndex); \
419378
contractName::__registerUserFunctionsAndProcedures(qpi); \

src/contract_core/contract_exec.h

Lines changed: 81 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ enum ContractCallbacksRunningFlags
7272
NoContractCallback = 0,
7373
ContractCallbackManagementRightsTransfer = 1,
7474
ContractCallbackPostIncomingTransfer = 2,
75+
ContractCallbackShareholderProposalAndVoting = 4,
7576
};
7677

7778

@@ -609,7 +610,7 @@ void QPI::QpiContextProcedureCall::__qpiReleaseStateForWriting(unsigned int cont
609610

610611
// Used to run a special system procedure from within a contract for example in asset management rights transfer
611612
template <unsigned int sysProcId, typename InputType, typename OutputType>
612-
void QPI::QpiContextProcedureCall::__qpiCallSystemProc(unsigned int sysProcContractIndex, InputType& input, OutputType& output, QPI::sint64 invocationReward) const
613+
bool QPI::QpiContextProcedureCall::__qpiCallSystemProc(unsigned int sysProcContractIndex, InputType& input, OutputType& output, QPI::sint64 invocationReward) const
613614
{
614615
// Make sure this function is used with an expected combination of sysProcId, input,
615616
// and output
@@ -619,6 +620,8 @@ void QPI::QpiContextProcedureCall::__qpiCallSystemProc(unsigned int sysProcContr
619620
|| (sysProcId == POST_RELEASE_SHARES && sizeof(InputType) == sizeof(QPI::PostManagementRightsTransfer_input) && sizeof(OutputType) == sizeof(QPI::NoData))
620621
|| (sysProcId == POST_ACQUIRE_SHARES && sizeof(InputType) == sizeof(QPI::PostManagementRightsTransfer_input) && sizeof(OutputType) == sizeof(QPI::NoData))
621622
|| (sysProcId == POST_INCOMING_TRANSFER && sizeof(InputType) == sizeof(QPI::PostIncomingTransfer_input) && sizeof(OutputType) == sizeof(QPI::NoData))
623+
|| (sysProcId == SET_SHAREHOLDER_PROPOSAL && sizeof(InputType) == sizeof(QPI::SET_SHAREHOLDER_PROPOSAL_input) && sizeof(OutputType) == sizeof(QPI::SET_SHAREHOLDER_PROPOSAL_output))
624+
|| (sysProcId == SET_SHAREHOLDER_VOTES && sizeof(InputType) == sizeof(QPI::SET_SHAREHOLDER_VOTES_input) && sizeof(OutputType) == sizeof(QPI::SET_SHAREHOLDER_VOTES_output))
622625
, "Unsupported __qpiCallSystemProc() call"
623626
);
624627

@@ -627,7 +630,8 @@ void QPI::QpiContextProcedureCall::__qpiCallSystemProc(unsigned int sysProcContr
627630
ASSERT(sysProcContractIndex < contractCount);
628631
ASSERT(contractStates[sysProcContractIndex] != nullptr);
629632
if (sysProcId == PRE_RELEASE_SHARES || sysProcId == PRE_ACQUIRE_SHARES
630-
|| sysProcId == POST_RELEASE_SHARES || sysProcId == POST_ACQUIRE_SHARES)
633+
|| sysProcId == POST_RELEASE_SHARES || sysProcId == POST_ACQUIRE_SHARES
634+
|| sysProcId == SET_SHAREHOLDER_PROPOSAL || sysProcId == SET_SHAREHOLDER_VOTES)
631635
{
632636
ASSERT(sysProcContractIndex != _currentContractIndex);
633637
}
@@ -637,14 +641,22 @@ void QPI::QpiContextProcedureCall::__qpiCallSystemProc(unsigned int sysProcContr
637641

638642
// Empty procedures lead to null pointer in contractSystemProcedures -> return default output (all zero/false)
639643
if (!contractSystemProcedures[sysProcContractIndex][sysProcId])
640-
return;
644+
{
645+
// Returning false informs the caller that the system procedure isn't defined, which is useful if the
646+
// zeroed output does not indicate an error but is a valid output value.
647+
return false;
648+
}
641649

642650
// Set flags of callbacks currently running (to prevent deadlocks and nested calling of QPI functions)
643651
auto contractCallbacksRunningBefore = contractCallbacksRunning;
644652
if (sysProcId == POST_INCOMING_TRANSFER)
645653
{
646654
contractCallbacksRunning |= ContractCallbackPostIncomingTransfer;
647655
}
656+
else if (sysProcId == SET_SHAREHOLDER_PROPOSAL || sysProcId == SET_SHAREHOLDER_VOTES)
657+
{
658+
contractCallbacksRunning |= ContractCallbackShareholderProposalAndVoting;
659+
}
648660
else if (sysProcId == PRE_RELEASE_SHARES || sysProcId == PRE_ACQUIRE_SHARES
649661
|| sysProcId == POST_RELEASE_SHARES || sysProcId == POST_ACQUIRE_SHARES)
650662
{
@@ -676,6 +688,8 @@ void QPI::QpiContextProcedureCall::__qpiCallSystemProc(unsigned int sysProcContr
676688

677689
// Restore flags of callbacks currently running
678690
contractCallbacksRunning = contractCallbacksRunningBefore;
691+
692+
return true;
679693
}
680694

681695
// If dest is a contract, notify contract by running system procedure POST_INCOMING_TRANSFER
@@ -693,6 +707,70 @@ void QPI::QpiContextProcedureCall::__qpiNotifyPostIncomingTransfer(const QPI::id
693707
__qpiCallSystemProc<POST_INCOMING_TRANSFER>(destContractIndex, input, output, 0);
694708
}
695709

710+
inline uint16 QPI::QpiContextProcedureCall::setShareholderProposal(
711+
uint16 contractIndex,
712+
const Array<uint8, 1024>& proposalDataBuffer,
713+
sint64 invocationReward
714+
) const
715+
{
716+
// prevent nested calling from callbacks
717+
if (contractCallbacksRunning & ContractCallbackShareholderProposalAndVoting)
718+
{
719+
return INVALID_PROPOSAL_INDEX;
720+
}
721+
722+
// check for invalid inputs
723+
if (contractIndex == _currentContractIndex
724+
|| contractIndex == 0
725+
|| contractIndex >= contractCount
726+
|| invocationReward < 0)
727+
{
728+
return INVALID_PROPOSAL_INDEX;
729+
}
730+
731+
// Copy proposalDataBuffer, because procedures are allowed to change their input
732+
Array<uint8, 1024> inputBuffer = proposalDataBuffer;
733+
734+
// run SET_SHAREHOLDER_PROPOSAL callback in other contract
735+
uint16 outputProposalIndex;
736+
if (!__qpiCallSystemProc<SET_SHAREHOLDER_PROPOSAL>(contractIndex, inputBuffer, outputProposalIndex, invocationReward))
737+
return INVALID_PROPOSAL_INDEX;
738+
739+
return outputProposalIndex;
740+
}
741+
742+
inline bool QPI::QpiContextProcedureCall::setShareholderVotes(
743+
uint16 contractIndex,
744+
const ProposalMultiVoteDataV1& shareholderVoteData,
745+
sint64 invocationReward
746+
) const
747+
{
748+
// prevent nested calling from callbacks
749+
if (contractCallbacksRunning & ContractCallbackShareholderProposalAndVoting)
750+
{
751+
return false;
752+
}
753+
754+
// check for invalid inputs
755+
if (contractIndex == _currentContractIndex
756+
|| contractIndex == 0
757+
|| contractIndex >= contractCount
758+
|| invocationReward < 0)
759+
{
760+
return false;
761+
}
762+
763+
// Copy proposalDataBuffer, because procedures are allowed to change their input
764+
ProposalMultiVoteDataV1 inputVote = shareholderVoteData;
765+
766+
// run SET_SHAREHOLDER_VOTES callback in other contract
767+
bit success; // initialized with zero by __qpiCallSystemProc
768+
__qpiCallSystemProc<SET_SHAREHOLDER_VOTES>(contractIndex, inputVote, success, invocationReward);
769+
770+
return success;
771+
}
772+
773+
696774
// Enter endless loop leading to timeout
697775
// -> TODO: unlock everything in case of function entry point, maybe retry later in case of deadlock handling
698776
// -> TODO: rollback of contract actions on contractProcessor()

0 commit comments

Comments
 (0)