Smart Contract¶
In this section we will look at the technical details of how the
eosio.forum
smart contract is implemented. This contract can be
roughly divided into four parts: proposal, vote, status and post.
All the actions and tables live in the same
class forum
.
Proposal¶
The proposal part contains a table proposals
and a few
actions to operate a proposal.
-
class
forum
¶ -
ACTION
propose
(eosio::name proposer, eosio::name proposal_name, string title, string proposal_json, eosio::time_point_sec expires_at)¶
[source]
-
TABLE
proposals
¶
[source] eosio::name proposal_name; // primary key eosio::name proposer; // secondary key string title; string proposal_json; eosio::time_point_sec created_at; eosio::time_point_sec expires_at;
Any account can execute the
propose()
action to create a new proposal. After some necessary parameter checks, a new proposal will be saved in tableproposals
with RAM charged to theproposer
. Each action parameter corresponds to a column in the table:proposer
is the account who created the proposal;proposal_name
is the primary key for tableproposals
and used as the unique identifier for a proposal;title
is a string for the proposal title and should be less than 1024 characters;proposal_json
is a JSON string for the proposal description and should comply with Proposal JSON Structure Guidelines;expires_at
defines the deadline for the voting period, which needs to be a time point between the action execution time and the next 6 months.
Once a proposal is created, any account (including the
proposer
itself) can vote on it via thevote()
action, as long as the current time is beforeexpires_at
.
-
ACTION
expire
(eosio::name proposal_name)¶
[source] This action allows the
proposer
to manually expire his/her proposal and end the voting immediately. This is done by modifying theexpires_at
field to the current time.53 54 55
proposal_table.modify(itr, proposer, [&](auto& row) { row.expires_at = current_time_point_sec(); });
The action
expire()
can only be called by the originalproposer
. Calling it on a non-existant or already expired proposal will return an error.
-
ACTION
clnproposal
(eosio::name proposal_name, uint64_t max_count)¶
[source] It’s possible to clean a proposal if it has expired and its freeze period of 3 days (set by
FREEZE_PERIOD_IN_SECONDS
) has fully elapsed.99
bool can_be_cleaned_up() const { return current_time_point_sec() > (expires_at + FREEZE_PERIOD_IN_SECONDS); }
The action
clnproposal()
will clean up all votes related to a proposal. It works iteratively by removing as many asmax_count
votes, and can be executed multiple times until all votes are removed.119 120 121 122 123 124 125 126 127 128 129 130 131
auto index = vote_table.template get_index<"byproposal"_n>(); auto vote_key_lower_bound = compute_by_proposal_key(proposal_name, name(0x0000000000000000)); auto vote_key_upper_bound = compute_by_proposal_key(proposal_name, name(0xFFFFFFFFFFFFFFFF)); auto lower_itr = index.lower_bound(vote_key_lower_bound); auto upper_itr = index.upper_bound(vote_key_upper_bound); uint64_t count = 0; while (count < max_count && lower_itr != upper_itr) { lower_itr = index.erase(lower_itr); count++; }
Notice that the secondary index
byproposal
is used to query and iterate over all votes of a givenproposal_name
(see tablevote
). Once there are no more associated votes, the proposal itself will be deleted.134 135 136
if (lower_itr == upper_itr && itr != proposal_table.end()) { proposal_table.erase(itr); }
This effectively clears all the RAM consumed for a proposal and all its votes. It’s safe to allow anybody to call
clnproposal()
since the action will only accept an expired proposal that has passed the freeze period, which means it has terminated its lifecycle. Voters, proposers, or any community member is invited to callclnproposal()
to clean the RAM related to a proposal.
-
ACTION
Vote¶
The vote part contains a table vote
as well as vote()
and unvote()
actions.
-
class
forum
-
-
TABLE
vote
uint64_t id; // primary key eosio::name proposal_name; // secondary key eosio::name voter; // secondary key uint8_t vote; string vote_json; eosio::time_point_sec updated_at;
For a non-expired proposal, any accounts can use the
vote()
action to publish a vote. It will consume a little bit of RAM from the voter (430 bytes) to save the vote info in tablevote
.The meaning of the vote is represented by the
vote
field.0
means no1
means yes255
means abstainOther values can be used to represent other meanings
In table
vote
, the primary keyid
is generated automatically. Secondary keys are created for fieldsproposal_name
andvoter
to support searching by proposal or voter. The fieldvote_json
is designed to provide extra information for a vote, such as a comment explaining the thought behind the vote.The voter can execute the
vote()
action again to change his/her vote, or call theunvote()
action to delete his/her vote in tablevote
. Removing the current active vote reclaims the stored RAM of the vote. Of course, the vote will not count anymore.The
vote()
andunvote()
actions will first check whether the proposal is still active, and refuse the execution if the proposal is expired.98
bool is_expired() const { return current_time_point_sec() >= expires_at; }
Therefore, it is guaranteed that the vote statistics for a proposal will be fixed once the proposal is expired, so that people will be able to count the votes and compute the voting result.
-
TABLE
Status¶
-
class
forum
-
-
TABLE
status
// scope is self eosio::name account; // primary key string content; eosio::time_point_sec updated_at;
The action
status()
will record a status for the associatedaccount
. If thecontent
is empty, the action will remove a previous status. Otherwise, it will add a status entry or modify the existing entry for theaccount
using thecontent
received.
-
TABLE
Todo
How this is used?