Skip to content
Permalink
Browse files

Merge pull request #58 from boscore/feature/eosio.forum

Remove expires_at
  • Loading branch information...
Thaipanda committed Jun 11, 2019
2 parents d14415f + 4bba4f5 commit 4dc33425b59e5da4b1de2c02186ea7aa8e2a46d8
Showing with 11 additions and 134 deletions.
  1. +2 −31 contracts/eosio.forum/include/forum.hpp
  2. +9 −103 contracts/eosio.forum/src/forum.cpp
@@ -27,13 +27,9 @@ class [[eosio::contract("forum")]] forum : public eosio::contract {
const name proposer,
const name proposal_name,
const string& title,
const string& proposal_json,
const time_point_sec& expires_at
const string& proposal_json
);

[[eosio::action]]
void expire(const name proposal_name);

[[eosio::action]]
void vote(
const name voter,
@@ -45,9 +41,6 @@ class [[eosio::contract("forum")]] forum : public eosio::contract {
[[eosio::action]]
void unvote(const name voter, const name proposal_name);

[[eosio::action]]
void clnproposal(const name proposal_name, uint64_t max_count);

[[eosio::action]]
void post(
const name poster,
@@ -65,31 +58,13 @@ class [[eosio::contract("forum")]] forum : public eosio::contract {
[[eosio::action]]
void status(const name account, const string& content);

[[eosio::action]]
void extend(
const name proposer,
const name proposal_name,
const time_point_sec expires_at
);

[[eosio::action]]
void cancel(
const name proposer,
const name proposal_name,
const uint64_t max_count
const name proposal_name
);

private:
// 3 days in seconds (Computation: 3 days * 24 hours * 60 minutes * 60 seconds)
constexpr static uint32_t FREEZE_PERIOD_IN_SECONDS = 3 * 24 * 60 * 60;

// 6 months in seconds (Computatio: 6 months * average days per month * 24 hours * 60 minutes * 60 seconds)
constexpr static uint32_t SIX_MONTHS_IN_SECONDS = (uint32_t) (6 * (365.25 / 12) * 24 * 60 * 60);

static inline time_point_sec current_time_point_sec() {
return time_point_sec(current_time_point());
}

static uint128_t compute_by_proposal_key(const name proposal_name, const name voter) {
return ((uint128_t) proposal_name.value) << 64 | voter.value;
}
@@ -104,13 +79,9 @@ class [[eosio::contract("forum")]] forum : public eosio::contract {
string title;
string proposal_json;
time_point_sec created_at;
time_point_sec expires_at;

auto primary_key()const { return proposal_name.value; }
uint64_t by_proposer() const { return proposer.value; }

bool is_expired() const { return current_time_point_sec() >= expires_at; }
bool can_be_cleaned_up() const { return current_time_point_sec() > (expires_at + FREEZE_PERIOD_IN_SECONDS); }
};
typedef eosio::multi_index<
"proposal"_n, proposal_row,
@@ -12,21 +12,14 @@ void forum::propose(
const name proposer,
const name proposal_name,
const string& title,
const string& proposal_json,
const time_point_sec& expires_at
const string& proposal_json
) {
require_auth(proposer);

check(proposal_name.length() > 2, "proposal name should be at least 3 characters long.");
check(title.size() < 1024, "title should be less than 1024 characters long.");
VALIDATE_JSON(proposal_json, 32768);

check(expires_at > current_time_point_sec(), "expires_at must be a value in the future.");

// Not a perfect assertion since we are not doing real date computation, but good enough for our use case
time_point_sec max_expires_at = current_time_point_sec() + SIX_MONTHS_IN_SECONDS;
check(expires_at <= max_expires_at, "expires_at must be within 6 months from now.");

proposals proposal_table(_self, _self.value);
check(proposal_table.find(proposal_name.value) == proposal_table.end(), "proposal with same name already exists.");

@@ -35,23 +28,7 @@ void forum::propose(
row.proposer = proposer;
row.title = title;
row.proposal_json = proposal_json;
row.created_at = current_time_point_sec();
row.expires_at = expires_at;
});
}

void forum::expire(const name proposal_name) {
proposals proposal_table(_self, _self.value);
auto itr = proposal_table.find(proposal_name.value);

check(itr != proposal_table.end(), "proposal not found.");
check(!itr->is_expired(), "proposal is already expired.");

auto proposer = itr->proposer;
require_auth(proposer);

proposal_table.modify(itr, eosio::same_payer, [&](auto& row) {
row.expires_at = current_time_point_sec();
row.created_at = current_time_point();
});
}

@@ -66,8 +43,6 @@ void forum::vote(
proposals proposal_table(_self, _self.value);
auto& row = proposal_table.get(proposal_name.value, "proposal_name does not exist.");

check(!row.is_expired(), "cannot vote on an expired proposal.");

VALIDATE_JSON(vote_json, 8192);

votes vote_table(_self, _self.value);
@@ -83,10 +58,6 @@ void forum::unvote(const name voter, const name proposal_name) {
proposals proposal_table(_self, _self.value);
auto& row = proposal_table.get(proposal_name.value, "proposal_name does not exist.");

if (row.is_expired()) {
check(row.can_be_cleaned_up(), "cannot unvote on an expired proposal within its freeze period.");
}

votes vote_table(_self, _self.value);

auto index = vote_table.template get_index<"byproposal"_n>();
@@ -98,44 +69,6 @@ void forum::unvote(const name voter, const name proposal_name) {
vote_table.erase(*itr);
}

/**
* This method does **not** require any authorization, here is the reasoning for that.
*
* This method allows anyone to clean a proposal if the proposal is either expired or does
* not exist anymore. This exact case can only happen either by itself (the proposal has reached
* its expiration time) or by the a proposer action (`expire`). In either case, 3 days must elapse before calling `clnproposal`.
*
* In all cases it's ok to let anyone clean the votes since there is no more "use"
* for the proposal nor the votes.
*/
void forum::clnproposal(const name proposal_name, uint64_t max_count) {
proposals proposal_table(_self, _self.value);

auto itr = proposal_table.find(proposal_name.value);
check(itr == proposal_table.end() || itr->can_be_cleaned_up(),
"proposal must not exist or be expired for at least 3 days prior to running clnproposal.");

votes vote_table(_self, _self.value);
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++;
}

// Let's delete the actual proposal if we deleted all votes and the proposal still exists
if (lower_itr == upper_itr && itr != proposal_table.end()) {
proposal_table.erase(itr);
}
}

void forum::post(
const name poster,
const string& post_uuid,
@@ -188,37 +121,12 @@ void forum::status(const name account, const string& content) {
}
}

void forum::extend(
const name proposer,
const name proposal_name,
const time_point_sec expires_at
) {
require_auth(proposer);
check(expires_at > current_time_point_sec(), "expires_at must be a value in the future.");

// Not a perfect assertion since we are not doing real date computation, but good enough for our use case
time_point_sec max_expires_at = current_time_point_sec() + SIX_MONTHS_IN_SECONDS;
check(expires_at <= max_expires_at, "expires_at must be within 6 months from now.");

// Check if `proposal_name` already exists
proposals proposal_table(_self, _self.value);
auto proposal_itr = proposal_table.find( proposal_name.value );
check( proposal_itr != proposal_table.end(), "Could not find proposal with that name" );
check( expires_at > proposal_itr->expires_at, "expires_at must be greater than current expiry.");
check( proposal_itr->proposer == proposer, "proposer does not match original proposer of proposal_name");

// Modify `proposals` table with lock boolean (true/false)
proposal_table.modify(proposal_itr, eosio::same_payer, [&](auto & row) {
row.expires_at = expires_at;
});
}

/**
* Cancel proposal using the authorization from the {{ proposer }}
*
* `proposal` & `votes` will be removed
*/
void forum::cancel(const name proposer, const name proposal_name, const uint64_t max_count) {
void forum::cancel(const name proposer, const name proposal_name) {
require_auth(proposer);

// Check if proposal already exists
@@ -229,9 +137,6 @@ void forum::cancel(const name proposer, const name proposal_name, const uint64_t
// Only original `proposer` of `proposal_name` is authorized to cancel a proposal prior to expiration
check( proposal_itr->proposer == proposer, "proposer does not match original proposer of proposal_name");

// Enforce `max_count` to be high enough to prevent vote manipulation using the of cancel action
check( max_count >= 500, "max_count must be equal or greater than 500");

votes vote_table(_self, _self.value);
auto index = vote_table.template get_index<"byproposal"_n>();

@@ -242,8 +147,9 @@ void forum::cancel(const name proposer, const name proposal_name, const uint64_t
auto upper_itr = index.upper_bound(vote_key_upper_bound);

// Iterate over votes and delete rows in `votes` table
// To prevent maximum CPU limit errors, only 1500 votes can be removed per `cancel` action
uint64_t count = 0;
while (count < max_count && lower_itr != upper_itr) {
while (count < 1500 && lower_itr != upper_itr) {
lower_itr = index.erase(lower_itr);
count++;
}
@@ -265,12 +171,12 @@ void forum::update_status(
if (itr == status_table.end()) {
status_table.emplace(_self, [&](auto& row) {
row.account = account;
row.updated_at = current_time_point_sec();
row.updated_at = current_time_point();
updater(row);
});
} else {
status_table.modify(itr, eosio::same_payer, [&](auto& row) {
row.updated_at = current_time_point_sec();
row.updated_at = current_time_point();
updater(row);
});
}
@@ -291,12 +197,12 @@ void forum::update_vote(
row.id = vote_table.available_primary_key();
row.proposal_name = proposal_name;
row.voter = voter;
row.updated_at = current_time_point_sec();
row.updated_at = current_time_point();
updater(row);
});
} else {
index.modify(itr, eosio::same_payer, [&](auto& row) {
row.updated_at = current_time_point_sec();
row.updated_at = current_time_point();
updater(row);
});
}

0 comments on commit 4dc3342

Please sign in to comment.
You can’t perform that action at this time.