From 98e5ab9d7d03ce980f34c307f19d9b929bf0a5bc Mon Sep 17 00:00:00 2001 From: Gerard097 Date: Tue, 25 Jul 2023 21:25:07 -0500 Subject: [PATCH 01/51] Fix designated initializers --- src/dao.cpp | 12 ++++++------ src/upvote_election/actions.cpp | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/dao.cpp b/src/dao.cpp index b334c39..ac323e1 100644 --- a/src/dao.cpp +++ b/src/dao.cpp @@ -828,7 +828,7 @@ void dao::claimnextper(uint64_t assignment_id) auto ab = calculatePeriodPayout( *periodToClaim, - AssetBatch{ .peg = pegSalary, .voice = voiceSalary, .reward = rewardSalary }, + AssetBatch{ .reward = rewardSalary, .peg = pegSalary, .voice = voiceSalary }, daoTokens, nextOpt, lastUsedTimeShare, @@ -962,9 +962,9 @@ AssetBatch dao::calculatePeriodPayout(Period& period, int64_t periodEndSec = period.getEndTime().sec_since_epoch(); AssetBatch payout { + .reward = eosio::asset{ 0, daoTokens.reward.symbol }, .peg = eosio::asset{ 0, daoTokens.peg.symbol }, - .voice = eosio::asset{ 0, daoTokens.voice.symbol }, - .reward = eosio::asset{ 0, daoTokens.reward.symbol } + .voice = eosio::asset{ 0, daoTokens.voice.symbol } }; while (nextTimeShareOpt) @@ -1035,9 +1035,9 @@ AssetBatch dao::calculatePendingClaims(uint64_t assignmentID, const AssetBatch& Assignment assignment(this, assignmentID); AssetBatch payAmount { + .reward = eosio::asset{ 0, daoTokens.reward.symbol }, .peg = eosio::asset{ 0, daoTokens.peg.symbol }, - .voice = eosio::asset{ 0, daoTokens.voice.symbol }, - .reward = eosio::asset{ 0, daoTokens.reward.symbol } + .voice = eosio::asset{ 0, daoTokens.voice.symbol } }; // Ensure that the claimed period is within the approved period count @@ -1050,8 +1050,8 @@ AssetBatch dao::calculatePendingClaims(uint64_t assignmentID, const AssetBatch& const asset rewardSalary = assignment.getRewardSalary(); AssetBatch salary{ - .peg = pegSalary, .reward = rewardSalary, + .peg = pegSalary, .voice = voiceSalary }; diff --git a/src/upvote_election/actions.cpp b/src/upvote_election/actions.cpp index 365d177..de01e39 100644 --- a/src/upvote_election/actions.cpp +++ b/src/upvote_election/actions.cpp @@ -654,9 +654,9 @@ void dao::createupvelc(uint64_t dao_id, ContentGroups& election_config) auto rounds = getRounds(election_config, endDate); UpvoteElection upvoteElection(*this, dao_id, UpvoteElectionData{ - .status = upvote_common::upvote_status::UPCOMING, .start_date = startDate, .end_date = endDate, + .status = upvote_common::upvote_status::UPCOMING, .duration = duration }); From 7f8ce43e6a861eaaaaae3e8255df229b877c4a5f Mon Sep 17 00:00:00 2001 From: Gerard097 Date: Tue, 25 Jul 2023 21:29:36 -0500 Subject: [PATCH 02/51] Fix likeable interface --- include/comments/comment.hpp | 2 +- include/comments/likeable.hpp | 3 ++- include/comments/section.hpp | 2 +- src/comments/comment.cpp | 6 +++--- src/comments/section.cpp | 4 ++-- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/include/comments/comment.hpp b/include/comments/comment.hpp index c224278..bec756a 100644 --- a/include/comments/comment.hpp +++ b/include/comments/comment.hpp @@ -9,7 +9,7 @@ namespace hypha { class dao; - class Comment : virtual public TypedDocument, public Likeable + class Comment : public Likeable { public: Comment(dao& dao, uint64_t id); diff --git a/include/comments/likeable.hpp b/include/comments/likeable.hpp index 8f4b249..5f36813 100644 --- a/include/comments/likeable.hpp +++ b/include/comments/likeable.hpp @@ -4,9 +4,10 @@ namespace hypha { - class Likeable: virtual public TypedDocument + class Likeable : public TypedDocument { public: + using TypedDocument::TypedDocument; virtual ~Likeable(); const void like(eosio::name user, eosio::name reaction); const void unlike(eosio::name user); diff --git a/include/comments/section.hpp b/include/comments/section.hpp index 60a2863..278f041 100644 --- a/include/comments/section.hpp +++ b/include/comments/section.hpp @@ -9,7 +9,7 @@ namespace hypha class dao; - class Section : virtual public TypedDocument, public Likeable + class Section : public Likeable { public: Section(dao& dao, uint64_t id); diff --git a/src/comments/comment.cpp b/src/comments/comment.cpp index 5344a4e..245a86a 100644 --- a/src/comments/comment.cpp +++ b/src/comments/comment.cpp @@ -14,7 +14,7 @@ namespace hypha const std::string ENTRY_EDITED = "edited"; const std::string ENTRY_DELETED = "deleted"; - Comment::Comment(dao& dao, uint64_t id) : TypedDocument(dao, id, TYPED_DOCUMENT_TYPE) + Comment::Comment(dao& dao, uint64_t id) : Likeable(dao, id, TYPED_DOCUMENT_TYPE) { TRACE_FUNCTION() } @@ -24,7 +24,7 @@ namespace hypha Section& section, const eosio::name author, const string& content - ) : TypedDocument(dao, TYPED_DOCUMENT_TYPE) + ) : Likeable(dao, TYPED_DOCUMENT_TYPE) { TRACE_FUNCTION() this->initComment( @@ -40,7 +40,7 @@ namespace hypha Comment& comment, const eosio::name author, const string& content - ) : TypedDocument(dao, TYPED_DOCUMENT_TYPE) + ) : Likeable(dao, TYPED_DOCUMENT_TYPE) { TRACE_FUNCTION() this->initComment( diff --git a/src/comments/section.cpp b/src/comments/section.cpp index 1034945..cffd3f6 100644 --- a/src/comments/section.cpp +++ b/src/comments/section.cpp @@ -11,7 +11,7 @@ namespace hypha const std::string GROUP_SECTION = "comment_section"; - Section::Section(dao& dao, uint64_t id) : TypedDocument(dao, id, TYPED_DOCUMENT_TYPE) + Section::Section(dao& dao, uint64_t id) : Likeable(dao, id, TYPED_DOCUMENT_TYPE) { TRACE_FUNCTION() } @@ -19,7 +19,7 @@ namespace hypha Section::Section( dao& dao, Document& proposal - ) : TypedDocument(dao, TYPED_DOCUMENT_TYPE) + ) : Likeable(dao, TYPED_DOCUMENT_TYPE) { TRACE_FUNCTION() ContentGroups contentGroups{ From ce3524ef80da88fbee3bacf3d575578a41f8004e Mon Sep 17 00:00:00 2001 From: Gerard097 Date: Fri, 28 Jul 2023 15:06:32 -0600 Subject: [PATCH 03/51] Fix Pricing plan designated initializer --- src/pricing/actions.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pricing/actions.cpp b/src/pricing/actions.cpp index 980a456..73965f9 100644 --- a/src/pricing/actions.cpp +++ b/src/pricing/actions.cpp @@ -537,8 +537,8 @@ ACTION dao::activateplan(ContentGroups& plan_info) .end_date = downgradeToDef ? eosio::time_point{} : end, .billing_day = billingDay, .period_count = downgradeToDef ? -1 : periods, - .offer_discount_perc_x10000 = offer.getDiscountPercentage(), .discount_perc_x10000 = plan.getDiscountPercentage(), + .offer_discount_perc_x10000 = offer.getDiscountPercentage(), .plan_name = plan.getName(), .plan_price = plan.getPrice(), .total_paid = payAmount, @@ -753,8 +753,8 @@ ACTION dao::updateprcpln(uint64_t pricing_plan_id, ContentGroups& pricing_plan_i .name = name ? name->getAs() : plan.getName(), .price = price ? price->getAs() : plan.getPrice(), .reactivation_period_sec = reactivationPeriod ? reactivationPeriod->getAs() : plan.getReactivationPeriod(), - .discount_perc_x10000 = discount ? discount->getAs() : plan.getDiscountPercentage(), - .max_member_count = maxMembers ? maxMembers->getAs() : plan.getMaxMemberCount() + .max_member_count = maxMembers ? maxMembers->getAs() : plan.getMaxMemberCount(), + .discount_perc_x10000 = discount ? discount->getAs() : plan.getDiscountPercentage() }); } From a41d84dbe5dad4f1185b9e4cc36b3532e0bc2cd8 Mon Sep 17 00:00:00 2001 From: NIK Date: Wed, 20 Sep 2023 18:17:53 +0800 Subject: [PATCH 04/51] intermediary check in --- deploy_dao.sh => build_and_deploy.sh | 0 deploy_only.sh | 2 ++ include/config/config.hpp | 2 +- include/dao.hpp | 15 +++++++++ src/dao.cpp | 48 ++++++++++++++++++++++------ 5 files changed, 57 insertions(+), 10 deletions(-) rename deploy_dao.sh => build_and_deploy.sh (100%) create mode 100755 deploy_only.sh diff --git a/deploy_dao.sh b/build_and_deploy.sh similarity index 100% rename from deploy_dao.sh rename to build_and_deploy.sh diff --git a/deploy_only.sh b/deploy_only.sh new file mode 100755 index 0000000..067e219 --- /dev/null +++ b/deploy_only.sh @@ -0,0 +1,2 @@ +cd build +cleos set contract dao.hypha dao dao_O3.wasm dao.abi \ No newline at end of file diff --git a/include/config/config.hpp b/include/config/config.hpp index ba7a4cc..86f39e7 100644 --- a/include/config/config.hpp +++ b/include/config/config.hpp @@ -1,5 +1,5 @@ #pragma once #define PRODUCTION_BUILD #define USE_TREASURY -// #define USE_UPVOTE_ELECTIONS +#define USE_UPVOTE_ELECTIONS // #define USE_PRICING_PLAN \ No newline at end of file diff --git a/include/dao.hpp b/include/dao.hpp index 526dc93..81fa97d 100644 --- a/include/dao.hpp +++ b/include/dao.hpp @@ -196,6 +196,8 @@ namespace pricing { ACTION initcalendar(uint64_t calendar_id, uint64_t next_period); + ACTION reset(); // debugging - maybe with the dev flags + #ifdef DEVELOP_BUILD_HELPERS struct InputEdge { @@ -482,6 +484,19 @@ namespace pricing { void createToken(const std::string& contractType, name issuer, const asset& token); + template + inline void delete_table (const name & code, const uint64_t & scope) { + + T table(code, scope); + auto itr = table.begin(); + + while (itr != table.end()) { + itr = table.erase(itr); + } + + } + + private: void onRewardTransfer(const name& from, const name& to, const asset& amount); diff --git a/src/dao.cpp b/src/dao.cpp index 9fdc09e..c47c915 100644 --- a/src/dao.cpp +++ b/src/dao.cpp @@ -1967,18 +1967,18 @@ void dao::createdao(ContentGroups& config) } else { - //Just do this check on mainnet - if (!isTestnet()) { + // //Just do this check on mainnet + // if (!isTestnet()) { - auto onboarder = configCW.getOrFail(DETAILS, common::ONBOARDER_ACCOUNT)->getAs(); + // auto onboarder = configCW.getOrFail(DETAILS, common::ONBOARDER_ACCOUNT)->getAs(); - auto hyphaId = getDAOID("hypha"_n); + // auto hyphaId = getDAOID("hypha"_n); - EOS_CHECK( - hyphaId.has_value() && Member::isMember(*this, *hyphaId, onboarder), - "You are not allowed to call this action" - ); - } + // EOS_CHECK( + // hyphaId.has_value() && Member::isMember(*this, *hyphaId, onboarder), + // "You are not allowed to call this action" + // ); + // } //Regular DAO creation auto daoDoc = createDao(nullptr); @@ -3444,4 +3444,34 @@ void dao::readDaoSettings(uint64_t daoID, const name& dao, ContentWrapper config Edge::write(get_self(), get_self(), daoID, settingsDoc.getID(), common::SETTINGS_EDGE); } +void dao::reset() { + require_auth(_self); + + delete_table(get_self(), 0); + delete_table(get_self(), 1); + delete_table(get_self(), 2); + delete_table(get_self(), 3); + delete_table(get_self(), get_self().value); + delete_table(get_self(), get_self().value); + delete_table(get_self(), get_self().value); + delete_table(get_self(), get_self().value); + + delete_table(get_self(), get_self().value); + delete_table(get_self(), get_self().value); + + // Document::document_table d_t(get_self(), get_self().value); + // auto d_itr = d_t.begin(); + // while (d_itr != d_t.end()) { + // d_itr = d_t.erase(d_itr); + // } + + // Edge::edge_table e_t(get_self(), get_self().value); + // auto e_itr = e_t.begin(); + // while (e_itr != e_t.end()) { + // e_itr = e_t.erase(e_itr); + // } + +} + + } // namespace hypha From 618dba58a35caf5c429eca874f148dfeef87acc8 Mon Sep 17 00:00:00 2001 From: NIK Date: Wed, 20 Sep 2023 20:38:47 +0800 Subject: [PATCH 05/51] merge accident --- src/dao.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/dao.cpp b/src/dao.cpp index f83b8e3..6acaaf8 100644 --- a/src/dao.cpp +++ b/src/dao.cpp @@ -1024,11 +1024,11 @@ AssetBatch dao::calculatePeriodPayout(Period& period, // { // Assignment assignment(this, assignmentID); - AssetBatch payAmount { - .reward = eosio::asset{ 0, daoTokens.reward.symbol }, - .peg = eosio::asset{ 0, daoTokens.peg.symbol }, - .voice = eosio::asset{ 0, daoTokens.voice.symbol } - }; +// AssetBatch payAmount { +// .reward = eosio::asset{ 0, daoTokens.reward.symbol }, +// .peg = eosio::asset{ 0, daoTokens.peg.symbol }, +// .voice = eosio::asset{ 0, daoTokens.voice.symbol } +// }; // // Ensure that the claimed period is within the approved period count // Period period = assignment.getStartPeriod(); @@ -1039,11 +1039,11 @@ AssetBatch dao::calculatePeriodPayout(Period& period, // const asset voiceSalary = assignment.getVoiceSalary(); // const asset rewardSalary = assignment.getRewardSalary(); - AssetBatch salary{ - .reward = rewardSalary, - .peg = pegSalary, - .voice = voiceSalary - }; +// AssetBatch salary{ +// .reward = rewardSalary, +// .peg = pegSalary, +// .voice = voiceSalary +// }; // auto currentTime = eosio::current_time_point().sec_since_epoch(); From 9409a51291576aeee14af9c777e24c33a9ad7320 Mon Sep 17 00:00:00 2001 From: NIK Date: Thu, 21 Sep 2023 11:50:14 +0800 Subject: [PATCH 06/51] bugfix for contract create proposal --- src/proposals/proposal.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/proposals/proposal.cpp b/src/proposals/proposal.cpp index ea6bce2..b940cf6 100644 --- a/src/proposals/proposal.cpp +++ b/src/proposals/proposal.cpp @@ -43,9 +43,9 @@ namespace hypha TRACE_FUNCTION() EOS_CHECK( - checkMembership(proposer, contentGroups) || - //Contract can always create proposal - proposer == m_dao.get_self(), + // Contract can always create proposal + proposer == m_dao.get_self() || + checkMembership(proposer, contentGroups), "Invalid memership for user: " + proposer.to_string() ); return this->internalPropose(proposer, contentGroups, publish, nullptr); From 7921eb9763e7fdbcfc45fbd7240d9e1319fe32b6 Mon Sep 17 00:00:00 2001 From: NIK Date: Thu, 21 Sep 2023 18:16:55 +0800 Subject: [PATCH 07/51] many fixes to set up a DAO from zero - setupdefs must be run before initCoreMembers - the contract needs to be a member (per Gery) - initialize system badges --- include/dao.hpp | 6 +++++ src/dao.cpp | 70 +++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 68 insertions(+), 8 deletions(-) diff --git a/include/dao.hpp b/include/dao.hpp index 4d59c4c..e9bf755 100644 --- a/include/dao.hpp +++ b/include/dao.hpp @@ -524,6 +524,12 @@ namespace pricing { void addDefaultSettings(ContentGroup& settingsGroup, const string& daoTitle, const string& daoDescStr); + void _setupdefs(uint64_t dao_id); + + void initSysBadges(); + void createSystemBadge(name badge_edge, string label, string icon); + + template std::optional getNameID(const name& n) const { diff --git a/src/dao.cpp b/src/dao.cpp index 5d57f4a..ddcf07c 100644 --- a/src/dao.cpp +++ b/src/dao.cpp @@ -258,6 +258,13 @@ void dao::setupdefs(uint64_t dao_id) checkAdminsAuth(dao_id); + _setupdefs(dao_id); + +} + +void dao::_setupdefs(uint64_t dao_id) +{ + auto defBadges = m_documentGraph.getEdgesFrom(getRootID(), name("defbadge")); for (auto& badge : defBadges) { @@ -1353,6 +1360,54 @@ static void makeEnrollerBadge(dao& dao, const name& owner, uint64_t daoID) makeAutoApproveBadge(dao.get_self(), "Enroller", "Enroller Permissions", owner, daoID, enrollerBadge.getID()); } +void dao::initSysBadges() { + + createSystemBadge(badges::common::links::ADMIN_BADGE, "Admin Badge", "https://assets.hypha.earth/badges/badge_admin.png"); + createSystemBadge(badges::common::links::ENROLLER_BADGE, "Enroller Badge", "https://assets.hypha.earth/badges/badge_enroller.png"); + createSystemBadge(badges::common::links::ENROLLER_BADGE, "Enroller Badge", "https://assets.hypha.earth/badges/badge_enroller.png"); + +} + +void dao::createSystemBadge(name badge_edge, string label, string icon) { + + badges::SystemBadgeType systemBadgeType; + if (badge_edge == badges::common::links::ADMIN_BADGE) { + systemBadgeType = badges::SystemBadgeType::Admin; + } else if (badge_edge == badges::common::links::ENROLLER_BADGE) { + systemBadgeType = badges::SystemBadgeType::Enroller; + } else { + eosio::check(false, "unknown system type"); + } + + + Document badge( + get_self(), + get_self(), + ContentGroups{ + ContentGroup{ + Content(CONTENT_GROUP_LABEL, DETAILS), + Content(common::STATE, common::STATE_APPROVED), + Content(common::VOICE_COEFFICIENT, 10000), + Content(common::REWARD_COEFFICIENT, 10000), + Content(common::PEG_COEFFICIENT, 10000), + Content("dao", (int64_t)getRootID()), + Content(ICON, icon) + }, + ContentGroup{ + Content(CONTENT_GROUP_LABEL, SYSTEM), + Content(TYPE, common::BADGE_NAME), + Content(hypha::badges::common::items::SYSTEM_BADGE_ID, (int64_t)systemBadgeType), + Content(NODE_LABEL, label) + } + } + ); + + auto edge = Edge(get_self(), get_self(), getRootID(), badge.getID(), badge_edge); + auto edge2 = Edge(get_self(), get_self(), getRootID(), badge.getID(), name("defbadge")); + +} + + static void makeAdminBadge(dao& dao, const name& owner, uint64_t daoID) { auto adminBadgeEdge = Edge::get(dao.get_self(), dao.getRootID(), badges::common::links::ADMIN_BADGE); @@ -1735,6 +1790,7 @@ ACTION dao::createcalen(bool is_default) const auto rootId = getRootID(); Edge(get_self(), get_self(), rootId, calendarDoc.getID(), name(common::CALENDAR_WEEK)); + Edge(get_self(), get_self(), rootId, calendarDoc.getID(), name(common::CALENDAR)); Edge(get_self(), get_self(), rootId, calendarDoc.getID(), common::OWNS); Edge(get_self(), get_self(), calendarDoc.getID(), rootId, common::OWNED_BY); @@ -1848,6 +1904,7 @@ static void checkEcosystemDao(dao& dao, uint64_t daoID) void dao::createdao(ContentGroups& config) { TRACE_FUNCTION(); + EOS_CHECK(!isPaused(), "Contract is paused for maintenance. Please try again later."); auto configCW = ContentWrapper(config); @@ -1944,14 +2001,7 @@ void dao::createdao(ContentGroups& config) ).send(); #endif - eosio::action( - eosio::permission_level{ get_self(), name("active") }, - get_self(), - name("setupdefs"), - std::make_tuple( - daoDoc.getID() - ) - ).send(); + _setupdefs(daoDoc.getID()); auto onboarder = getSettingsDocument(daoDoc.getID())->getOrFail(common::ONBOARDER_ACCOUNT); @@ -2214,6 +2264,10 @@ void dao::createroot(const std::string& notes) Settings dhoSettings(*this, rootDoc.getID()); addNameID(common::DHO_ROOT_NAME, rootDoc.getID()); + + getOrCreateMember(get_self()); + + initSysBadges(); } void dao::modalerts(uint64_t root_id, ContentGroups& alerts) From 82100689b0d83fff41e45aa0b3bc64be1c6e3095 Mon Sep 17 00:00:00 2001 From: NIK Date: Thu, 21 Sep 2023 19:15:24 +0800 Subject: [PATCH 08/51] intermediary check in --- src/dao.cpp | 44 ++++++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/src/dao.cpp b/src/dao.cpp index ddcf07c..052a514 100644 --- a/src/dao.cpp +++ b/src/dao.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -1991,14 +1992,20 @@ void dao::createdao(ContentGroups& config) #ifdef USE_TREASURY // Create Treasury - eosio::action( - eosio::permission_level{ get_self(), name("active") }, - get_self(), - name("createtrsy"), - std::make_tuple( - daoDoc.getID() - ) - ).send(); + + // this is all it really does in createtrsy + hypha::treasury::Treasury trsy(*this, hypha::treasury::TreasuryData { + .dao = static_cast(daoDoc.getID()) + }); + + // eosio::action( + // eosio::permission_level{ get_self(), name("active") }, + // get_self(), + // name("createtrsy"), + // std::make_tuple( + // daoDoc.getID() + // ) + // ).send(); #endif _setupdefs(daoDoc.getID()); @@ -2940,7 +2947,7 @@ void dao::checkAdminsAuth(uint64_t dao_id) void dao::genPeriods(const std::string& owner, int64_t periodDuration, uint64_t ownerId, uint64_t calendarId, int64_t periodCount/*, int64_t period_duration_sec*/) { //Max number of periods that should be created in one call - const int64_t MAX_PERIODS_PER_CALL = 10; + const int64_t MAX_PERIODS_PER_CALL = 20; //Get last period //Document daoDoc(get_self(), dao_id); @@ -2996,18 +3003,23 @@ void dao::createVoiceToken(const eosio::name& daoName, auto dhoSettings = getSettingsDocument(); name governanceContract = dhoSettings->getOrFail(GOVERNANCE_TOKEN_CONTRACT); - eosio::action( + eosio::transaction txn; + txn.actions.emplace_back( eosio::permission_level{ governanceContract, name("active") }, governanceContract, name("create"), std::make_tuple( - daoName, - get_self(), - asset{ -getTokenUnit(voiceToken), voiceToken.symbol }, - decayPeriod, - decayPerPeriodx10M + daoName, + get_self(), + asset{ -getTokenUnit(voiceToken), voiceToken.symbol }, + decayPeriod, + decayPerPeriodx10M ) - ).send(); + ); + txn.send(daoName.value, get_self()); + + + } void dao::createToken(const std::string& contractType, name issuer, const asset& token) From 38b4f0a3c5fdb67b81f0a10381327c4cdd673717 Mon Sep 17 00:00:00 2001 From: NIK Date: Thu, 21 Sep 2023 20:14:55 +0800 Subject: [PATCH 09/51] removed various naked send() delayed actions the problem is they all used the same sender id and so cannot be used to create a new DAO Now we have different IDs everywhere for send transaction --- src/dao.cpp | 19 +++++++++++++------ src/member.cpp | 5 ----- src/util.cpp | 36 ++++++++++++++++++++++++------------ 3 files changed, 37 insertions(+), 23 deletions(-) diff --git a/src/dao.cpp b/src/dao.cpp index 052a514..1613644 100644 --- a/src/dao.cpp +++ b/src/dao.cpp @@ -1332,8 +1332,10 @@ void dao::setdaosetting(const uint64_t& dao_id, std::map(issuer.value) << 64) | to.value; + + txn.send(senderID + 1, issuer); + } void issueTenantToken(const eosio::name &token_contract, @@ -171,17 +177,23 @@ namespace hypha { TRACE_FUNCTION() - eosio::action( + eosio::transaction txn; + + txn.actions.emplace_back( eosio::permission_level{issuer, eosio::name("active")}, token_contract, eosio::name("issue"), - std::make_tuple(tenant, issuer, token_amount, memo)) - .send(); + std::make_tuple(tenant, issuer, token_amount, memo) + ); - eosio::action( + txn.actions.emplace_back( eosio::permission_level{issuer, eosio::name("active")}, token_contract, eosio::name("transfer"), - std::make_tuple(tenant, issuer, to, token_amount, memo)) - .send(); + std::make_tuple(tenant, issuer, to, token_amount, memo) + ); + uint128_t senderID = (static_cast(tenant.value) << 64) | to.value; + + txn.send(senderID + 2, issuer); + } double normalizeToken(const eosio::asset& token) From bf9c2c427c738abea9833610f583913a652d1ca6 Mon Sep 17 00:00:00 2001 From: NIK Date: Thu, 21 Sep 2023 23:42:05 +0800 Subject: [PATCH 10/51] Revert "removed various naked send() delayed actions" This reverts commit 38b4f0a3c5fdb67b81f0a10381327c4cdd673717. --- src/dao.cpp | 19 ++++++------------- src/member.cpp | 5 +++++ src/util.cpp | 36 ++++++++++++------------------------ 3 files changed, 23 insertions(+), 37 deletions(-) diff --git a/src/dao.cpp b/src/dao.cpp index 1613644..052a514 100644 --- a/src/dao.cpp +++ b/src/dao.cpp @@ -1332,10 +1332,8 @@ void dao::setdaosetting(const uint64_t& dao_id, std::map(issuer.value) << 64) | to.value; - - txn.send(senderID + 1, issuer); - + std::make_tuple(issuer, to, token_amount, memo)) + .send(); } void issueTenantToken(const eosio::name &token_contract, @@ -177,23 +171,17 @@ namespace hypha { TRACE_FUNCTION() - eosio::transaction txn; - - txn.actions.emplace_back( + eosio::action( eosio::permission_level{issuer, eosio::name("active")}, token_contract, eosio::name("issue"), - std::make_tuple(tenant, issuer, token_amount, memo) - ); + std::make_tuple(tenant, issuer, token_amount, memo)) + .send(); - txn.actions.emplace_back( + eosio::action( eosio::permission_level{issuer, eosio::name("active")}, token_contract, eosio::name("transfer"), - std::make_tuple(tenant, issuer, to, token_amount, memo) - ); - uint128_t senderID = (static_cast(tenant.value) << 64) | to.value; - - txn.send(senderID + 2, issuer); - + std::make_tuple(tenant, issuer, to, token_amount, memo)) + .send(); } double normalizeToken(const eosio::asset& token) From bef23df451542359a5a4b43b8888055b10d8c0ac Mon Sep 17 00:00:00 2001 From: NIK Date: Fri, 22 Sep 2023 00:11:21 +0800 Subject: [PATCH 11/51] back to inline actions --- src/dao.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/dao.cpp b/src/dao.cpp index 052a514..91435f4 100644 --- a/src/dao.cpp +++ b/src/dao.cpp @@ -3003,8 +3003,7 @@ void dao::createVoiceToken(const eosio::name& daoName, auto dhoSettings = getSettingsDocument(); name governanceContract = dhoSettings->getOrFail(GOVERNANCE_TOKEN_CONTRACT); - eosio::transaction txn; - txn.actions.emplace_back( + eosio::action( eosio::permission_level{ governanceContract, name("active") }, governanceContract, name("create"), @@ -3015,10 +3014,7 @@ void dao::createVoiceToken(const eosio::name& daoName, decayPeriod, decayPerPeriodx10M ) - ); - txn.send(daoName.value, get_self()); - - + ).send(); } From 077d504ac8575bc6b44c087c901a874576cc4486 Mon Sep 17 00:00:00 2001 From: Gerard097 Date: Fri, 22 Sep 2023 01:04:32 +0000 Subject: [PATCH 12/51] Modify setup.sh as bin/bash --- setup.sh | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/setup.sh b/setup.sh index d17cf3c..f81f1be 100755 --- a/setup.sh +++ b/setup.sh @@ -1,7 +1,11 @@ -#!/bin/sh +#!/bin/bash + +YELLOW="\e[33m" +DEFAULT="\e[0m" +RED="\e[31m" echoerr() { - echo "\033[0;31m Error - Invalid arguments: \033[0m $1" + echo -e "${RED}Error${DEFAULT} - Invalid arguments: ${RED}$1${DEFAULT}" echo "Expected one of the following modes of operation:" echo " Mode 1: setup.sh (dev|eosdev|prod|eosprod|develop|production) (e.g., 'setup.sh dev')" echo " Mode 2: setup.sh compiler (docker|local) (e.g., 'setup.sh compiler docker')" @@ -28,7 +32,7 @@ then # Get setup.sh directory since we could be running this script from a different location CWD="$(dirname "$0")" - echo "\nConfiguring build for \033[1;33m"$ENV"\033[0m\n" + echo -e "\nConfiguring build for ${YELLOW}"$ENV"${DEFAULT}\n" cp "$CWD/templates/config/$ENV.config.hpp" "$CWD/include/config/config.hpp" mkdir -p build @@ -49,7 +53,7 @@ then # Get setup.sh directory since we could be running this script from a different location CWD="$(dirname "$0")" - echo "\nConfiguring compiler for \033[1;33m"$COMPILER"\033[0m\n" + echo "\nConfiguring compiler for ${YELLOW}"$COMPILER"${DEFAULT}\n" if [[ "$COMPILER" == "local" ]]; then From d0f358fa7c46e2330bba940ff8856745be454f43 Mon Sep 17 00:00:00 2001 From: Gerard097 Date: Thu, 21 Sep 2023 19:13:38 -0600 Subject: [PATCH 13/51] Fix setup.sh colored text in Mac --- setup.sh | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/setup.sh b/setup.sh index f81f1be..aec4141 100755 --- a/setup.sh +++ b/setup.sh @@ -1,8 +1,20 @@ #!/bin/bash -YELLOW="\e[33m" -DEFAULT="\e[0m" -RED="\e[31m" +if [ "$(uname)" == "Linux" ]; then + YELLOW="\e[33m" + DEFAULT="\e[0m" + RED="\e[31m" +elif [ "$(uname)" == "Darwin" ]; then + RED="\033[0;31m" + DEFAULT="\033[0m" + YELLOW="\033[1;33m" +else + RED="" + DEFAULT="" + YELLOW="" + echo "INFO: Colored text disabled" +fi + echoerr() { echo -e "${RED}Error${DEFAULT} - Invalid arguments: ${RED}$1${DEFAULT}" From d9392f82b265ecc36d24ebccfd38f8a808e0d0ca Mon Sep 17 00:00:00 2001 From: NIK Date: Sun, 24 Sep 2023 22:01:52 +0800 Subject: [PATCH 14/51] intermediary check in --- include/dao.hpp | 6 + src/upvote_election/actions.cpp | 188 +++++++++++++++++++++++++++++ src/upvote_election/vote_group.cpp | 13 ++ 3 files changed, 207 insertions(+) diff --git a/include/dao.hpp b/include/dao.hpp index e9bf755..b93c49e 100644 --- a/include/dao.hpp +++ b/include/dao.hpp @@ -328,6 +328,9 @@ namespace pricing { #endif #ifdef USE_UPVOTE_ELECTIONS //Upvote System + ACTION startupelc(uint64_t election_id, bool reschedule); + ACTION testgrouprng(std::vector ids, uint32_t seed); + ACTION createupvelc(uint64_t dao_id, ContentGroups& election_config); ACTION editupvelc(uint64_t election_id, ContentGroups& election_config); ACTION cancelupvelc(uint64_t election_id); @@ -499,6 +502,9 @@ namespace pricing { private: + std::vector shuffleVector(std::vector& ids, uint32_t seed); + std::vector> createGroups(const std::vector& ids); + void onRewardTransfer(const name& from, const name& to, const asset& amount); //AssetBatch calculatePendingClaims(uint64_t assignmentID, const AssetBatch& daoTokens); diff --git a/src/upvote_election/actions.cpp b/src/upvote_election/actions.cpp index de01e39..08a95be 100644 --- a/src/upvote_election/actions.cpp +++ b/src/upvote_election/actions.cpp @@ -13,6 +13,9 @@ #include +#include + + #ifdef USE_UPVOTE_ELECTIONS namespace hypha { @@ -385,6 +388,147 @@ void dao::importelct(uint64_t dao_id, bool deferred) } #endif + +// Function to convert eosio::sha256 to a 64-bit uint +uint64_t sha256ToUint64(const eosio::checksum256& sha256Hash) { + const uint64_t* hashData = reinterpret_cast(sha256Hash.data()); + // Assuming sha256Hash is 32 bytes, take the first two 64-bit values + return hashData[0] ^ hashData[1]; +} + +/// @brief Test random group creation +/// @param ids +void dao::testgrouprng(std::vector ids, uint32_t seed) { + auto randomIds = shuffleVector(ids, seed); + + eosio::print("ids: "); + for (const uint64_t& element : ids) { + eosio::print(element, " "); + } + eosio::print("\n"); + + eosio::print("random ids: "); + for (const uint64_t& element : randomIds) { + eosio::print(element, " "); + } + eosio::print("\n"); + + auto groups = createGroups(randomIds); + + eosio::print("groups: "); + for (uint32_t i = 0; i < groups.size(); ++i) { + auto group = groups[i]; + eosio::print("group: ", i, "(", group.size(), "): "); + for (const uint64_t& element : group) { + eosio::print(element, " "); + } + } + eosio::print("\n"); + +} + +std::vector dao::shuffleVector(std::vector& ids, uint32_t seed) { + struct MyRandomGenerator { + using result_type = uint32_t; + + static constexpr result_type min() { + return 0; // Minimum possible random value + } + + static constexpr result_type max() { + return UINT32_MAX; // Maximum possible random value + } + + uint32_t seed; + uint32_t value; + + MyRandomGenerator(uint32_t seed, uint32_t value) { + this->seed = seed; + this->value = value; + } + + result_type operator()() { + const uint64_t a = 1103515245; + const uint64_t c = 12345; + + seed = (uint32_t)((a * seed + c) % 0x7fffffff); + value = ((uint64_t)seed * max()) >> 31; + + // Increment the value each time operator() is called + ++value; + + return value; + } + }; + + // Create a random number generator with the seed + MyRandomGenerator rng(seed, 0); + + // Shuffle the vector using the RNG + std::shuffle(ids.begin(), ids.end(), rng); + + return ids; + +} + +std::vector> dao::createGroups(const std::vector& ids) { + std::vector> groups; + + // Calculate the number of groups needed + int numGroups = std::ceil(static_cast(ids.size()) / 6); + + // Create the groups with an initial capacity of 6 + for (int i = 0; i < numGroups; ++i) { + groups.push_back(std::vector()); + groups.back().reserve(6); + } + + // Initialize group iterators + auto currentGroup = groups.begin(); + auto endGroup = groups.end(); + + // Iterate over the IDs and distribute them into groups + for (const auto& id : ids) { + // Add the ID to the current group + currentGroup->push_back(id); + + // Move to the next group (take turns) + ++currentGroup; + + // Wrap back to the beginning of the groups if needed + if (currentGroup == endGroup) { + currentGroup = groups.begin(); + } + } + + return groups; +} + +void dao::startupelc(uint64_t election_id, bool reschedule) +{ + UpvoteElection election(*this, election_id); + + auto status = election.getStatus(); + + auto now = eosio::current_time_point(); + + auto daoId = election.getDaoID(); + + // 1 - randomize + std::vector delegates = getGraph().getEdgesFrom(daoId, badges::common::links::DELEGATE); + + std::vector delegateIDs(delegates.size()); + for (Edge& delegate : delegates) { + delegateIDs.push_back(std::move(delegate.getToNode())); + } + + // eosio::checksum256 checksum = /* Your checksum calculation here */; + // Convert the checksum into a 32-bit integer seed + uint32_t seed = 0; // checksumToUint64(checksum); + + +} + //Check if we need to update an ongoing elections status: //upcoming -> ongoing //ongoing -> finished @@ -416,6 +560,8 @@ void dao::updateupvelc(uint64_t election_id, bool reschedule) //Let's update as we already started if (start <= now) { + + // startUpvoteElection(); Edge::get(get_self(), daoId, election.getId(), upvote_common::links::UPCOMING_ELECTION).erase(); @@ -427,6 +573,7 @@ void dao::updateupvelc(uint64_t election_id, bool reschedule) election.setCurrentRound(&startRound); //Setup all candidates + // TODO: randomize this list auto delegates = getGraph().getEdgesFrom(daoId, badges::common::links::DELEGATE); std::vector delegateIds; @@ -434,6 +581,14 @@ void dao::updateupvelc(uint64_t election_id, bool reschedule) for (auto& delegate : delegates) { delegateIds.push_back(delegate.getToNode()); + // TODO + // + // instead of adding delegates to the round, we add them to groups + // and we add the groups to the round + // A round and a group is the same data structure + // only the round has groups, and the group has members. + // + // startRound.addCandidate(delegate.getToNode()); } @@ -459,11 +614,14 @@ void dao::updateupvelc(uint64_t election_id, bool reschedule) Edge::get(get_self(), election_id, upvote_common::links::CURRENT_ROUND).erase(); if (auto nextRound = currentRound.getNextRound()) { + + // set up the next round election.setCurrentRound(nextRound.get()); setupCandidates(nextRound->getId(), winners); + // round.addCandidate adds candidates for (auto& winner : winners) { nextRound->addCandidate(winner); } @@ -471,6 +629,9 @@ void dao::updateupvelc(uint64_t election_id, bool reschedule) scheduleElectionUpdate(*this, election, nextRound->getEndDate()); } else { + + // The election has ended. + Edge::get(get_self(), daoId, election.getId(), upvote_common::links::ONGOING_ELECTION).erase(); Edge(get_self(), get_self(), daoId, election.getId(), upvote_common::links::PREVIOUS_ELECTION); @@ -547,6 +708,28 @@ void dao::cancelupvelc(uint64_t election_id) election.update(); } +// +// void VoteGroup::castvote(ElectionRound& round, VoteGroup& group, name voter, uint64_t member) { +// eosio::require_auth(voter); + +// //Verify round_id is the same as the current round +// ElectionRound round(*this, round_id); + +// UpvoteElection election = round.getElection(); + +// //Current round has to be defined +// auto currentRound = election.getCurrentRound(); + +// EOS_CHECK( +// currentRound.getId() == round_id, +// "You can only vote on the current round" +// ); + +// /// +// /// Get ElectionVoteGroup document for this memeber and this round + +// } + void dao::castelctnvote(uint64_t round_id, name voter, std::vector voted) { eosio::require_auth(voter); @@ -568,6 +751,10 @@ void dao::castelctnvote(uint64_t round_id, name voter, std::vector vot auto memberId = getMemberID(voter); + // TODO: We can probably change this check - we get the + // auto groupId = getGroup(voter) + // + EOS_CHECK( badges::hasVoterBadge(*this, election.getDaoID(), memberId) || //Just enable voting to candidates if the round is not the first one @@ -715,4 +902,5 @@ void dao::editupvelc(uint64_t election_id, ContentGroups& election_config) } + #endif \ No newline at end of file diff --git a/src/upvote_election/vote_group.cpp b/src/upvote_election/vote_group.cpp index 50ef86c..0cff217 100644 --- a/src/upvote_election/vote_group.cpp +++ b/src/upvote_election/vote_group.cpp @@ -6,6 +6,10 @@ #include "dao.hpp" +// this document keeps all votes a member has made in a round +// this will need to change since each member can only vote once in a round now, not as many as they want + + namespace hypha::upvote_election { using namespace upvote_election::common; @@ -62,6 +66,14 @@ std::optional VoteGroup::getFromRound(dao& dao, uint64_t roundId, uin return std::nullopt; } + +// limit the members field to 1 +// check the member is member of the same group +// This is one vote for multiple members in a large group - we won't have this anymore +// Vote group is tied to a single member + +// We can change the meaning of this + void VoteGroup::castVotes(ElectionRound& round, std::vector members) { auto roundId = getRoundID(); @@ -75,6 +87,7 @@ void VoteGroup::castVotes(ElectionRound& round, std::vector members) auto contract = getDao().get_self(); //We need to first erase previous votes if any + // Because someone might change their vote auto prevVotes = getDao().getGraph().getEdgesFrom(getId(), links::VOTE); dao::election_vote_table elctn_t(contract, roundId); From b34ad2e425f0ca287a8c92f3816bbdb50f7c5a6b Mon Sep 17 00:00:00 2001 From: NIK Date: Sun, 24 Sep 2023 22:38:20 +0800 Subject: [PATCH 15/51] more testing --- deploy_only.sh | 1 + include/dao.hpp | 1 + src/upvote_election/actions.cpp | 56 +++++++++++++++++++-------------- 3 files changed, 35 insertions(+), 23 deletions(-) diff --git a/deploy_only.sh b/deploy_only.sh index 067e219..d66813a 100755 --- a/deploy_only.sh +++ b/deploy_only.sh @@ -1,2 +1,3 @@ cd build +wasm-opt -O3 dao/dao.wasm -o dao/dao_O3.wasm cleos set contract dao.hypha dao dao_O3.wasm dao.abi \ No newline at end of file diff --git a/include/dao.hpp b/include/dao.hpp index b93c49e..012dd67 100644 --- a/include/dao.hpp +++ b/include/dao.hpp @@ -330,6 +330,7 @@ namespace pricing { //Upvote System ACTION startupelc(uint64_t election_id, bool reschedule); ACTION testgrouprng(std::vector ids, uint32_t seed); + ACTION testgroupr1(uint32_t num_members, uint32_t seed); ACTION createupvelc(uint64_t dao_id, ContentGroups& election_config); ACTION editupvelc(uint64_t election_id, ContentGroups& election_config); diff --git a/src/upvote_election/actions.cpp b/src/upvote_election/actions.cpp index 08a95be..1e33254 100644 --- a/src/upvote_election/actions.cpp +++ b/src/upvote_election/actions.cpp @@ -398,32 +398,45 @@ uint64_t sha256ToUint64(const eosio::checksum256& sha256Hash) { /// @brief Test random group creation /// @param ids +void dao::testgroupr1(uint32_t num_members, uint32_t seed) { + require_auth(get_self()); + + std::vector ids(num_members); // Initialize a vector with 100 elements + + // Set each element's value to its index + for (size_t i = 0; i < num_members; ++i) { + ids[i] = static_cast(i); + } + testgrouprng(ids, seed); + +} + void dao::testgrouprng(std::vector ids, uint32_t seed) { - auto randomIds = shuffleVector(ids, seed); - eosio::print("ids: "); - for (const uint64_t& element : ids) { - eosio::print(element, " "); - } - eosio::print("\n"); + require_auth(get_self()); + + // eosio::print(" ids: "); + // for (const uint64_t& element : ids) { + // eosio::print(element, " "); + // } - eosio::print("random ids: "); - for (const uint64_t& element : randomIds) { - eosio::print(element, " "); - } - eosio::print("\n"); + auto randomIds = shuffleVector(ids, seed); + + // eosio::print(" random ids: "); + // for (const uint64_t& element : randomIds) { + // eosio::print(element, " "); + // } auto groups = createGroups(randomIds); - eosio::print("groups: "); - for (uint32_t i = 0; i < groups.size(); ++i) { - auto group = groups[i]; - eosio::print("group: ", i, "(", group.size(), "): "); - for (const uint64_t& element : group) { - eosio::print(element, " "); - } - } - eosio::print("\n"); + // eosio::print(" groups: "); + // for (uint32_t i = 0; i < groups.size(); ++i) { + // auto group = groups[i]; + // eosio::print("group: ", i, "(", group.size(), "): "); + // for (const uint64_t& element : group) { + // eosio::print(element, " "); + // } + // } } @@ -454,9 +467,6 @@ std::vector dao::shuffleVector(std::vector& ids, uint32_t se seed = (uint32_t)((a * seed + c) % 0x7fffffff); value = ((uint64_t)seed * max()) >> 31; - // Increment the value each time operator() is called - ++value; - return value; } }; From 5a608cbb01c67fa14b7defad0998483091daf456 Mon Sep 17 00:00:00 2001 From: NIK Date: Mon, 25 Sep 2023 10:17:12 +0800 Subject: [PATCH 16/51] added ElectionGroup class and docuument --- include/upvote_election/common.hpp | 5 + include/upvote_election/election_group.hpp | 45 ++++++ src/CMakeLists.txt | 1 + src/upvote_election/election_group.cpp | 151 +++++++++++++++++++++ 4 files changed, 202 insertions(+) create mode 100644 include/upvote_election/election_group.hpp create mode 100644 src/upvote_election/election_group.cpp diff --git a/include/upvote_election/common.hpp b/include/upvote_election/common.hpp index e7e528f..c73f31c 100644 --- a/include/upvote_election/common.hpp +++ b/include/upvote_election/common.hpp @@ -17,7 +17,10 @@ namespace groups { namespace types { inline constexpr auto UPVOTE_ELECTION = eosio::name("upvt.electn"); inline constexpr auto ELECTION_ROUND = eosio::name("electn.round"); + inline constexpr auto ELECTION_GROUP = eosio::name("electn.group"); inline constexpr auto ELECTION_VOTE = eosio::name("election.vote"); + + // TODO: Remove this inline constexpr auto ELECTION_VOTE_GROUP = eosio::name("vote.group"); } @@ -36,8 +39,10 @@ namespace links { inline constexpr auto START_ROUND = eosio::name("startround"); inline constexpr auto CURRENT_ROUND = eosio::name("currentround"); inline constexpr auto CHIEF_ROUND = eosio::name("chiefround"); + inline constexpr auto ELECTION_ROUND_MEMBER = eosio::name("ue.rd.member"); inline constexpr auto HEAD_ROUND = eosio::name("headround"); inline constexpr auto ROUND = eosio::name("round"); + inline constexpr auto ELECTION_GROUP_LINK = eosio::name("ue.group.lnk"); inline constexpr auto NEXT_ROUND = eosio::name("nextround"); inline constexpr auto ROUND_CANDIDATE = eosio::name("candidate"); inline constexpr auto ROUND_WINNER = eosio::name("winner"); diff --git a/include/upvote_election/election_group.hpp b/include/upvote_election/election_group.hpp new file mode 100644 index 0000000..91448d8 --- /dev/null +++ b/include/upvote_election/election_group.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include + +#include + +#include + +#include + +namespace hypha::upvote_election +{ + +class UpvoteElection; + +class ElectionGroup : public TypedDocument +{ + DECLARE_DOCUMENT( + Data, + PROPERTY(type, std::string, Type, USE_GET), + PROPERTY(member_count, int64_t, MemberCount, USE_GET), + PROPERTY(group_id, int64_t, RoundId, USE_GET) + ) +public: + ElectionGroup(dao& dao, uint64_t id); + ElectionGroup(dao& dao, uint64_t group_id, std::vector member_ids, Data data); + + std::optional getWinner(); + + // UpvoteElection getElection(); + // std::vector getWinners(); + // int64_t getAccountPower(uint64_t accountId); + // bool isCandidate(uint64_t accountId); + // void addCandidate(uint64_t accountId); + +private: + virtual const std::string buildNodeLabel(ContentGroups &content) override + { + return "Election Group"; + } +}; + +using ElectionGroupData = ElectionGroup::Data; + +} // namespace hypha::upvote_election \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fce3e2f..8775286 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -60,6 +60,7 @@ add_contract( dao dao comments/reaction.cpp upvote_election/actions.cpp upvote_election/election_round.cpp + upvote_election/election_group.cpp upvote_election/upvote_election.cpp upvote_election/vote_group.cpp ../document-graph/src/document_graph/document_graph.cpp diff --git a/src/upvote_election/election_group.cpp b/src/upvote_election/election_group.cpp new file mode 100644 index 0000000..fbca6e7 --- /dev/null +++ b/src/upvote_election/election_group.cpp @@ -0,0 +1,151 @@ +#include "upvote_election/election_group.hpp" +#include "upvote_election/upvote_election.hpp" +#include "upvote_election/common.hpp" + +#include + +#include "dao.hpp" + +namespace hypha::upvote_election { + +using namespace upvote_election::common; + + +ElectionGroup::ElectionGroup(dao& dao, uint64_t id) + : TypedDocument(dao, id, types::ELECTION_GROUP) +{} + +ElectionGroup::ElectionGroup(dao& dao, uint64_t round_id, std::vector member_ids, Data data) + +//ElectionGroup::ElectionGroup(dao& dao, uint64_t id, Data data) + : TypedDocument(dao, types::ELECTION_GROUP) +{ + + auto cgs = convert(std::move(data)); + + initializeDocument(dao, cgs); + + EOS_CHECK(member_ids.size() <= 6, "max 6 members in group"); + + // create edges to all members (max 6) + for (size_t i = 0; i < member_ids.size(); ++i) { + Edge( + getDao().get_self(), + getDao().get_self(), + getId(), // from this + member_ids[i], // to the member + links::ELECTION_ROUND_MEMBER + ); + } + + + // Create edge from the round to this group + Edge( + getDao().get_self(), + getDao().get_self(), + round_id, + getId(), + links::ELECTION_GROUP_LINK + ); + + // I don't think we need 2 way links everywhere?! + // Edge( + // getDao().get_self(), + // getDao().get_self(), + // getId(), + // election_id, + // links::ELECTION + // ); +} + +std::optional ElectionGroup::getWinner() { + return std::nullopt; +} + +/* +UpvoteElection ElectionRound::getElection() { + return UpvoteElection( + getDao(), + Edge::get(getDao().get_self(), getId(), links::ELECTION).getToNode() + ); +} + +std::vector ElectionRound::getWinners() +{ + std::vector winners; + + dao::election_vote_table elctn_t(getDao().get_self(), getId()); + + auto byAmount = elctn_t.get_index<"byamount"_n>(); + + auto beg = byAmount.rbegin(); + + auto idx = 0; + + while (idx < getPassingCount() && beg != byAmount.rend()) { + winners.push_back(beg->account_id); + ++idx; + ++beg; + } + + return winners; +} + +void ElectionRound::setNextRound(ElectionRound* nextRound) const +{ + EOS_CHECK( + !getNextRound(), + "Election Round already has next round set" + ); + + Edge( + getDao().get_self(), + getDao().get_self(), + getId(), + nextRound->getId(), + links::NEXT_ROUND + ); +} + +std::unique_ptr ElectionRound::getNextRound() const +{ + if (auto [exists, edge] = Edge::getIfExists(getDao().get_self(), getId(), links::NEXT_ROUND); + exists) { + return std::make_unique(getDao(), edge.getToNode()); + } + + return {}; +} + +bool ElectionRound::isCandidate(uint64_t accountId) +{ + return Edge::exists( + getDao().get_self(), + getId(), + accountId, + links::ROUND_CANDIDATE + ); +} + +int64_t ElectionRound::getAccountPower(uint64_t accountId) +{ + if (isCandidate(accountId)) { + return std::max(int64_t{1}, getDelegatePower()); + } + + return 1; +} + +void ElectionRound::addCandidate(uint64_t accountId) +{ + Edge( + getDao().get_self(), + getDao().get_self(), + getId(), + accountId, + links::ROUND_CANDIDATE + ); +} +*/ + +} \ No newline at end of file From 6cefd8dc7b53c945ade397f0cea3a12f43a8bf07 Mon Sep 17 00:00:00 2001 From: NIK Date: Mon, 25 Sep 2023 23:45:17 +0800 Subject: [PATCH 17/51] intermediary check in defining objects round, group, and vote --- include/dao.hpp | 4 + include/upvote_election/common.hpp | 2 + include/upvote_election/election_group.hpp | 10 +- include/upvote_election/up_vote_vote.hpp | 42 +++++++ src/upvote_election/actions.cpp | 64 ++++++++++ src/upvote_election/election_group.cpp | 101 +++++---------- src/upvote_election/up_vote_vote.cpp | 135 +++++++++++++++++++++ 7 files changed, 283 insertions(+), 75 deletions(-) create mode 100644 include/upvote_election/up_vote_vote.hpp create mode 100644 src/upvote_election/up_vote_vote.cpp diff --git a/include/dao.hpp b/include/dao.hpp index 012dd67..5d9222d 100644 --- a/include/dao.hpp +++ b/include/dao.hpp @@ -331,12 +331,16 @@ namespace pricing { ACTION startupelc(uint64_t election_id, bool reschedule); ACTION testgrouprng(std::vector ids, uint32_t seed); ACTION testgroupr1(uint32_t num_members, uint32_t seed); + ACTION castupvote(uint64_t round_id, uint64_t group_id, name voter, uint64_t voted_id); ACTION createupvelc(uint64_t dao_id, ContentGroups& election_config); ACTION editupvelc(uint64_t election_id, ContentGroups& election_config); ACTION cancelupvelc(uint64_t election_id); ACTION updateupvelc(uint64_t election_id, bool reschedule); ACTION castelctnvote(uint64_t round_id, name voter, std::vector voted); + + + #ifdef EOS_BUILD ACTION importelct(uint64_t dao_id, bool deferred); #endif diff --git a/include/upvote_election/common.hpp b/include/upvote_election/common.hpp index c73f31c..5637d8d 100644 --- a/include/upvote_election/common.hpp +++ b/include/upvote_election/common.hpp @@ -19,6 +19,7 @@ namespace types { inline constexpr auto ELECTION_ROUND = eosio::name("electn.round"); inline constexpr auto ELECTION_GROUP = eosio::name("electn.group"); inline constexpr auto ELECTION_VOTE = eosio::name("election.vote"); + inline constexpr auto ELECTION_UP_VOTE = eosio::name("upvt.vote"); // TODO: Remove this inline constexpr auto ELECTION_VOTE_GROUP = eosio::name("vote.group"); @@ -47,6 +48,7 @@ namespace links { inline constexpr auto ROUND_CANDIDATE = eosio::name("candidate"); inline constexpr auto ROUND_WINNER = eosio::name("winner"); inline constexpr auto ELECTION_GROUP = eosio::name("elctngroup"); + inline constexpr auto UP_VOTE_VOTE = eosio::name("ue.vote"); inline constexpr auto VOTE = eosio::name("vote"); inline constexpr auto CHIEF_DELEGATE = eosio::name("chiefdelegate"); inline constexpr auto HEAD_DELEGATE = eosio::name("headdelegate"); diff --git a/include/upvote_election/election_group.hpp b/include/upvote_election/election_group.hpp index 91448d8..279bd8a 100644 --- a/include/upvote_election/election_group.hpp +++ b/include/upvote_election/election_group.hpp @@ -15,9 +15,9 @@ class UpvoteElection; class ElectionGroup : public TypedDocument { + DECLARE_DOCUMENT( Data, - PROPERTY(type, std::string, Type, USE_GET), PROPERTY(member_count, int64_t, MemberCount, USE_GET), PROPERTY(group_id, int64_t, RoundId, USE_GET) ) @@ -26,13 +26,9 @@ class ElectionGroup : public TypedDocument ElectionGroup(dao& dao, uint64_t group_id, std::vector member_ids, Data data); std::optional getWinner(); + bool isElectionRoundMember(uint64_t accountId); + void vote(uint64_t from, uint64_t to); - // UpvoteElection getElection(); - // std::vector getWinners(); - // int64_t getAccountPower(uint64_t accountId); - // bool isCandidate(uint64_t accountId); - // void addCandidate(uint64_t accountId); - private: virtual const std::string buildNodeLabel(ContentGroups &content) override { diff --git a/include/upvote_election/up_vote_vote.hpp b/include/upvote_election/up_vote_vote.hpp new file mode 100644 index 0000000..b753fad --- /dev/null +++ b/include/upvote_election/up_vote_vote.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include +#include + +namespace hypha::upvote_election +{ + +class ElectionGroup; + +// stores voted +// edges +// round -> vote +// member -> vote +// +class UpVoteVote : public TypedDocument +{ + DECLARE_DOCUMENT( + Data, + PROPERTY(voter_id, int64_t, VoterID, USE_GET), + PROPERTY(voted_id, int64_t, VotedID, USE_GET) + ) + +public: + UpVoteVote(dao& dao, uint64_t id); + UpVoteVote(dao& dao, uint64_t group_id, Data data); + + // void castVotes(ElectionGroup& group, uint64_t voted); //?? + + uint64_t getElectionGroup(); + + // static std::optional getFromRound(dao& dao, uint64_t roundId, uint64_t memberId); +private: + virtual const std::string buildNodeLabel(ContentGroups &content) override + { + return "Vote Group"; + } +}; + +using UpVoteVoteData = UpVoteVote::Data; + +} // namespace hypha::upvote_election \ No newline at end of file diff --git a/src/upvote_election/actions.cpp b/src/upvote_election/actions.cpp index 1e33254..6303344 100644 --- a/src/upvote_election/actions.cpp +++ b/src/upvote_election/actions.cpp @@ -2,6 +2,8 @@ #include "upvote_election/upvote_election.hpp" #include "upvote_election/election_round.hpp" #include "upvote_election/vote_group.hpp" +#include "upvote_election/election_group.hpp" +#include "upvote_election/up_vote_vote.hpp" #include #include @@ -24,8 +26,12 @@ using upvote_election::UpvoteElection; using upvote_election::UpvoteElectionData; using upvote_election::ElectionRound; using upvote_election::ElectionRoundData; +using upvote_election::ElectionGroup; +using upvote_election::ElectionGroupData; using upvote_election::VoteGroup; using upvote_election::VoteGroupData; +using upvote_election::UpVoteVote; +using upvote_election::UpVoteVoteData; namespace upvote_common = upvote_election::common; @@ -740,6 +746,63 @@ void dao::cancelupvelc(uint64_t election_id) // } +void dao::castupvote(uint64_t round_id, uint64_t group_id, name voter, uint64_t voted_id) +{ + // Check auth + eosio::require_auth(voter); + auto memberId = getMemberID(voter); + + // Check the round is valid + ElectionRound round(*this, round_id); + UpvoteElection election = round.getElection(); + auto currentRound = election.getCurrentRound(); + + EOS_CHECK( + currentRound.getId() == round_id, + "You can only vote on the current round" + ); + + // Check the group membership of voter and voted + ElectionGroup group(*this, group_id); + + EOS_CHECK( + group.isElectionRoundMember(memberId), + "Only members of the group can vote." + ); + + EOS_CHECK( + group.isElectionRoundMember(voted_id), + "Only members of the group can be voted for." + ); + + // group.vote(); + + // EOS_CHECK( + // badges::hasVoterBadge(*this, election.getDaoID(), memberId) || + // //Just enable voting to candidates if the round is not the first one + // (currentRound.isCandidate(memberId) && currentRound.getDelegatePower() > 0), + // "Only registered voters are allowed to perform this action" + // ); + + // auto votedEdge = Edge::getOrNew(get_self(), get_self(), round_id, memberId, eosio::name("voted")); + + // if (votedEdge.empty()) { + // votedEdge.erase(); + // return; + // } + + // if (auto voteGroup = VoteGroup::getFromRound(*this, round_id, memberId)) { + // voteGroup->castVotes(round, std::move(voted)); + // } + // else { + // VoteGroup group(*this, memberId, VoteGroupData{ + // .round_id = static_cast(round_id) + // }); + + // group.castVotes(round, std::move(voted)); + // } +} + void dao::castelctnvote(uint64_t round_id, name voter, std::vector voted) { eosio::require_auth(voter); @@ -791,6 +854,7 @@ void dao::castelctnvote(uint64_t round_id, name voter, std::vector vot } } + /* election_config: [ [ diff --git a/src/upvote_election/election_group.cpp b/src/upvote_election/election_group.cpp index fbca6e7..7ccf7e3 100644 --- a/src/upvote_election/election_group.cpp +++ b/src/upvote_election/election_group.cpp @@ -1,6 +1,7 @@ #include "upvote_election/election_group.hpp" #include "upvote_election/upvote_election.hpp" #include "upvote_election/common.hpp" +#include "upvote_election/up_vote_vote.hpp" #include @@ -62,90 +63,54 @@ std::optional ElectionGroup::getWinner() { return std::nullopt; } -/* -UpvoteElection ElectionRound::getElection() { - return UpvoteElection( - getDao(), - Edge::get(getDao().get_self(), getId(), links::ELECTION).getToNode() +bool ElectionGroup::isElectionRoundMember(uint64_t accountId) +{ + return Edge::exists( + getDao().get_self(), + getId(), + accountId, + links::ELECTION_ROUND_MEMBER ); -} +} -std::vector ElectionRound::getWinners() +void ElectionGroup::vote(uint64_t from, uint64_t to) { - std::vector winners; - dao::election_vote_table elctn_t(getDao().get_self(), getId()); + // get all existing vote edges + std::vector voteEdges = getDao().getGraph().getEdgesFrom(getId(), links::UP_VOTE_VOTE); + + // make sure both members are part of this group - auto byAmount = elctn_t.get_index<"byamount"_n>(); + // get the member count - auto beg = byAmount.rbegin(); + // iterate over vote edges - calculate who has the most votes and find the "from" edge + UpVoteVote* vote = nullptr; - auto idx = 0; + for (auto& edge : voteEdges) { - while (idx < getPassingCount() && beg != byAmount.rend()) { - winners.push_back(beg->account_id); - ++idx; - ++beg; } - return winners; -} - -void ElectionRound::setNextRound(ElectionRound* nextRound) const -{ - EOS_CHECK( - !getNextRound(), - "Election Round already has next round set" - ); + // if from edge, delete the edge and create a new upvote doc - or change its voted field and save it back? + // if no from edge, create a new UpVoteVote doc - Edge( - getDao().get_self(), - getDao().get_self(), - getId(), - nextRound->getId(), - links::NEXT_ROUND - ); -} - -std::unique_ptr ElectionRound::getNextRound() const -{ - if (auto [exists, edge] = Edge::getIfExists(getDao().get_self(), getId(), links::NEXT_ROUND); - exists) { - return std::make_unique(getDao(), edge.getToNode()); - } - return {}; -} -bool ElectionRound::isCandidate(uint64_t accountId) -{ - return Edge::exists( - getDao().get_self(), - getId(), - accountId, - links::ROUND_CANDIDATE - ); -} -int64_t ElectionRound::getAccountPower(uint64_t accountId) -{ - if (isCandidate(accountId)) { - return std::max(int64_t{1}, getDelegatePower()); - } + // if (exists) { + // vote = UpVoteVote(getDao(), edge.getToNode()); + // } else { + // vote = UpVoteVote() + // } - return 1; + // Edge( + // getDao().get_self(), + // getDao().get_self(), + // getId(), + // nextRound->getId(), + // links::NEXT_ROUND + // ); } -void ElectionRound::addCandidate(uint64_t accountId) -{ - Edge( - getDao().get_self(), - getDao().get_self(), - getId(), - accountId, - links::ROUND_CANDIDATE - ); -} -*/ + } \ No newline at end of file diff --git a/src/upvote_election/up_vote_vote.cpp b/src/upvote_election/up_vote_vote.cpp new file mode 100644 index 0000000..9b04723 --- /dev/null +++ b/src/upvote_election/up_vote_vote.cpp @@ -0,0 +1,135 @@ +#include "upvote_election/vote_group.hpp" + +#include "upvote_election/common.hpp" + +#include "upvote_election/election_round.hpp" + +#include "dao.hpp" + +// this document keeps all votes a member has made in a round +// this will need to change since each member can only vote once in a round now, not as many as they want + + +namespace hypha::upvote_election { + +using namespace upvote_election::common; + +UpVoteVote::UpVoteVote(dao& dao, uint64_t id) + : TypedDocument(dao, id, types::ELECTION_UP_VOTE) +{} + +// invoke + // UpVoteVote upvote(*this, memberId, UpVoteVoteData{ + // .round_id = static_cast(round_id) + // }); + + +UpVoteVote::UpVoteVote(dao& dao, uint64_t group_id, Data data) + : TypedDocument(dao, types::ELECTION_UP_VOTE) +{ + auto cgs = convert(std::move(data)); + + initializeDocument(dao, cgs); + + // edge from group to vote + Edge( + getDao().get_self(), + getDao().get_self(), + group_id, + getId(), + links::UP_VOTE_VOTE + ); + +} + +// not sure we need this but we have the edge... +uint64_t UpVoteVote::getElectionGroup() +{ + return Edge::getTo( + getDao().get_self(), + getId(), + links::UP_VOTE_VOTE + ).getFromNode(); +} + +// std::optional UpVoteVote::getFromRound(dao& dao, uint64_t roundId, uint64_t memberId) +// { +// auto groups = dao.getGraph().getEdgesFrom(memberId, common::links::ELECTION_GROUP); + +// for (auto& group : groups) { +// if (Edge::exists(dao.get_self(), group.getToNode(), roundId, links::ROUND)) { +// return UpVoteVote(dao, group.getToNode()); +// } +// } + +// return std::nullopt; +// } + + +// limit the members field to 1 +// check the member is member of the same group +// This is one vote for multiple members in a large group - we won't have this anymore +// Vote group is tied to a single member + +// We can change the meaning of this + +// void UpVoteVote::castVotes(ElectionRound& round, std::vector members) +// { +// auto roundId = getRoundID(); +// auto power = round.getAccountPower(getOwner()); + +// EOS_CHECK( +// roundId == round.getId(), +// "Missmatch between stored round id and round parameter" +// ); + +// auto contract = getDao().get_self(); + +// //We need to first erase previous votes if any +// // Because someone might change their vote +// auto prevVotes = getDao().getGraph().getEdgesFrom(getId(), links::VOTE); + +// dao::election_vote_table elctn_t(contract, roundId); + +// for (auto& edge : prevVotes) { +// auto memId = edge.getToNode(); +// auto voteEntry = elctn_t.get(memId, "Member entry doesn't exists"); +// elctn_t.modify(voteEntry, eosio::same_payer, [&](dao::ElectionVote& vote){ +// vote.total_amount -= power; +// }); +// edge.erase(); +// } + +// for (auto memId : members) { +// //Verify member is a candidate +// EOS_CHECK( +// round.isCandidate(memId), +// "Member must be a candidate to be voted" +// ); + +// auto voteIt = elctn_t.find(memId); + +// if (voteIt != elctn_t.end()) { +// elctn_t.modify(voteIt, eosio::same_payer, [&](dao::ElectionVote& vote){ +// vote.total_amount += power; +// }); +// } +// else { +// elctn_t.emplace(contract, [&](dao::ElectionVote& vote){ +// vote.total_amount += power; +// vote.account_id = memId; +// }); +// } + +// Edge( +// contract, +// contract, +// getId(), +// memId, +// links::VOTE +// ); +// } +// } + +} + From 37151f59cb174e88b67905d7df2e33610fa70af9 Mon Sep 17 00:00:00 2001 From: NIK Date: Tue, 26 Sep 2023 08:55:58 +0800 Subject: [PATCH 18/51] intermediary check in --- include/upvote_election/common.hpp | 1 + include/upvote_election/election_group.hpp | 7 +-- include/upvote_election/up_vote_vote.hpp | 4 +- src/upvote_election/election_group.cpp | 59 +++++++++++++++++++--- src/upvote_election/election_round.cpp | 35 +++++++++++++ src/upvote_election/up_vote_vote.cpp | 6 --- 6 files changed, 95 insertions(+), 17 deletions(-) diff --git a/include/upvote_election/common.hpp b/include/upvote_election/common.hpp index 5637d8d..4f64b3f 100644 --- a/include/upvote_election/common.hpp +++ b/include/upvote_election/common.hpp @@ -49,6 +49,7 @@ namespace links { inline constexpr auto ROUND_WINNER = eosio::name("winner"); inline constexpr auto ELECTION_GROUP = eosio::name("elctngroup"); inline constexpr auto UP_VOTE_VOTE = eosio::name("ue.vote"); + inline constexpr auto UPVOTE_GROUP_WINNER = eosio::name("ue.winner"); inline constexpr auto VOTE = eosio::name("vote"); inline constexpr auto CHIEF_DELEGATE = eosio::name("chiefdelegate"); inline constexpr auto HEAD_DELEGATE = eosio::name("headdelegate"); diff --git a/include/upvote_election/election_group.hpp b/include/upvote_election/election_group.hpp index 279bd8a..783059b 100644 --- a/include/upvote_election/election_group.hpp +++ b/include/upvote_election/election_group.hpp @@ -19,15 +19,16 @@ class ElectionGroup : public TypedDocument DECLARE_DOCUMENT( Data, PROPERTY(member_count, int64_t, MemberCount, USE_GET), - PROPERTY(group_id, int64_t, RoundId, USE_GET) + PROPERTY(group_id, int64_t, GroupId, USE_GET), + PROPERTY(winner, int64_t, Winner, USE_GETSET) + ) public: ElectionGroup(dao& dao, uint64_t id); ElectionGroup(dao& dao, uint64_t group_id, std::vector member_ids, Data data); - std::optional getWinner(); bool isElectionRoundMember(uint64_t accountId); - void vote(uint64_t from, uint64_t to); + void vote(int64_t from, int64_t to); private: virtual const std::string buildNodeLabel(ContentGroups &content) override diff --git a/include/upvote_election/up_vote_vote.hpp b/include/upvote_election/up_vote_vote.hpp index b753fad..9ebca79 100644 --- a/include/upvote_election/up_vote_vote.hpp +++ b/include/upvote_election/up_vote_vote.hpp @@ -17,8 +17,8 @@ class UpVoteVote : public TypedDocument { DECLARE_DOCUMENT( Data, - PROPERTY(voter_id, int64_t, VoterID, USE_GET), - PROPERTY(voted_id, int64_t, VotedID, USE_GET) + PROPERTY(voter_id, int64_t, VoterId, USE_GET), + PROPERTY(voted_id, int64_t, VotedId, USE_GETSET) ) public: diff --git a/src/upvote_election/election_group.cpp b/src/upvote_election/election_group.cpp index 7ccf7e3..1d325e6 100644 --- a/src/upvote_election/election_group.cpp +++ b/src/upvote_election/election_group.cpp @@ -6,6 +6,7 @@ #include #include "dao.hpp" +#include namespace hypha::upvote_election { @@ -24,6 +25,8 @@ ElectionGroup::ElectionGroup(dao& dao, uint64_t round_id, std::vector auto cgs = convert(std::move(data)); + //data.member_count = member_ids.size(); + initializeDocument(dao, cgs); EOS_CHECK(member_ids.size() <= 6, "max 6 members in group"); @@ -59,10 +62,6 @@ ElectionGroup::ElectionGroup(dao& dao, uint64_t round_id, std::vector // ); } -std::optional ElectionGroup::getWinner() { - return std::nullopt; -} - bool ElectionGroup::isElectionRoundMember(uint64_t accountId) { return Edge::exists( @@ -73,7 +72,7 @@ bool ElectionGroup::isElectionRoundMember(uint64_t accountId) ); } -void ElectionGroup::vote(uint64_t from, uint64_t to) +void ElectionGroup::vote(int64_t from, int64_t to) { // get all existing vote edges @@ -82,13 +81,61 @@ void ElectionGroup::vote(uint64_t from, uint64_t to) // make sure both members are part of this group // get the member count + auto memcount = getMemberCount(); + uint64_t votesToWin = memcount * 2 / 3 + 1; // iterate over vote edges - calculate who has the most votes and find the "from" edge - UpVoteVote* vote = nullptr; + std::unordered_map voteCount; + + bool existingVote = false; + bool hasWinner = false; + int64_t winner = 0; for (auto& edge : voteEdges) { + UpVoteVote upvote(getDao(), edge.getToNode()); + + int64_t voted_id = upvote.getVotedId(); + int64_t voter_id = upvote.getVoterId(); + + if (voteCount.find(voted_id) == voteCount.end()) { + voteCount[voted_id] = 1; + } else { + voteCount[voted_id] = voteCount[voted_id] + 1; + } + + // check for winner + if (voteCount[voted_id] >= votesToWin) { + hasWinner = true; + winner = voted_id; + } + + // check if we need to change the vote + if (voter_id == from) { + // change vote + existingVote = true; + upvote.setVotedId(to); + upvote.update(); + } + } + + if (!existingVote) { + // Create a new vote + UpVoteVote vote(getDao(), getId(), UpVoteVoteData{ + .voter_id = from, + .voted_id = to + }); + // edge case - can't happen in upvote elections + // if (votesToWin == 1) { + // hasWinner = true; + // winner = to; + // } + } + if (hasWinner) { + setWinner(winner); + update(); } + // if from edge, delete the edge and create a new upvote doc - or change its voted field and save it back? // if no from edge, create a new UpVoteVote doc diff --git a/src/upvote_election/election_round.cpp b/src/upvote_election/election_round.cpp index cf4bc9d..57431b2 100644 --- a/src/upvote_election/election_round.cpp +++ b/src/upvote_election/election_round.cpp @@ -10,6 +10,41 @@ namespace hypha::upvote_election { +// SET VALUE EXAMPLE - use accessors magically created with the DECLARE_DOCUMENT Macro +// + // DECLARE_DOCUMENT( + // Data, + // PROPERTY(credit, eosio::asset, Credit, USE_GETSET), + // PROPERTY(type, std::string, Type, USE_GETSET) + // ) +//// +//// ... code +// setCredit(std::move(newCredit)); // credit is asset type +// update(); +//// ... +// get +// float offerDisc = 1.0f - offer.getDiscountPercentage() / 10000.f; +// +// +// the magical setters are defined to acess the content group details and get/set +// they don't seem to call update, so update needs to be called. +// +// #define USE_GET_SET_DEC(name, type, getSet)\ +// const type& get##getSet() {\ +// return getContentWrapper()\ +// .getOrFail(DETAILS, #name)\ +// ->getAs();\ +// }\ +// void set##getSet(type value) {\ +// auto cw = getContentWrapper();\ +// cw.insertOrReplace(\ +// *cw.getGroupOrFail(DETAILS),\ +// Content{ #name, std::move(value) }\ +// );\ +// } + + + using namespace upvote_election::common; static void validateStartDate(const time_point& startDate) diff --git a/src/upvote_election/up_vote_vote.cpp b/src/upvote_election/up_vote_vote.cpp index 9b04723..8775c32 100644 --- a/src/upvote_election/up_vote_vote.cpp +++ b/src/upvote_election/up_vote_vote.cpp @@ -18,12 +18,6 @@ UpVoteVote::UpVoteVote(dao& dao, uint64_t id) : TypedDocument(dao, id, types::ELECTION_UP_VOTE) {} -// invoke - // UpVoteVote upvote(*this, memberId, UpVoteVoteData{ - // .round_id = static_cast(round_id) - // }); - - UpVoteVote::UpVoteVote(dao& dao, uint64_t group_id, Data data) : TypedDocument(dao, types::ELECTION_UP_VOTE) { From e307a4e918fb880cd84f20b13ad6be930ee898ac Mon Sep 17 00:00:00 2001 From: NIK Date: Tue, 26 Sep 2023 13:46:31 +0800 Subject: [PATCH 19/51] intermediary check in --- include/upvote_election/election_group.hpp | 5 +- include/upvote_election/election_round.hpp | 5 +- include/upvote_election/up_vote_vote.hpp | 3 - include/upvote_election/vote_group.hpp | 1 - src/upvote_election/actions.cpp | 63 ++-- src/upvote_election/election_group.cpp | 4 - src/upvote_election/election_round.cpp | 365 ++++++++++----------- src/upvote_election/vote_group.cpp | 114 +++---- 8 files changed, 274 insertions(+), 286 deletions(-) diff --git a/include/upvote_election/election_group.hpp b/include/upvote_election/election_group.hpp index 783059b..a308af9 100644 --- a/include/upvote_election/election_group.hpp +++ b/include/upvote_election/election_group.hpp @@ -18,14 +18,13 @@ class ElectionGroup : public TypedDocument DECLARE_DOCUMENT( Data, - PROPERTY(member_count, int64_t, MemberCount, USE_GET), - PROPERTY(group_id, int64_t, GroupId, USE_GET), + PROPERTY(member_count, int64_t, MemberCount, USE_GETSET), PROPERTY(winner, int64_t, Winner, USE_GETSET) ) public: ElectionGroup(dao& dao, uint64_t id); - ElectionGroup(dao& dao, uint64_t group_id, std::vector member_ids, Data data); + ElectionGroup(dao& dao, uint64_t round_id, std::vector member_ids, Data data); bool isElectionRoundMember(uint64_t accountId); void vote(int64_t from, int64_t to); diff --git a/include/upvote_election/election_round.hpp b/include/upvote_election/election_round.hpp index 781ac79..38bb720 100644 --- a/include/upvote_election/election_round.hpp +++ b/include/upvote_election/election_round.hpp @@ -33,9 +33,8 @@ class ElectionRound : public TypedDocument void setNextRound(ElectionRound* nextRound) const; std::unique_ptr getNextRound() const; - int64_t getAccountPower(uint64_t accountId); - bool isCandidate(uint64_t accountId); - void addCandidate(uint64_t accountId); + + void addElectionGroup(std::vector accound_ids); private: virtual const std::string buildNodeLabel(ContentGroups &content) override diff --git a/include/upvote_election/up_vote_vote.hpp b/include/upvote_election/up_vote_vote.hpp index 9ebca79..d1978b6 100644 --- a/include/upvote_election/up_vote_vote.hpp +++ b/include/upvote_election/up_vote_vote.hpp @@ -25,11 +25,8 @@ class UpVoteVote : public TypedDocument UpVoteVote(dao& dao, uint64_t id); UpVoteVote(dao& dao, uint64_t group_id, Data data); - // void castVotes(ElectionGroup& group, uint64_t voted); //?? - uint64_t getElectionGroup(); - // static std::optional getFromRound(dao& dao, uint64_t roundId, uint64_t memberId); private: virtual const std::string buildNodeLabel(ContentGroups &content) override { diff --git a/include/upvote_election/vote_group.hpp b/include/upvote_election/vote_group.hpp index 7a1a64a..a7bc757 100644 --- a/include/upvote_election/vote_group.hpp +++ b/include/upvote_election/vote_group.hpp @@ -18,7 +18,6 @@ class VoteGroup : public TypedDocument VoteGroup(dao& dao, uint64_t id); VoteGroup(dao& dao, uint64_t memberId, Data data); - void castVotes(ElectionRound& round, std::vector members); uint64_t getOwner(); static std::optional getFromRound(dao& dao, uint64_t roundId, uint64_t memberId); diff --git a/src/upvote_election/actions.cpp b/src/upvote_election/actions.cpp index 6303344..3eda84d 100644 --- a/src/upvote_election/actions.cpp +++ b/src/upvote_election/actions.cpp @@ -589,7 +589,6 @@ void dao::updateupvelc(uint64_t election_id, bool reschedule) election.setCurrentRound(&startRound); //Setup all candidates - // TODO: randomize this list auto delegates = getGraph().getEdgesFrom(daoId, badges::common::links::DELEGATE); std::vector delegateIds; @@ -597,18 +596,25 @@ void dao::updateupvelc(uint64_t election_id, bool reschedule) for (auto& delegate : delegates) { delegateIds.push_back(delegate.getToNode()); - // TODO - // - // instead of adding delegates to the round, we add them to groups - // and we add the groups to the round - // A round and a group is the same data structure - // only the round has groups, and the group has members. - // - // - startRound.addCandidate(delegate.getToNode()); } - setupCandidates(startRound.getId(), delegateIds); + uint64_t seed = 0; // TODO: Get seed from table + + auto randomIds = shuffleVector(delegateIds, seed); + + auto groups = createGroups(randomIds); + + // we could add this to the round interface - to add groups + for (auto& groupMembers : groups) { + startRound.addElectionGroup(groupMembers); + } + + + + + + // Analuze what this is and if we need it + // setupCandidates(startRound.getId(), delegateIds); scheduleElectionUpdate(*this, election, startRound.getEndDate()); } @@ -639,7 +645,8 @@ void dao::updateupvelc(uint64_t election_id, bool reschedule) // round.addCandidate adds candidates for (auto& winner : winners) { - nextRound->addCandidate(winner); + // TODO: Change all this - Nik + //nextRound->addCandidate(winner); } scheduleElectionUpdate(*this, election, nextRound->getEndDate()); @@ -828,12 +835,12 @@ void dao::castelctnvote(uint64_t round_id, name voter, std::vector vot // auto groupId = getGroup(voter) // - EOS_CHECK( - badges::hasVoterBadge(*this, election.getDaoID(), memberId) || - //Just enable voting to candidates if the round is not the first one - (currentRound.isCandidate(memberId) && currentRound.getDelegatePower() > 0), - "Only registered voters are allowed to perform this action" - ); + // EOS_CHECK( + // badges::hasVoterBadge(*this, election.getDaoID(), memberId) || + // //Just enable voting to candidates if the round is not the first one + // (currentRound.isCandidate(memberId) && currentRound.getDelegatePower() > 0), + // "Only registered voters are allowed to perform this action" + // ); auto votedEdge = Edge::getOrNew(get_self(), get_self(), round_id, memberId, eosio::name("voted")); @@ -842,16 +849,18 @@ void dao::castelctnvote(uint64_t round_id, name voter, std::vector vot return; } - if (auto voteGroup = VoteGroup::getFromRound(*this, round_id, memberId)) { - voteGroup->castVotes(round, std::move(voted)); - } - else { - VoteGroup group(*this, memberId, VoteGroupData{ - .round_id = static_cast(round_id) - }); + // TODO: remove or replace with our voting - group.castVotes(round, std::move(voted)); - } + // if (auto voteGroup = VoteGroup::getFromRound(*this, round_id, memberId)) { + // voteGroup->castVotes(round, std::move(voted)); + // } + // else { + // VoteGroup group(*this, memberId, VoteGroupData{ + // .round_id = static_cast(round_id) + // }); + + // group.castVotes(round, std::move(voted)); + // } } diff --git a/src/upvote_election/election_group.cpp b/src/upvote_election/election_group.cpp index 1d325e6..9380378 100644 --- a/src/upvote_election/election_group.cpp +++ b/src/upvote_election/election_group.cpp @@ -18,15 +18,11 @@ ElectionGroup::ElectionGroup(dao& dao, uint64_t id) {} ElectionGroup::ElectionGroup(dao& dao, uint64_t round_id, std::vector member_ids, Data data) - -//ElectionGroup::ElectionGroup(dao& dao, uint64_t id, Data data) : TypedDocument(dao, types::ELECTION_GROUP) { auto cgs = convert(std::move(data)); - //data.member_count = member_ids.size(); - initializeDocument(dao, cgs); EOS_CHECK(member_ids.size() <= 6, "max 6 members in group"); diff --git a/src/upvote_election/election_round.cpp b/src/upvote_election/election_round.cpp index 57431b2..7a499dc 100644 --- a/src/upvote_election/election_round.cpp +++ b/src/upvote_election/election_round.cpp @@ -1,4 +1,5 @@ #include "upvote_election/election_round.hpp" +#include "upvote_election/election_group.hpp" #include @@ -10,216 +11,204 @@ namespace hypha::upvote_election { -// SET VALUE EXAMPLE - use accessors magically created with the DECLARE_DOCUMENT Macro -// - // DECLARE_DOCUMENT( - // Data, - // PROPERTY(credit, eosio::asset, Credit, USE_GETSET), - // PROPERTY(type, std::string, Type, USE_GETSET) - // ) -//// -//// ... code -// setCredit(std::move(newCredit)); // credit is asset type -// update(); -//// ... -// get -// float offerDisc = 1.0f - offer.getDiscountPercentage() / 10000.f; -// -// -// the magical setters are defined to acess the content group details and get/set -// they don't seem to call update, so update needs to be called. -// -// #define USE_GET_SET_DEC(name, type, getSet)\ -// const type& get##getSet() {\ -// return getContentWrapper()\ -// .getOrFail(DETAILS, #name)\ -// ->getAs();\ -// }\ -// void set##getSet(type value) {\ -// auto cw = getContentWrapper();\ -// cw.insertOrReplace(\ -// *cw.getGroupOrFail(DETAILS),\ -// Content{ #name, std::move(value) }\ -// );\ -// } - - - -using namespace upvote_election::common; - -static void validateStartDate(const time_point& startDate) -{ - //Only valid if start date is in the future - EOS_CHECK( - startDate > eosio::current_time_point(), - _s("Election start date must be in the future") - ) -} - -static void validateEndDate(const time_point& startDate, const time_point& endDate) -{ - //Only valid if start date is in the future - EOS_CHECK( - endDate > startDate, - to_str("End date must happen after start date: ", startDate, " to ", endDate) - ); -} - -ElectionRound::ElectionRound(dao& dao, uint64_t id) - : TypedDocument(dao, id, types::ELECTION_ROUND) -{} - -ElectionRound::ElectionRound(dao& dao, uint64_t election_id, Data data) - : TypedDocument(dao, types::ELECTION_ROUND) -{ - EOS_CHECK( - data.delegate_power >= 0, - "Delegate Power must be greater or equal to 0" - ); - - validateStartDate(data.start_date); - - validateEndDate(data.start_date, data.end_date); - - auto cgs = convert(std::move(data)); - - initializeDocument(dao, cgs); - - auto type = getType(); - - if (type == round_types::CHIEF) { + // SET VALUE EXAMPLE - use accessors magically created with the DECLARE_DOCUMENT Macro + // + // DECLARE_DOCUMENT( + // Data, + // PROPERTY(credit, eosio::asset, Credit, USE_GETSET), + // PROPERTY(type, std::string, Type, USE_GETSET) + // ) + //// + //// ... code + // setCredit(std::move(newCredit)); // credit is asset type + // update(); + //// ... + // get + // float offerDisc = 1.0f - offer.getDiscountPercentage() / 10000.f; + // + // + // the magical setters are defined to acess the content group details and get/set + // they don't seem to call update, so update needs to be called. + // + // #define USE_GET_SET_DEC(name, type, getSet)\ + // const type& get##getSet() {\ + // return getContentWrapper()\ + // .getOrFail(DETAILS, #name)\ + // ->getAs();\ + // }\ + // void set##getSet(type value) {\ + // auto cw = getContentWrapper();\ + // cw.insertOrReplace(\ + // *cw.getGroupOrFail(DETAILS),\ + // Content{ #name, std::move(value) }\ + // );\ + // } + + + + using namespace upvote_election::common; + + static void validateStartDate(const time_point& startDate) + { + //Only valid if start date is in the future EOS_CHECK( - !Edge::getIfExists(dao.get_self(), election_id, links::CHIEF_ROUND).first, - "There is another chief delegate round already" + startDate > eosio::current_time_point(), + _s("Election start date must be in the future") ) + } + + static void validateEndDate(const time_point& startDate, const time_point& endDate) + { + //Only valid if start date is in the future + EOS_CHECK( + endDate > startDate, + to_str("End date must happen after start date: ", startDate, " to ", endDate) + ); + } + + ElectionRound::ElectionRound(dao& dao, uint64_t id) + : TypedDocument(dao, id, types::ELECTION_ROUND) + {} + + ElectionRound::ElectionRound(dao& dao, uint64_t election_id, Data data) + : TypedDocument(dao, types::ELECTION_ROUND) + { + EOS_CHECK( + data.delegate_power >= 0, + "Delegate Power must be greater or equal to 0" + ); + + validateStartDate(data.start_date); + + validateEndDate(data.start_date, data.end_date); + + auto cgs = convert(std::move(data)); + + initializeDocument(dao, cgs); + + auto type = getType(); + + if (type == round_types::CHIEF) { + EOS_CHECK( + !Edge::getIfExists(dao.get_self(), election_id, links::CHIEF_ROUND).first, + "There is another chief delegate round already" + ) + + Edge( + getDao().get_self(), + getDao().get_self(), + election_id, + getId(), + links::CHIEF_ROUND + ); + } + else if (type == round_types::HEAD) { + EOS_CHECK( + !Edge::getIfExists(dao.get_self(), election_id, links::HEAD_ROUND).first, + "There is another head delegate round already" + ) + + Edge( + getDao().get_self(), + getDao().get_self(), + election_id, + getId(), + links::HEAD_ROUND + ); + + EOS_CHECK( + getPassingCount() == 1, + "There can be only 1 Head Delegate" + ) + } Edge( getDao().get_self(), getDao().get_self(), election_id, getId(), - links::CHIEF_ROUND + links::ROUND ); - } - else if (type == round_types::HEAD) { - EOS_CHECK( - !Edge::getIfExists(dao.get_self(), election_id, links::HEAD_ROUND).first, - "There is another head delegate round already" - ) Edge( getDao().get_self(), getDao().get_self(), - election_id, getId(), - links::HEAD_ROUND + election_id, + links::ELECTION ); + } - EOS_CHECK( - getPassingCount() == 1, - "There can be only 1 Head Delegate" - ) + UpvoteElection ElectionRound::getElection() { + return UpvoteElection( + getDao(), + Edge::get(getDao().get_self(), getId(), links::ELECTION).getToNode() + ); } - Edge( - getDao().get_self(), - getDao().get_self(), - election_id, - getId(), - links::ROUND - ); - - Edge( - getDao().get_self(), - getDao().get_self(), - getId(), - election_id, - links::ELECTION - ); -} - -UpvoteElection ElectionRound::getElection() { - return UpvoteElection( - getDao(), - Edge::get(getDao().get_self(), getId(), links::ELECTION).getToNode() - ); -} - -std::vector ElectionRound::getWinners() -{ - std::vector winners; - - dao::election_vote_table elctn_t(getDao().get_self(), getId()); - - auto byAmount = elctn_t.get_index<"byamount"_n>(); - - auto beg = byAmount.rbegin(); - - auto idx = 0; - - while (idx < getPassingCount() && beg != byAmount.rend()) { - winners.push_back(beg->account_id); - ++idx; - ++beg; + std::vector ElectionRound::getWinners() + { + std::vector winners; + + return winners; } - return winners; -} - -void ElectionRound::setNextRound(ElectionRound* nextRound) const -{ - EOS_CHECK( - !getNextRound(), - "Election Round already has next round set" - ); - - Edge( - getDao().get_self(), - getDao().get_self(), - getId(), - nextRound->getId(), - links::NEXT_ROUND - ); -} - -std::unique_ptr ElectionRound::getNextRound() const -{ - if (auto [exists, edge] = Edge::getIfExists(getDao().get_self(), getId(), links::NEXT_ROUND); - exists) { - return std::make_unique(getDao(), edge.getToNode()); + void ElectionRound::setNextRound(ElectionRound* nextRound) const + { + EOS_CHECK( + !getNextRound(), + "Election Round already has next round set" + ); + + Edge( + getDao().get_self(), + getDao().get_self(), + getId(), + nextRound->getId(), + links::NEXT_ROUND + ); } - - return {}; -} - -bool ElectionRound::isCandidate(uint64_t accountId) -{ - return Edge::exists( - getDao().get_self(), - getId(), - accountId, - links::ROUND_CANDIDATE - ); -} - -int64_t ElectionRound::getAccountPower(uint64_t accountId) -{ - if (isCandidate(accountId)) { - return std::max(int64_t{1}, getDelegatePower()); + + std::unique_ptr ElectionRound::getNextRound() const + { + if (auto [exists, edge] = Edge::getIfExists(getDao().get_self(), getId(), links::NEXT_ROUND); + exists) { + return std::make_unique(getDao(), edge.getToNode()); + } + + return {}; } - return 1; -} - -void ElectionRound::addCandidate(uint64_t accountId) -{ - Edge( - getDao().get_self(), - getDao().get_self(), - getId(), - accountId, - links::ROUND_CANDIDATE - ); -} + // bool ElectionRound::isCandidate(uint64_t accountId) + // { + // return Edge::exists( + // getDao().get_self(), + // getId(), + // accountId, + // links::ROUND_CANDIDATE + // ); + // } + + // int64_t ElectionRound::getAccountPower(uint64_t accountId) + // { + // if (isCandidate(accountId)) { + // return std::max(int64_t{1}, getDelegatePower()); + // } + + // return 1; + // } + + void ElectionRound::addElectionGroup(std::vector accound_ids) + { + ElectionGroup electionGroup( + getDao(), + getId(), + accound_ids, + ElectionGroupData{ + .member_count = accound_ids.size() + } + ); + + } } \ No newline at end of file diff --git a/src/upvote_election/vote_group.cpp b/src/upvote_election/vote_group.cpp index 0cff217..d16cb90 100644 --- a/src/upvote_election/vote_group.cpp +++ b/src/upvote_election/vote_group.cpp @@ -74,63 +74,63 @@ std::optional VoteGroup::getFromRound(dao& dao, uint64_t roundId, uin // We can change the meaning of this -void VoteGroup::castVotes(ElectionRound& round, std::vector members) -{ - auto roundId = getRoundID(); - auto power = round.getAccountPower(getOwner()); - - EOS_CHECK( - roundId == round.getId(), - "Missmatch between stored round id and round parameter" - ); - - auto contract = getDao().get_self(); - - //We need to first erase previous votes if any - // Because someone might change their vote - auto prevVotes = getDao().getGraph().getEdgesFrom(getId(), links::VOTE); - - dao::election_vote_table elctn_t(contract, roundId); - - for (auto& edge : prevVotes) { - auto memId = edge.getToNode(); - auto voteEntry = elctn_t.get(memId, "Member entry doesn't exists"); - elctn_t.modify(voteEntry, eosio::same_payer, [&](dao::ElectionVote& vote){ - vote.total_amount -= power; - }); - edge.erase(); - } - - for (auto memId : members) { - //Verify member is a candidate - EOS_CHECK( - round.isCandidate(memId), - "Member must be a candidate to be voted" - ); - - auto voteIt = elctn_t.find(memId); - - if (voteIt != elctn_t.end()) { - elctn_t.modify(voteIt, eosio::same_payer, [&](dao::ElectionVote& vote){ - vote.total_amount += power; - }); - } - else { - elctn_t.emplace(contract, [&](dao::ElectionVote& vote){ - vote.total_amount += power; - vote.account_id = memId; - }); - } - - Edge( - contract, - contract, - getId(), - memId, - links::VOTE - ); - } -} +// void VoteGroup::castVotes(ElectionRound& round, std::vector members) +// { +// auto roundId = getRoundID(); +// auto power = round.getAccountPower(getOwner()); + +// EOS_CHECK( +// roundId == round.getId(), +// "Missmatch between stored round id and round parameter" +// ); + +// auto contract = getDao().get_self(); + +// //We need to first erase previous votes if any +// // Because someone might change their vote +// auto prevVotes = getDao().getGraph().getEdgesFrom(getId(), links::VOTE); + +// dao::election_vote_table elctn_t(contract, roundId); + +// for (auto& edge : prevVotes) { +// auto memId = edge.getToNode(); +// auto voteEntry = elctn_t.get(memId, "Member entry doesn't exists"); +// elctn_t.modify(voteEntry, eosio::same_payer, [&](dao::ElectionVote& vote){ +// vote.total_amount -= power; +// }); +// edge.erase(); +// } + +// for (auto memId : members) { +// //Verify member is a candidate +// EOS_CHECK( +// round.isCandidate(memId), +// "Member must be a candidate to be voted" +// ); + +// auto voteIt = elctn_t.find(memId); + +// if (voteIt != elctn_t.end()) { +// elctn_t.modify(voteIt, eosio::same_payer, [&](dao::ElectionVote& vote){ +// vote.total_amount += power; +// }); +// } +// else { +// elctn_t.emplace(contract, [&](dao::ElectionVote& vote){ +// vote.total_amount += power; +// vote.account_id = memId; +// }); +// } + +// Edge( +// contract, +// contract, +// getId(), +// memId, +// links::VOTE +// ); +// } +// } } From 569e0db906698d018d67741b3ddd56ad36b280b6 Mon Sep 17 00:00:00 2001 From: NIK Date: Tue, 26 Sep 2023 17:00:15 +0800 Subject: [PATCH 20/51] intermediary check in --- include/dao.hpp | 1 - include/upvote_election/common.hpp | 2 +- include/upvote_election/election_group.hpp | 2 + include/upvote_election/election_round.hpp | 4 +- src/CMakeLists.txt | 11 +- src/upvote_election/actions.cpp | 117 ++++----------------- src/upvote_election/election_group.cpp | 1 - src/upvote_election/election_round.cpp | 11 +- src/upvote_election/up_vote_vote.cpp | 2 +- src/upvote_election/upvote_election.cpp | 9 +- 10 files changed, 47 insertions(+), 113 deletions(-) diff --git a/include/dao.hpp b/include/dao.hpp index 5d9222d..71b77df 100644 --- a/include/dao.hpp +++ b/include/dao.hpp @@ -337,7 +337,6 @@ namespace pricing { ACTION editupvelc(uint64_t election_id, ContentGroups& election_config); ACTION cancelupvelc(uint64_t election_id); ACTION updateupvelc(uint64_t election_id, bool reschedule); - ACTION castelctnvote(uint64_t round_id, name voter, std::vector voted); diff --git a/include/upvote_election/common.hpp b/include/upvote_election/common.hpp index 4f64b3f..8e24002 100644 --- a/include/upvote_election/common.hpp +++ b/include/upvote_election/common.hpp @@ -59,7 +59,7 @@ namespace items { inline constexpr auto UPVOTE_STARTDATE = "upvote_start_date_time"; inline constexpr auto UPVOTE_DURATION = "upvote_duration"; inline constexpr auto ROUND_DURATION = "duration"; - inline constexpr auto PASSING_AMOUNT = "passing_count"; + // inline constexpr auto PASSING_AMOUNT = "passing_count"; // TODO: remove inline constexpr auto ROUND_ID = "round_id"; inline constexpr auto ROUND_TYPE = "type"; } diff --git a/include/upvote_election/election_group.hpp b/include/upvote_election/election_group.hpp index a308af9..c24bcb1 100644 --- a/include/upvote_election/election_group.hpp +++ b/include/upvote_election/election_group.hpp @@ -8,6 +8,8 @@ #include +#include "upvote_election/up_vote_vote.hpp" + namespace hypha::upvote_election { diff --git a/include/upvote_election/election_round.hpp b/include/upvote_election/election_round.hpp index 38bb720..075f40c 100644 --- a/include/upvote_election/election_round.hpp +++ b/include/upvote_election/election_round.hpp @@ -19,8 +19,8 @@ class ElectionRound : public TypedDocument PROPERTY(start_date, eosio::time_point, StartDate, USE_GET), PROPERTY(end_date, eosio::time_point, EndDate, USE_GET), PROPERTY(duration, int64_t, Duration, USE_GET), - PROPERTY(delegate_power, int64_t, DelegatePower, USE_GET), - PROPERTY(passing_count, int64_t, PassingCount, USE_GET) + PROPERTY(delegate_power, int64_t, DelegatePower, USE_GET) + // PROPERTY(passing_count, int64_t, PassingCount, USE_GET) //PROPERTY(round_id, int64_t, RoundId, USE_GET) ) public: diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8775286..93f97bc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -18,6 +18,12 @@ add_contract( dao dao recurring_activity.cpp time_share.cpp settings.cpp + upvote_election/actions.cpp + upvote_election/election_round.cpp + upvote_election/election_group.cpp + upvote_election/upvote_election.cpp + upvote_election/up_vote_vote.cpp + upvote_election/vote_group.cpp treasury/actions.cpp treasury/balance.cpp treasury/payment.cpp @@ -58,11 +64,6 @@ add_contract( dao dao comments/comment.cpp comments/section.cpp comments/reaction.cpp - upvote_election/actions.cpp - upvote_election/election_round.cpp - upvote_election/election_group.cpp - upvote_election/upvote_election.cpp - upvote_election/vote_group.cpp ../document-graph/src/document_graph/document_graph.cpp ../document-graph/src/document_graph/document.cpp ../document-graph/src/document_graph/content_wrapper.cpp diff --git a/src/upvote_election/actions.cpp b/src/upvote_election/actions.cpp index 3eda84d..d968144 100644 --- a/src/upvote_election/actions.cpp +++ b/src/upvote_election/actions.cpp @@ -35,6 +35,9 @@ using upvote_election::UpVoteVoteData; namespace upvote_common = upvote_election::common; +// This creates rounds based on the election data - +// We can create a maximum number of rounds from this? +// static std::map getRounds(ContentGroups& electionConfig, time_point& endDate) { auto cw = ContentWrapper(electionConfig); @@ -47,15 +50,15 @@ static std::map getRounds(ContentGroups& electionCon ElectionRoundData data; - data.passing_count = cw.getOrFail( - i, - upvote_common::items::PASSING_AMOUNT - ).second->getAs(); + // data.passing_count = cw.getOrFail( + // i, + // upvote_common::items::PASSING_AMOUNT + // ).second->getAs(); - EOS_CHECK( - data.passing_count >= 1, - "Passing count must be greater or equal to 1" - ) + // EOS_CHECK( + // data.passing_count >= 1, + // "Passing count must be greater or equal to 1" + // ) data.type = cw.getOrFail( i, @@ -108,13 +111,15 @@ static void createRounds(dao& dao, UpvoteElection& election, std::mapgetPassingCount() > electionRound->getPassingCount(), - "Passing count has to be decremental" - ); + // Nik: Not sure what this check is for - we're not using getPassingCount anymore anyway + // EOS_CHECK( + // prevRound->getPassingCount() > electionRound->getPassingCount(), + // "Passing count has to be decremental" + // ); prevRound->setNextRound(electionRound.get()); } @@ -782,85 +788,8 @@ void dao::castupvote(uint64_t round_id, uint64_t group_id, name voter, uint64_t "Only members of the group can be voted for." ); - // group.vote(); - - // EOS_CHECK( - // badges::hasVoterBadge(*this, election.getDaoID(), memberId) || - // //Just enable voting to candidates if the round is not the first one - // (currentRound.isCandidate(memberId) && currentRound.getDelegatePower() > 0), - // "Only registered voters are allowed to perform this action" - // ); - - // auto votedEdge = Edge::getOrNew(get_self(), get_self(), round_id, memberId, eosio::name("voted")); - - // if (votedEdge.empty()) { - // votedEdge.erase(); - // return; - // } - - // if (auto voteGroup = VoteGroup::getFromRound(*this, round_id, memberId)) { - // voteGroup->castVotes(round, std::move(voted)); - // } - // else { - // VoteGroup group(*this, memberId, VoteGroupData{ - // .round_id = static_cast(round_id) - // }); - - // group.castVotes(round, std::move(voted)); - // } -} - -void dao::castelctnvote(uint64_t round_id, name voter, std::vector voted) -{ - eosio::require_auth(voter); - - //TODO: Cancel existing Delegate badges - - //Verify round_id is the same as the current round - ElectionRound round(*this, round_id); - - UpvoteElection election = round.getElection(); - - //Current round has to be defined - auto currentRound = election.getCurrentRound(); - - EOS_CHECK( - currentRound.getId() == round_id, - "You can only vote on the current round" - ); - - auto memberId = getMemberID(voter); - - // TODO: We can probably change this check - we get the - // auto groupId = getGroup(voter) - // - - // EOS_CHECK( - // badges::hasVoterBadge(*this, election.getDaoID(), memberId) || - // //Just enable voting to candidates if the round is not the first one - // (currentRound.isCandidate(memberId) && currentRound.getDelegatePower() > 0), - // "Only registered voters are allowed to perform this action" - // ); - - auto votedEdge = Edge::getOrNew(get_self(), get_self(), round_id, memberId, eosio::name("voted")); - - if (voted.empty()) { - votedEdge.erase(); - return; - } - - // TODO: remove or replace with our voting + group.vote(memberId, voted_id); - // if (auto voteGroup = VoteGroup::getFromRound(*this, round_id, memberId)) { - // voteGroup->castVotes(round, std::move(voted)); - // } - // else { - // VoteGroup group(*this, memberId, VoteGroupData{ - // .round_id = static_cast(round_id) - // }); - - // group.castVotes(round, std::move(voted)); - // } } @@ -921,7 +850,7 @@ void dao::createupvelc(uint64_t dao_id, ContentGroups& election_config) time_point endDate = startDate; //Will calculate also what is the endDate for the upvote election - auto rounds = getRounds(election_config, endDate); + auto roundsMap = getRounds(election_config, endDate); UpvoteElection upvoteElection(*this, dao_id, UpvoteElectionData{ .start_date = startDate, @@ -930,7 +859,7 @@ void dao::createupvelc(uint64_t dao_id, ContentGroups& election_config) .duration = duration }); - createRounds(*this, upvoteElection, rounds, startDate, endDate); + createRounds(*this, upvoteElection, roundsMap, startDate, endDate); scheduleElectionUpdate(*this, upvoteElection, startDate); } diff --git a/src/upvote_election/election_group.cpp b/src/upvote_election/election_group.cpp index 9380378..3cd26fc 100644 --- a/src/upvote_election/election_group.cpp +++ b/src/upvote_election/election_group.cpp @@ -1,7 +1,6 @@ #include "upvote_election/election_group.hpp" #include "upvote_election/upvote_election.hpp" #include "upvote_election/common.hpp" -#include "upvote_election/up_vote_vote.hpp" #include diff --git a/src/upvote_election/election_round.cpp b/src/upvote_election/election_round.cpp index 7a499dc..dbf80e7 100644 --- a/src/upvote_election/election_round.cpp +++ b/src/upvote_election/election_round.cpp @@ -94,6 +94,9 @@ namespace hypha::upvote_election { "There is another chief delegate round already" ) + // TODO: I don't think we need to know what kinds of rounds there are. + // We can estimate the maximum number of rounds but since any group may not have a winner + // it could be over after round 1. Edge( getDao().get_self(), getDao().get_self(), @@ -116,10 +119,10 @@ namespace hypha::upvote_election { links::HEAD_ROUND ); - EOS_CHECK( - getPassingCount() == 1, - "There can be only 1 Head Delegate" - ) + // EOS_CHECK( + // getPassingCount() == 1, + // "There can be only 1 Head Delegate" + // ) } Edge( diff --git a/src/upvote_election/up_vote_vote.cpp b/src/upvote_election/up_vote_vote.cpp index 8775c32..7079841 100644 --- a/src/upvote_election/up_vote_vote.cpp +++ b/src/upvote_election/up_vote_vote.cpp @@ -1,4 +1,4 @@ -#include "upvote_election/vote_group.hpp" +#include "upvote_election/up_vote_vote.hpp" #include "upvote_election/common.hpp" diff --git a/src/upvote_election/upvote_election.cpp b/src/upvote_election/upvote_election.cpp index f817ce3..e3f3e36 100644 --- a/src/upvote_election/upvote_election.cpp +++ b/src/upvote_election/upvote_election.cpp @@ -116,10 +116,11 @@ std::vector UpvoteElection::getRounds() const rounds.emplace_back(getDao(), next->getId()); } - EOS_CHECK( - rounds.size() >= 2, - "There has to be at least 2 election rounds" - ); + // a getter should never throw?! + // EOS_CHECK( + // rounds.size() >= 2, + // "There has to be at least 2 election rounds" + // ); return rounds; } From ca2437eb790d7af8b53df2dbbb7d6b77c3c822e1 Mon Sep 17 00:00:00 2001 From: NIK Date: Tue, 26 Sep 2023 18:49:52 +0800 Subject: [PATCH 21/51] ignoring lib folder - using vs code plugin that added libs --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 9a39e96..74488d1 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ main1.go dao-contracts migration.sh build_ninja +lib From 17d05bf2c73fb89b707e4f9b11b818e686761ddc Mon Sep 17 00:00:00 2001 From: NIK Date: Tue, 26 Sep 2023 19:05:43 +0800 Subject: [PATCH 22/51] intermediary check in --- include/dao.hpp | 1 - include/upvote_election/common.hpp | 1 + src/upvote_election/actions.cpp | 71 ++++++++------------------ src/upvote_election/election_group.cpp | 56 +++++++++++--------- src/upvote_election/election_round.cpp | 12 +++++ 5 files changed, 66 insertions(+), 75 deletions(-) diff --git a/include/dao.hpp b/include/dao.hpp index 71b77df..bc65ad7 100644 --- a/include/dao.hpp +++ b/include/dao.hpp @@ -328,7 +328,6 @@ namespace pricing { #endif #ifdef USE_UPVOTE_ELECTIONS //Upvote System - ACTION startupelc(uint64_t election_id, bool reschedule); ACTION testgrouprng(std::vector ids, uint32_t seed); ACTION testgroupr1(uint32_t num_members, uint32_t seed); ACTION castupvote(uint64_t round_id, uint64_t group_id, name voter, uint64_t voted_id); diff --git a/include/upvote_election/common.hpp b/include/upvote_election/common.hpp index 8e24002..1146c11 100644 --- a/include/upvote_election/common.hpp +++ b/include/upvote_election/common.hpp @@ -62,6 +62,7 @@ namespace items { // inline constexpr auto PASSING_AMOUNT = "passing_count"; // TODO: remove inline constexpr auto ROUND_ID = "round_id"; inline constexpr auto ROUND_TYPE = "type"; + inline constexpr auto WINNER = "winner"; } } // namespace hypha::upvote diff --git a/src/upvote_election/actions.cpp b/src/upvote_election/actions.cpp index d968144..e848a42 100644 --- a/src/upvote_election/actions.cpp +++ b/src/upvote_election/actions.cpp @@ -107,7 +107,7 @@ static void createRounds(dao& dao, UpvoteElection& election, std::map prevRound; - bool hasChief = false; + // bool hasChief = false; // I don't think we need to check rounds.. for (auto& [roundId, roundData] : rounds) { @@ -121,20 +121,20 @@ static void createRounds(dao& dao, UpvoteElection& election, std::mapgetType() == upvote_common::round_types::CHIEF, - "There has to be a Chief round previous to Head Delegate round" - ); - } - else { - EOS_CHECK( - !hasChief, - to_str("Cannot create ", roundData.type, "type rounds after a Chief round") - ); + // if (roundData.type == upvote_common::round_types::HEAD) { + // EOS_CHECK( + // prevRound && prevRound->getType() == upvote_common::round_types::CHIEF, + // "There has to be a Chief round previous to Head Delegate round" + // ); + // } + // else { + // EOS_CHECK( + // !hasChief, + // to_str("Cannot create ", roundData.type, "type rounds after a Chief round") + // ); - hasChief = hasChief || roundData.type == upvote_common::round_types::CHIEF; - } + // hasChief = hasChief || roundData.type == upvote_common::round_types::CHIEF; + // } auto electionRound = std::make_unique( dao, @@ -159,14 +159,14 @@ static void createRounds(dao& dao, UpvoteElection& election, std::mapgetType() == upvote_common::round_types::CHIEF || - prevRound->getType() == upvote_common::round_types::HEAD, - "Last round must be of type Chief or Head" - ) + // EOS_CHECK( + // prevRound->getType() == upvote_common::round_types::CHIEF || + // prevRound->getType() == upvote_common::round_types::HEAD, + // "Last round must be of type Chief or Head" + // ) //Verify we have a chief round - election.getChiefRound(); + // election.getChiefRound(); //At the end both start date and end date should be the same EOS_CHECK( @@ -526,31 +526,6 @@ std::vector> dao::createGroups(const std::vector return groups; } -void dao::startupelc(uint64_t election_id, bool reschedule) -{ - UpvoteElection election(*this, election_id); - - auto status = election.getStatus(); - - auto now = eosio::current_time_point(); - - auto daoId = election.getDaoID(); - - // 1 - randomize - std::vector delegates = getGraph().getEdgesFrom(daoId, badges::common::links::DELEGATE); - - std::vector delegateIDs(delegates.size()); - for (Edge& delegate : delegates) { - delegateIDs.push_back(std::move(delegate.getToNode())); - } - - // eosio::checksum256 checksum = /* Your checksum calculation here */; - // Convert the checksum into a 32-bit integer seed - uint32_t seed = 0; // checksumToUint64(checksum); - - -} - //Check if we need to update an ongoing elections status: //upcoming -> ongoing //ongoing -> finished @@ -582,8 +557,7 @@ void dao::updateupvelc(uint64_t election_id, bool reschedule) //Let's update as we already started if (start <= now) { - - // startUpvoteElection(); + // START.... Edge::get(get_self(), daoId, election.getId(), upvote_common::links::UPCOMING_ELECTION).erase(); @@ -610,7 +584,6 @@ void dao::updateupvelc(uint64_t election_id, bool reschedule) auto groups = createGroups(randomIds); - // we could add this to the round interface - to add groups for (auto& groupMembers : groups) { startRound.addElectionGroup(groupMembers); } @@ -619,7 +592,7 @@ void dao::updateupvelc(uint64_t election_id, bool reschedule) - // Analuze what this is and if we need it + // analyze what this is and if we need it // setupCandidates(startRound.getId(), delegateIds); scheduleElectionUpdate(*this, election, startRound.getEndDate()); diff --git a/src/upvote_election/election_group.cpp b/src/upvote_election/election_group.cpp index 3cd26fc..6693f28 100644 --- a/src/upvote_election/election_group.cpp +++ b/src/upvote_election/election_group.cpp @@ -37,7 +37,6 @@ ElectionGroup::ElectionGroup(dao& dao, uint64_t round_id, std::vector ); } - // Create edge from the round to this group Edge( getDao().get_self(), @@ -47,14 +46,6 @@ ElectionGroup::ElectionGroup(dao& dao, uint64_t round_id, std::vector links::ELECTION_GROUP_LINK ); - // I don't think we need 2 way links everywhere?! - // Edge( - // getDao().get_self(), - // getDao().get_self(), - // getId(), - // election_id, - // links::ELECTION - // ); } bool ElectionGroup::isElectionRoundMember(uint64_t accountId) @@ -92,25 +83,29 @@ void ElectionGroup::vote(int64_t from, int64_t to) int64_t voted_id = upvote.getVotedId(); int64_t voter_id = upvote.getVoterId(); - if (voteCount.find(voted_id) == voteCount.end()) { - voteCount[voted_id] = 1; - } else { - voteCount[voted_id] = voteCount[voted_id] + 1; + // If the voter has already voted before, we change their + // vote to the new voted "to" + if (voter_id == from) { + existingVote = true; + upvote.setVotedId(to); + upvote.update(); + voted_id = to; } + + // Count all votes + // if (voteCount.find(voted_id) == voteCount.end()) { + // voteCount[voted_id] = 1; + // } else { + // voteCount[voted_id] = voteCount[voted_id] + 1; + // } + voteCount[voted_id]++; + // check for winner if (voteCount[voted_id] >= votesToWin) { hasWinner = true; winner = voted_id; } - - // check if we need to change the vote - if (voter_id == from) { - // change vote - existingVote = true; - upvote.setVotedId(to); - upvote.update(); - } } if (!existingVote) { @@ -119,11 +114,22 @@ void ElectionGroup::vote(int64_t from, int64_t to) .voter_id = from, .voted_id = to }); - // edge case - can't happen in upvote elections - // if (votesToWin == 1) { - // hasWinner = true; - // winner = to; + + // Count + // if (voteCount.find(to) == voteCount.end()) { + // voteCount[to] = 1; + // } else { + // voteCount[to] = voteCount[to] + 1; // } + voteCount[to]++; + + // check for winner + if (voteCount[to] >= votesToWin) { + hasWinner = true; + winner = to; + } + + } if (hasWinner) { diff --git a/src/upvote_election/election_round.cpp b/src/upvote_election/election_round.cpp index dbf80e7..b55fe68 100644 --- a/src/upvote_election/election_round.cpp +++ b/src/upvote_election/election_round.cpp @@ -2,6 +2,8 @@ #include "upvote_election/election_group.hpp" #include +// #include +// #include #include "upvote_election/common.hpp" @@ -153,6 +155,16 @@ namespace hypha::upvote_election { { std::vector winners; + std::vector groupEdges = getDao().getGraph().getEdgesFrom(getId(), links::ELECTION_GROUP_LINK); + for (auto& edge : groupEdges) { + ElectionGroup group(getDao(), edge.getToNode()); + auto cw = group.getDocument().getContentWrapper(); + auto [idx, item] = cw.get(DETAILS, items::WINNER); + if (idx != -1) { + winners.push_back(item->getAs()); + } + } + return winners; } From 1603d53260691793c969220b514154f9779ba221 Mon Sep 17 00:00:00 2001 From: NIK Date: Wed, 27 Sep 2023 14:46:40 +0800 Subject: [PATCH 23/51] added edenia code to calculate group sizes --- include/dao.hpp | 2 +- src/upvote_election/actions.cpp | 230 ++++++++++++++++++++++++++------ 2 files changed, 192 insertions(+), 40 deletions(-) diff --git a/include/dao.hpp b/include/dao.hpp index bc65ad7..0ffe813 100644 --- a/include/dao.hpp +++ b/include/dao.hpp @@ -506,7 +506,7 @@ namespace pricing { private: std::vector shuffleVector(std::vector& ids, uint32_t seed); - std::vector> createGroups(const std::vector& ids); + std::vector> createGroups(const std::vector& ids, int minGroupSize); void onRewardTransfer(const name& from, const name& to, const asset& amount); diff --git a/src/upvote_election/actions.cpp b/src/upvote_election/actions.cpp index e848a42..0ede080 100644 --- a/src/upvote_election/actions.cpp +++ b/src/upvote_election/actions.cpp @@ -35,6 +35,92 @@ using upvote_election::UpVoteVoteData; namespace upvote_common = upvote_election::common; + + static uint32_t int_pow(uint32_t base, uint32_t exponent) + { + uint32_t result = 1; + for (uint32_t i = 0; i < exponent; ++i) + { + result *= base; + } + return result; + } + + // From Eden Code + static uint32_t int_root(uint32_t x, uint32_t y) + { + // find z, such that $z^y \le x < (z+1)^y$ + // + // hard coded limits based on the election constraints + uint32_t low = 0, high = 12; + while (high - low > 1) + { + uint32_t mid = (high + low) / 2; + if (x < int_pow(mid, y)) + { + high = mid; + } + else + { + low = mid; + } + } + return low; + } + + static std::size_t count_rounds(uint32_t num_members) + { + std::size_t result = 1; + for (uint32_t i = 12; i <= num_members; i *= 4) + { + ++result; + } + return result; + } + + static std::vector get_group_sizes(uint32_t num_members, std::size_t num_rounds) + { + auto basic_group_size = int_root(num_members, num_rounds); + if (basic_group_size == 3) + { + std::vector result(num_rounds, 4); + // result.front() is always 4, but for some reason, that causes clang to miscompile this. + // TODO: look for UB... + auto large_rounds = + static_cast(std::log(static_cast(num_members) / + int_pow(result.front(), num_rounds - 1) / 3) / + std::log(1.25)); + result.back() = 3; + eosio::check(large_rounds <= 1, + "More that one large round is unexpected when the final group size is 3."); + for (std::size_t i = result.size() - large_rounds - 1; i < result.size() - 1; ++i) + { + result[i] = 5; + } + return result; + } + else if (basic_group_size >= 6) + { + // 5,6,...,6,N + std::vector result(num_rounds, 6); + result.front() = 5; + auto divisor = int_pow(6, num_rounds - 1); + result.back() = (num_members + divisor - 1) / divisor; + return result; + } + else + { + // \lfloor \log_{(G+1)/G}\frac{N}{G^R} \rfloor + auto large_rounds = static_cast( + std::log(static_cast(num_members) / int_pow(basic_group_size, num_rounds)) / + std::log((basic_group_size + 1.0) / basic_group_size)); + // x,x,x,x,x,x + std::vector result(num_rounds, basic_group_size + 1); + std::fill_n(result.begin(), num_rounds - large_rounds, basic_group_size); + return result; + } + } + // This creates rounds based on the election data - // We can create a maximum number of rounds from this? // @@ -411,6 +497,8 @@ uint64_t sha256ToUint64(const eosio::checksum256& sha256Hash) { /// @brief Test random group creation /// @param ids void dao::testgroupr1(uint32_t num_members, uint32_t seed) { + + require_auth(get_self()); std::vector ids(num_members); // Initialize a vector with 100 elements @@ -420,6 +508,23 @@ void dao::testgroupr1(uint32_t num_members, uint32_t seed) { ids[i] = static_cast(i); } testgrouprng(ids, seed); + + // for (uint32_t n = 0; n < num_members; ++n) { + // auto rounds = count_rounds(n); + + // std::vector group_sizes = get_group_sizes(n, rounds); + + // eosio::print(" N: ", n, " rounds: ", rounds, " g_sizes: "); + + // for (uint32_t s : group_sizes) { + // eosio::print(s, " "); + // } + + // eosio::print(" ::: "); + + + // } + } @@ -427,28 +532,37 @@ void dao::testgrouprng(std::vector ids, uint32_t seed) { require_auth(get_self()); - // eosio::print(" ids: "); - // for (const uint64_t& element : ids) { - // eosio::print(element, " "); - // } + eosio::print(" ids: "); + for (const uint64_t& element : ids) { + eosio::print(element, " "); + } auto randomIds = shuffleVector(ids, seed); - // eosio::print(" random ids: "); - // for (const uint64_t& element : randomIds) { - // eosio::print(element, " "); - // } - - auto groups = createGroups(randomIds); + eosio::print(" random ids: "); + for (const uint64_t& element : randomIds) { + eosio::print(element, " "); + } - // eosio::print(" groups: "); - // for (uint32_t i = 0; i < groups.size(); ++i) { - // auto group = groups[i]; - // eosio::print("group: ", i, "(", group.size(), "): "); - // for (const uint64_t& element : group) { - // eosio::print(element, " "); - // } - // } + //// Eden defines min group size as 4, but depending on + //// the numbers, Edenia code does something slightly different + // int minGroupsSize = 4; + + //// use Edenia code to figure out group size + auto n = ids.size(); + auto rounds = count_rounds(n); + std::vector group_sizes = get_group_sizes(n, rounds); + int minGroupsSize = group_sizes[0]; + auto groups = createGroups(randomIds, minGroupsSize); + + eosio::print(" groups min size: ", minGroupsSize); + for (uint32_t i = 0; i < groups.size(); ++i) { + auto group = groups[i]; + eosio::print(" group: ", i, "(", group.size(), "): "); + for (const uint64_t& element : group) { + eosio::print(element, " "); + } + } } @@ -493,16 +607,58 @@ std::vector dao::shuffleVector(std::vector& ids, uint32_t se } -std::vector> dao::createGroups(const std::vector& ids) { + +// 36 / 6 = 6 => 1 round, 1 HD round +size_t numrounds(size_t num_delegates) { + size_t rounds = 1; + if (num_delegates > 12) { + size_t numGroups = std::ceil(static_cast(num_delegates) / 6); + + } + return rounds; +} + +// From EDEN contracts - decides group max size by num groups and num participants +// struct election_round_config +// { +// uint16_t num_participants; +// uint16_t num_groups; +// constexpr uint8_t group_max_size() const +// { +// return (num_participants + num_groups - 1) / num_groups; +// } +// constexpr uint16_t num_short_groups() const +// { +// return group_max_size() * num_groups - num_participants; +// } + +// constexpr uint32_t num_large_groups() const { return num_groups - num_short_groups(); } +// constexpr uint32_t group_min_size() const { return group_max_size() - 1; } +// uint32_t member_index_to_group(uint32_t idx) const; +// uint32_t group_to_first_member_index(uint32_t idx) const; +// // invariants: +// // num_groups * group_max_size - num_short_groups = num_participants +// // group_max_size <= 12 +// // num_short_groups < num_groups +// }; + + + // Requirements: + // - Except for the last round, the group size shall be in [4,6] + // - The last round has a minimum group size of 3 + // - The maximum group size shall be as small as possible + // - The group sizes within a round shall have a maximum difference of 1 +std::vector> dao::createGroups(const std::vector& ids, int minGroupSize) { std::vector> groups; - // Calculate the number of groups needed - int numGroups = std::ceil(static_cast(ids.size()) / 6); + // Calculate the number of groups needed - group size fills up to 6 max, except when there's + // only 1 group, which goes up to 11 + int numGroups = ids.size() / minGroupSize; - // Create the groups with an initial capacity of 6 + // Create the groups with an initial capacity of 4 for (int i = 0; i < numGroups; ++i) { groups.push_back(std::vector()); - groups.back().reserve(6); + groups.back().reserve(4); } // Initialize group iterators @@ -517,7 +673,7 @@ std::vector> dao::createGroups(const std::vector // Move to the next group (take turns) ++currentGroup; - // Wrap back to the beginning of the groups if needed + // Wrap back to the beginning of the groups if (currentGroup == endGroup) { currentGroup = groups.begin(); } @@ -541,16 +697,16 @@ void dao::updateupvelc(uint64_t election_id, bool reschedule) auto daoId = election.getDaoID(); - auto setupCandidates = [&](uint64_t roundId, const std::vector& members){ - election_vote_table elctn_t(get_self(), roundId); + // auto setupCandidates = [&](uint64_t roundId, const std::vector& members){ + // election_vote_table elctn_t(get_self(), roundId); - for (auto& memId : members) { - elctn_t.emplace(get_self(), [memId](ElectionVote& vote) { - vote.total_amount = 0; - vote.account_id = memId; - }); - } - }; + // for (auto& memId : members) { + // elctn_t.emplace(get_self(), [memId](ElectionVote& vote) { + // vote.total_amount = 0; + // vote.account_id = memId; + // }); + // } + // }; if (status == upvote_common::upvote_status::UPCOMING) { auto start = election.getStartDate(); @@ -582,16 +738,12 @@ void dao::updateupvelc(uint64_t election_id, bool reschedule) auto randomIds = shuffleVector(delegateIds, seed); - auto groups = createGroups(randomIds); + auto groups = createGroups(randomIds, 4); // TODO: CHANGE THIS TO EDENIA CODE for (auto& groupMembers : groups) { startRound.addElectionGroup(groupMembers); } - - - - // analyze what this is and if we need it // setupCandidates(startRound.getId(), delegateIds); @@ -620,7 +772,7 @@ void dao::updateupvelc(uint64_t election_id, bool reschedule) election.setCurrentRound(nextRound.get()); - setupCandidates(nextRound->getId(), winners); + // setupCandidates(nextRound->getId(), winners); // round.addCandidate adds candidates for (auto& winner : winners) { From ce3dd66c092cfce01e212864aca637a378e321a7 Mon Sep 17 00:00:00 2001 From: NIK Date: Wed, 27 Sep 2023 23:52:21 +0800 Subject: [PATCH 24/51] added a modified updateSeed method Based on Edenia code to handle block chain hash --- include/upvote_election/convert.hpp | 120 +++++++ include/upvote_election/for_each_field.hpp | 58 +++ include/upvote_election/from_bin.hpp | 378 ++++++++++++++++++++ include/upvote_election/map_macro.h | 65 ++++ include/upvote_election/reflection.hpp | 73 ++++ include/upvote_election/stream.hpp | 273 ++++++++++++++ include/upvote_election/upvote_election.hpp | 5 + src/upvote_election/actions.cpp | 16 + src/upvote_election/upvote_election.cpp | 370 ++++++++++--------- 9 files changed, 1190 insertions(+), 168 deletions(-) create mode 100644 include/upvote_election/convert.hpp create mode 100644 include/upvote_election/for_each_field.hpp create mode 100644 include/upvote_election/from_bin.hpp create mode 100644 include/upvote_election/map_macro.h create mode 100644 include/upvote_election/reflection.hpp create mode 100644 include/upvote_election/stream.hpp diff --git a/include/upvote_election/convert.hpp b/include/upvote_election/convert.hpp new file mode 100644 index 0000000..6d1caca --- /dev/null +++ b/include/upvote_election/convert.hpp @@ -0,0 +1,120 @@ +#pragma once + +#include +#include +#include +#include +#include +#include "for_each_field.hpp" +#include "stream.hpp" + +namespace eosio +{ + struct no_conversion + { + using reverse = no_conversion; + }; + struct widening_conversion; + // Fields must match exactly + struct strict_conversion + { + using reverse = strict_conversion; + }; + // Can discard some fields + struct narrowing_conversion + { + using reverse = widening_conversion; + }; + // Can default construct some fields + struct widening_conversion + { + using reverse = narrowing_conversion; + }; + + no_conversion conversion_kind(...); + void serialize_as(...); + + template + using serialization_type = decltype(serialize_as(std::declval())); + + template + using conversion_kind_t = std::conditional_t< + std::is_same_v(), std::declval())), + no_conversion>, + typename decltype(conversion_kind(std::declval(), std::declval()))::reverse, + decltype(conversion_kind(std::declval(), std::declval()))>; + + template + auto convert_impl(Field field, const T& src, U& dst, F&& f, int) + -> std::void_t + { + convert(field(&src), field(&dst), f); + } + + template + auto convert_impl(Field field, const T& src, U& dst, F&& f, long) + { + static_assert(!std::is_same_v, strict_conversion>, + "Member not found"); + static_assert(!std::is_same_v, widening_conversion>, + "Member not found"); + } + + inline constexpr auto choose_first = [](auto src, auto dest) { return src; }; + inline constexpr auto choose_second = [](auto src, auto dest) { return dest; }; + + // TODO: add some validation + + template + void convert(const T& src, U& dst, F&& chooser) + { + if constexpr (std::is_same_v) + { + dst = src; + } + else + { + static_assert(!std::is_same_v, no_conversion>, + "Conversion not defined"); + for_each_field>( + [&](const char*, auto field) { convert_impl(field, src, dst, chooser, 0); }); + } + } + + template + void convert(const std::variant& src, U& dst, F&& chooser) + { + std::visit([&](auto& src) { return convert(src, dst, chooser); }, src); + } + + template + void convert(const std::vector& src, std::vector& dst, F&& chooser) + { + dst.resize(src.size()); + for (std::size_t i = 0; i < src.size(); ++i) + { + convert(src[i], dst[i], chooser); + } + } + + template + void convert(const std::optional& src, std::optional& dst, F&& chooser) + { + if (src) + { + dst.emplace(); + convert(*src, *dst, chooser); + } + else + { + dst = std::nullopt; + } + } + + struct stream; + template + void convert(const input_stream& src, std::vector& dst, F&& chooser) + { + dst.assign(src.pos, src.end); + } +} // namespace eosio diff --git a/include/upvote_election/for_each_field.hpp b/include/upvote_election/for_each_field.hpp new file mode 100644 index 0000000..f0c0821 --- /dev/null +++ b/include/upvote_election/for_each_field.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include +#include "reflection.hpp" + +#if __has_include() + +#include + +namespace eosio +{ + template + constexpr auto for_each_field(T&& t, F&& f) + -> std::enable_if_t>> + { + return boost::pfr::for_each_field(static_cast(t), static_cast(f)); + } +} // namespace eosio +#endif + +namespace eosio +{ + template + constexpr auto for_each_field(T&& t, F&& f) + -> std::enable_if_t>> + { + eosio_for_each_field((std::decay_t*)nullptr, [&](const char*, auto member, auto...) { + if constexpr (std::is_member_object_pointer_v) + { + f(t.*member(&t)); + } + }); + } + + template + constexpr void for_each_field(F&& f) + { + eosio_for_each_field((T*)nullptr, [&f](const char* name, auto member, auto...) { + if constexpr (std::is_member_object_pointer_v) + { + f(name, [member](auto p) -> decltype((p->*member(p))) { return p->*member(p); }); + } + }); + } + + // Calls f(#fn_name, &T::fn_name) for every reflected member function of T. + template + constexpr void for_each_method(F&& f) + { + eosio_for_each_field((T*)nullptr, [&f](const char* name, auto member, auto... arg_names) { + if constexpr (std::is_member_function_pointer_v) + { + f(name, member((T*)nullptr), arg_names...); + } + }); + } + +} // namespace eosio diff --git a/include/upvote_election/from_bin.hpp b/include/upvote_election/from_bin.hpp new file mode 100644 index 0000000..e76c7e4 --- /dev/null +++ b/include/upvote_election/from_bin.hpp @@ -0,0 +1,378 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "convert.hpp" +#include "for_each_field.hpp" +#include "stream.hpp" + +namespace eosio +{ + template + void from_bin(T& obj, S& stream); + + template + uint32_t varuint32_from_bin(S& stream) + { + uint32_t result = 0; + int shift = 0; + uint8_t b = 0; + do + { + check(shift < 35, convert_stream_error(stream_error::invalid_varuint_encoding)); + from_bin(b, stream); + result |= uint32_t(b & 0x7f) << shift; + shift += 7; + } while (b & 0x80); + return result; + } + + template + void varuint32_from_bin(uint32_t& dest, S& stream) + { + dest = varuint32_from_bin(stream); + } + + template + uint64_t varuint64_from_bin(S& stream) + { + uint64_t result = 0; + int shift = 0; + uint8_t b = 0; + do + { + check(shift < 70, convert_stream_error(stream_error::invalid_varuint_encoding)); + from_bin(b, stream); + result |= uint64_t(b & 0x7f) << shift; + shift += 7; + } while (b & 0x80); + return result; + } + + template + void varuint64_from_bin(uint64_t& dest, S& stream) + { + dest = varuint64_from_bin(stream); + } + + // zig-zag encoding + template + void varint32_from_bin(int32_t& result, S& stream) + { + uint32_t v; + varuint32_from_bin(v, stream); + if (v & 1) + result = ((~v) >> 1) | 0x8000'0000; + else + result = v >> 1; + } + + // signed leb128 encoding + template + Signed sleb_from_bin(S& stream) + { + using Unsigned = std::make_unsigned_t; + Unsigned result = 0; + int shift = 0; + uint8_t b = 0; + do + { + check(shift < sizeof(Unsigned) * 8, + convert_stream_error(stream_error::invalid_varuint_encoding)); + from_bin(b, stream); + result |= Unsigned(b & 0x7f) << shift; + shift += 7; + } while (b & 0x80); + if (shift < sizeof(Unsigned) * 8 && (b & 0x40)) + result |= -(Unsigned(1) << shift); + return result; + } + + // signed leb128 encoding + template + int64_t sleb64_from_bin(S& stream) + { + return sleb_from_bin(stream); + } + + // signed leb128 encoding + template + int64_t sleb32_from_bin(S& stream) + { + return sleb_from_bin(stream); + } + + template + void from_bin_assoc(T& v, S& stream) + { + uint32_t size; + varuint32_from_bin(size, stream); + for (size_t i = 0; i < size; ++i) + { + typename T::value_type elem; + from_bin(elem, stream); + v.emplace(elem); + } + } + + template + void from_bin_sequence(T& v, S& stream) + { + uint32_t size; + varuint32_from_bin(size, stream); + for (size_t i = 0; i < size; ++i) + { + v.emplace_back(); + from_bin(v.back(), stream); + } + } + + template + void from_bin(T (&v)[N], S& stream) + { + uint32_t size; + varuint32_from_bin(size, stream); + check(size == N, convert_stream_error(stream_error::array_size_mismatch)); + if constexpr (has_bitwise_serialization()) + { + stream.read(reinterpret_cast(v), size * sizeof(T)); + } + else + { + for (size_t i = 0; i < size; ++i) + { + from_bin(v[i], stream); + } + } + } + + template + void from_bin(std::vector& v, S& stream) + { + if constexpr (has_bitwise_serialization()) + { + if constexpr (sizeof(size_t) >= 8) + { + uint64_t size; + varuint64_from_bin(size, stream); + stream.check_available(size * sizeof(T)); + v.resize(size); + stream.read(reinterpret_cast(v.data()), size * sizeof(T)); + } + else + { + uint32_t size; + varuint32_from_bin(size, stream); + stream.check_available(size * sizeof(T)); + v.resize(size); + stream.read(reinterpret_cast(v.data()), size * sizeof(T)); + } + } + else + { + uint32_t size; + varuint32_from_bin(size, stream); + v.resize(size); + for (size_t i = 0; i < size; ++i) + { + from_bin(v[i], stream); + } + } + } + + template + void from_bin(std::set& v, S& stream) + { + return from_bin_assoc(v, stream); + } + + template + void from_bin(std::map& v, S& stream) + { + uint32_t size; + varuint32_from_bin(size, stream); + for (size_t i = 0; i < size; ++i) + { + std::pair elem; + from_bin(elem, stream); + v.emplace(elem); + } + } + + template + void from_bin(std::deque& v, S& stream) + { + return from_bin_sequence(v, stream); + } + + template + void from_bin(std::list& v, S& stream) + { + return from_bin_sequence(v, stream); + } + + template + void from_bin(input_stream& obj, S& stream) + { + if constexpr (sizeof(size_t) >= 8) + { + uint64_t size; + varuint64_from_bin(size, stream); + stream.check_available(size); + stream.read_reuse_storage(obj.pos, size); + obj.end = obj.pos + size; + } + else + { + uint32_t size; + varuint32_from_bin(size, stream); + stream.check_available(size); + stream.read_reuse_storage(obj.pos, size); + obj.end = obj.pos + size; + } + } + + template + void from_bin(std::pair& obj, S& stream) + { + from_bin(obj.first, stream); + from_bin(obj.second, stream); + } + + template + inline void from_bin(std::string& obj, S& stream) + { + uint32_t size; + varuint32_from_bin(size, stream); + obj.resize(size); + stream.read(obj.data(), obj.size()); + } + + template + inline void from_bin(std::string_view& obj, S& stream) + { + uint32_t size; + varuint32_from_bin(size, stream); + obj = std::string_view(stream.get_pos(), size); + stream.skip(size); + } + + template + void from_bin(std::optional& obj, S& stream) + { + bool present; + from_bin(present, stream); + if (!present) + { + obj.reset(); + return; + } + obj.emplace(); + from_bin(*obj, stream); + } + + template + void variant_from_bin(std::variant& v, uint32_t i, S& stream) + { + if constexpr (I < std::variant_size_v>) + { + if (i == I) + { + auto& x = v.template emplace(); + from_bin(x, stream); + } + else + { + variant_from_bin(v, i, stream); + } + } + else + { + check(false, convert_stream_error(stream_error::bad_variant_index)); + } + } + + template + void from_bin(std::variant& obj, S& stream) + { + uint32_t u; + varuint32_from_bin(u, stream); + variant_from_bin<0>(obj, u, stream); + } + + template + void from_bin(std::array& obj, S& stream) + { + for (T& elem : obj) + { + from_bin(elem, stream); + } + } + + template + void from_bin_tuple(T& obj, S& stream) + { + if constexpr (N < std::tuple_size_v) + { + from_bin(std::get(obj), stream); + from_bin_tuple(obj, stream); + } + } + + template + void from_bin(std::tuple& obj, S& stream) + { + return from_bin_tuple<0>(obj, stream); + } + + template + void from_bin(T& obj, S& stream) + { + if constexpr (has_bitwise_serialization()) + { + stream.read(reinterpret_cast(&obj), sizeof(T)); + } + else if constexpr (std::is_same_v, void>) + { + for_each_field(obj, [&](auto& member) { from_bin(member, stream); }); + } + else + { + // TODO: This can operate in place for standard serializers + decltype(serialize_as(obj)) temp; + from_bin(temp, stream); + convert(temp, obj, choose_first); + } + } + + template + T from_bin(S& stream) + { + T obj; + from_bin(obj, stream); + return obj; + } + + template + void convert_from_bin(T& obj, const std::vector& bin) + { + input_stream stream{bin}; + return from_bin(obj, stream); + } + + template + T convert_from_bin(const std::vector& bin) + { + T obj; + convert_from_bin(obj, bin); + return obj; + } + +} // namespace eosio diff --git a/include/upvote_election/map_macro.h b/include/upvote_election/map_macro.h new file mode 100644 index 0000000..347f07a --- /dev/null +++ b/include/upvote_election/map_macro.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2012 William Swanson + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the names of the authors or + * their institutions shall not be used in advertising or otherwise to + * promote the sale, use or other dealings in this Software without + * prior written authorization from the authors. + */ + +/* + * This file has been modified by block.one + */ + +#ifndef EOSIO_MAP_MACRO_H_INCLUDED +#define EOSIO_MAP_MACRO_H_INCLUDED + +#define EOSIO_EVAL0(...) __VA_ARGS__ +#define EOSIO_EVAL1(...) EOSIO_EVAL0(EOSIO_EVAL0(EOSIO_EVAL0(__VA_ARGS__))) +#define EOSIO_EVAL2(...) EOSIO_EVAL1(EOSIO_EVAL1(EOSIO_EVAL1(__VA_ARGS__))) +#define EOSIO_EVAL3(...) EOSIO_EVAL2(EOSIO_EVAL2(EOSIO_EVAL2(__VA_ARGS__))) +#define EOSIO_EVAL4(...) EOSIO_EVAL3(EOSIO_EVAL3(EOSIO_EVAL3(__VA_ARGS__))) +#define EOSIO_EVAL(...) EOSIO_EVAL4(EOSIO_EVAL4(EOSIO_EVAL4(__VA_ARGS__))) + +#define EOSIO_MAP_END(...) +#define EOSIO_MAP_OUT + +#define EOSIO_MAP_GET_END2() 0, EOSIO_MAP_END +#define EOSIO_MAP_GET_END1(...) EOSIO_MAP_GET_END2 +#define EOSIO_MAP_GET_END(...) EOSIO_MAP_GET_END1 +#define EOSIO_MAP_NEXT0(test, next, ...) next EOSIO_MAP_OUT +#define EOSIO_MAP_NEXT1(test, next) EOSIO_MAP_NEXT0(test, next, 0) +#define EOSIO_MAP_NEXT(test, next) EOSIO_MAP_NEXT1(EOSIO_MAP_GET_END test, next) + +// Macros below this point added by block.one + +#define EOSIO_MAP_REUSE_ARG0_0(f, arg0, x, peek, ...) \ + f(arg0, x) EOSIO_MAP_NEXT(peek, EOSIO_MAP_REUSE_ARG0_1)(f, arg0, peek, __VA_ARGS__) +#define EOSIO_MAP_REUSE_ARG0_1(f, arg0, x, peek, ...) \ + f(arg0, x) EOSIO_MAP_NEXT(peek, EOSIO_MAP_REUSE_ARG0_0)(f, arg0, peek, __VA_ARGS__) +// Handle 0 arguments +#define EOSIO_MAP_REUSE_ARG0_I(f, arg0, peek, ...) \ + EOSIO_MAP_NEXT(peek, EOSIO_MAP_REUSE_ARG0_1)(f, arg0, peek, __VA_ARGS__) +#define EOSIO_MAP_REUSE_ARG0(f, ...) \ + EOSIO_EVAL(EOSIO_MAP_REUSE_ARG0_I(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#endif diff --git a/include/upvote_election/reflection.hpp b/include/upvote_election/reflection.hpp new file mode 100644 index 0000000..804d346 --- /dev/null +++ b/include/upvote_election/reflection.hpp @@ -0,0 +1,73 @@ +#pragma once + +#include +#include "map_macro.h" + +namespace eosio +{ + namespace reflection + { + template + struct has_for_each_field + { + private: + struct F + { + template + void operator()(const A&, const B&); + }; + + template + static char test(decltype(eosio_for_each_field((C*)nullptr, std::declval()))*); + + template + static long test(...); + + public: + static constexpr bool value = sizeof(test((void*)nullptr)) == sizeof(char); + }; + + template + inline constexpr bool has_for_each_field_v = has_for_each_field::value; + +#define EOSIO_REFLECT_MEMBER(STRUCT, FIELD) \ + f(#FIELD, [](auto p) -> decltype(&std::decay_t::FIELD) { \ + return &std::decay_t::FIELD; \ + }); + +#define EOSIO_REFLECT_STRIP_BASEbase +#define EOSIO_REFLECT_BASE(STRUCT, BASE) \ + static_assert(std::is_base_of_v, \ + #BASE " is not a base class of " #STRUCT); \ + eosio_for_each_field((EOSIO_REFLECT_STRIP_BASE##BASE*)nullptr, f); + +#define EOSIO_REFLECT_SIGNATURE(STRUCT, ...) \ + [[maybe_unused]] inline const char* get_type_name(STRUCT*) { return #STRUCT; } \ + template \ + constexpr void eosio_for_each_field(STRUCT*, F f) + +/** + * EOSIO_REFLECT(, ...) + * Each parameter should be either the keyword 'base' followed by a base class of the struct or + * an identifier which names a non-static data member of the struct. + */ +#define EOSIO_REFLECT(...) \ + EOSIO_REFLECT_SIGNATURE(__VA_ARGS__) \ + { \ + EOSIO_MAP_REUSE_ARG0(EOSIO_REFLECT_INTERNAL, __VA_ARGS__) \ + } + +// Identity the keyword 'base' followed by at least one token +#define EOSIO_REFLECT_SELECT_I(a, b, c, d, ...) EOSIO_REFLECT_##d +#define EOSIO_REFLECT_IS_BASE() ~, ~ +#define EOSIO_REFLECT_IS_BASE_TESTbase ~, EOSIO_REFLECT_IS_BASE + +#define EOSIO_APPLY(m, x) m x +#define EOSIO_CAT(x, y) x##y +#define EOSIO_REFLECT_INTERNAL(STRUCT, FIELD) \ + EOSIO_APPLY(EOSIO_REFLECT_SELECT_I, \ + (EOSIO_CAT(EOSIO_REFLECT_IS_BASE_TEST, FIELD()), MEMBER, BASE, MEMBER)) \ + (STRUCT, FIELD) + + } // namespace reflection +} // namespace eosio diff --git a/include/upvote_election/stream.hpp b/include/upvote_election/stream.hpp new file mode 100644 index 0000000..1be32e7 --- /dev/null +++ b/include/upvote_election/stream.hpp @@ -0,0 +1,273 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace eosio +{ + enum class stream_error + { + no_error, + overrun, + underrun, + float_error, + varuint_too_big, + invalid_varuint_encoding, + bad_variant_index, + invalid_asset_format, + array_size_mismatch, + invalid_name_char, + invalid_name_char13, + name_too_long, + json_writer_error, // !!! + }; // stream_error + + constexpr inline std::string_view convert_stream_error(stream_error e) + { + switch (e) + { + // clang-format off + case stream_error::no_error: return "No error"; + case stream_error::overrun: return "Stream overrun"; + case stream_error::underrun: return "Stream underrun"; + case stream_error::float_error: return "Float error"; + case stream_error::varuint_too_big: return "Varuint too big"; + case stream_error::invalid_varuint_encoding: return "Invalid varuint encoding"; + case stream_error::bad_variant_index: return "Bad variant index"; + case stream_error::invalid_asset_format: return "Invalid asset format"; + case stream_error::array_size_mismatch: return "T[] size and unpacked size don't match"; + case stream_error::invalid_name_char: return "character is not in allowed character set for names"; + case stream_error::invalid_name_char13: return "thirteenth character in name cannot be a letter that comes after j"; + case stream_error::name_too_long: return "string is too long to be a valid name"; + case stream_error::json_writer_error: return "Error writing json"; + // clang-format on + + default: + return "unknown"; + } + } + + template + constexpr bool has_bitwise_serialization() + { + if constexpr (std::is_arithmetic_v) + { + return true; + } + else if constexpr (std::is_enum_v) + { + static_assert(!std::is_convertible_v>, + "Serializing unscoped enum"); + return true; + } + else + { + return false; + } + } + + template + struct small_buffer + { + char data[max_size]; + char* pos{data}; + + void reverse() { std::reverse(data, pos); } + }; + + struct vector_stream + { + std::vector& data; + vector_stream(std::vector& data) : data(data) {} + + void write(char c) { data.push_back(c); } + void write(const void* src, std::size_t sz) + { + auto s = reinterpret_cast(src); + data.insert(data.end(), s, s + sz); + } + template + void write_raw(const T& v) + { + write(&v, sizeof(v)); + } + }; + + struct string_stream + { + std::string& data; + string_stream(std::string& data) : data(data) {} + + void write(char c) { data.push_back(c); } + void write(const void* src, std::size_t sz) + { + auto s = reinterpret_cast(src); + data.insert(data.end(), s, s + sz); + } + template + void write_raw(const T& v) + { + write(&v, sizeof(v)); + } + }; + + struct fixed_buf_stream + { + char* pos; + char* end; + + fixed_buf_stream(char* pos, size_t size) : pos{pos}, end{pos + size} {} + + void write(char c) + { + check(pos < end, convert_stream_error(stream_error::overrun)); + *pos++ = c; + } + + void write(const void* src, std::size_t sz) + { + check(pos + sz <= end, convert_stream_error(stream_error::overrun)); + memcpy(pos, src, sz); + pos += sz; + } + + template + void write_raw(const T& v) + { + write(&v, sizeof(v)); + } + }; + + struct size_stream + { + size_t size = 0; + + void write(char c) { ++size; } + + void write(const void* src, std::size_t sz) { size += sz; } + + template + void write_raw(const T& v) + { + size += sizeof(v); + } + }; + + template + void write_str(std::string_view str, S& stream) + { + stream.write(str.data(), str.size()); + } + + template + void increase_indent(S&) + { + } + + template + void decrease_indent(S&) + { + } + + template + void write_colon(S& s) + { + s.write(':'); + } + + template + void write_newline(S&) + { + } + + template + struct pretty_stream : Base + { + using Base::Base; + int indent_size = 4; + std::vector current_indent; + }; + + template + void increase_indent(pretty_stream& s) + { + s.current_indent.resize(s.current_indent.size() + s.indent_size, ' '); + } + + template + void decrease_indent(pretty_stream& s) + { + check(s.current_indent.size() >= s.indent_size, convert_stream_error(stream_error::overrun)); + s.current_indent.resize(s.current_indent.size() - s.indent_size); + } + + template + void write_colon(pretty_stream& s) + { + s.write(": ", 2); + } + + template + void write_newline(pretty_stream& s) + { + s.write('\n'); + s.write(s.current_indent.data(), s.current_indent.size()); + } + + struct input_stream + { + const char* pos; + const char* end; + + constexpr input_stream() : pos{nullptr}, end{nullptr} {} + constexpr input_stream(const char* pos, size_t size) : pos{pos}, end{pos + size} {} + constexpr input_stream(const char* pos, const char* end) : pos{pos}, end{end} {} + input_stream(const std::vector& v) : pos{v.data()}, end{v.data() + v.size()} {} + constexpr input_stream(std::string_view v) : pos{v.data()}, end{v.data() + v.size()} {} + constexpr input_stream(const input_stream&) = default; + + input_stream& operator=(const input_stream&) = default; + + size_t remaining() const { return end - pos; } + + void check_available(size_t size) const + { + check(size <= std::size_t(end - pos), convert_stream_error(stream_error::overrun)); + } + + auto get_pos() const { return pos; } + + void read(void* dest, size_t size) + { + check(size <= size_t(end - pos), convert_stream_error(stream_error::overrun)); + memcpy(dest, pos, size); + pos += size; + } + + template + void read_raw(T& dest) + { + read(&dest, sizeof(dest)); + } + + void skip(size_t size) + { + check(size <= size_t(end - pos), convert_stream_error(stream_error::overrun)); + pos += size; + } + + void read_reuse_storage(const char*& result, size_t size) + { + check(size <= size_t(end - pos), convert_stream_error(stream_error::overrun)); + result = pos; + pos += size; + } + }; + +} // namespace eosio diff --git a/include/upvote_election/upvote_election.hpp b/include/upvote_election/upvote_election.hpp index 68d6f1d..c04607d 100644 --- a/include/upvote_election/upvote_election.hpp +++ b/include/upvote_election/upvote_election.hpp @@ -3,6 +3,8 @@ #include #include +// TODO: move all abieos libs into their own folder +#include "stream.hpp" namespace hypha::upvote_election { @@ -14,6 +16,7 @@ class UpvoteElection : public TypedDocument Data, PROPERTY(start_date, eosio::time_point, StartDate, USE_GETSET), PROPERTY(end_date, eosio::time_point, EndDate, USE_GETSET), + PROPERTY(seed, eosio::checksum256, Seed, USE_GETSET), PROPERTY(status, std::string, Status, USE_GETSET), PROPERTY(duration, int64_t, Duration, USE_GETSET) ) @@ -35,6 +38,8 @@ class UpvoteElection : public TypedDocument void setStartRound(ElectionRound* startRound) const; void setCurrentRound(ElectionRound* currenttRound) const; + void updateSeed(eosio::input_stream& bytes); + void validate(); private: virtual const std::string buildNodeLabel(ContentGroups &content) override diff --git a/src/upvote_election/actions.cpp b/src/upvote_election/actions.cpp index 0ede080..1a40c4f 100644 --- a/src/upvote_election/actions.cpp +++ b/src/upvote_election/actions.cpp @@ -36,6 +36,22 @@ using upvote_election::UpVoteVoteData; namespace upvote_common = upvote_election::common; +// we capture a checksum256 seed, then convert it to a uint32_t to use for our rng +// we always store the last seed in the election document, so we can keep using it +// our rng changes the seed on every operation +static uint32_t checksum256_to_uint32(const eosio::checksum256& checksum) { + // Hash the data using SHA-256 + auto hash = eosio::sha256(reinterpret_cast(checksum.extract_as_byte_array().data()), 32); + + // Extract the first 4 bytes and convert to uint32_t + uint32_t result = 0; + for (int i = 0; i < 4; ++i) { + result = (result << 8) | hash.extract_as_byte_array()[i]; + } + + return result; +} + static uint32_t int_pow(uint32_t base, uint32_t exponent) { uint32_t result = 1; diff --git a/src/upvote_election/upvote_election.cpp b/src/upvote_election/upvote_election.cpp index e3f3e36..40e1a3a 100644 --- a/src/upvote_election/upvote_election.cpp +++ b/src/upvote_election/upvote_election.cpp @@ -4,180 +4,214 @@ #include "upvote_election/common.hpp" #include "upvote_election/election_round.hpp" +#include "upvote_election/from_bin.hpp" #include namespace hypha::upvote_election { -using namespace upvote_election::common; - -UpvoteElection::UpvoteElection(dao& dao, uint64_t id) - : TypedDocument(dao, id, types::UPVOTE_ELECTION) -{} - -static void validateStartDate(const time_point& startDate) -{ - //Only valid if start date is in the future - EOS_CHECK( - startDate > eosio::current_time_point(), - _s("Election start date must be in the future") - ) -} - -static void validateEndDate(const time_point& startDate, const time_point& endDate) -{ - //Only valid if start date is in the future - EOS_CHECK( - endDate > startDate, - to_str("End date must happen after start date: ", startDate, " to ", endDate) - ); -} - -UpvoteElection::UpvoteElection(dao& dao, uint64_t dao_id, Data data) - : TypedDocument(dao, types::UPVOTE_ELECTION) -{ - auto cgs = convert(std::move(data)); - - initializeDocument(dao, cgs); - - validate(); - - //Also check there are no ongoing elections - //TODO: Might want to check if there is an ongoing election, it will - //finish before the start date so then it is valid - EOS_CHECK( - dao.getGraph().getEdgesFrom(dao_id, links::ONGOING_ELECTION).empty(), - _s("There is an ongoing election for this DAO") - ) - - //Validate that there are no other upcoming elections - EOS_CHECK( - dao.getGraph().getEdgesFrom(dao_id, links::UPCOMING_ELECTION).empty(), - _s("DAOs can only have 1 upcoming election") - ) - - Edge( - getDao().get_self(), - getDao().get_self(), - getId(), - dao_id, - hypha::common::DAO - ); - - Edge( - getDao().get_self(), - getDao().get_self(), - dao_id, - getId(), - links::ELECTION - ); - - Edge( - getDao().get_self(), - getDao().get_self(), - dao_id, - getId(), - links::UPCOMING_ELECTION - ); -} - -uint64_t UpvoteElection::getDaoID() const -{ - return Edge::get( - getDao().get_self(), - getId(), - hypha::common::DAO - ).getToNode(); -} - -UpvoteElection UpvoteElection::getUpcomingElection(dao& dao, uint64_t dao_id) -{ - return UpvoteElection( - dao, - Edge::get(dao.get_self(), dao_id, links::UPCOMING_ELECTION).getToNode() - ); -} - -std::vector UpvoteElection::getRounds() const -{ - std::vector rounds; - - auto start = Edge::get( - getDao().get_self(), - getId(), - links::START_ROUND - ).getToNode(); - - rounds.emplace_back(getDao(), start); - - std::unique_ptr next; - - while((next = rounds.back().getNextRound())) { - rounds.emplace_back(getDao(), next->getId()); + using namespace upvote_election::common; + + UpvoteElection::UpvoteElection(dao& dao, uint64_t id) + : TypedDocument(dao, id, types::UPVOTE_ELECTION) + {} + + static void validateStartDate(const time_point& startDate) + { + //Only valid if start date is in the future + EOS_CHECK( + startDate > eosio::current_time_point(), + _s("Election start date must be in the future") + ) + } + + static void validateEndDate(const time_point& startDate, const time_point& endDate) + { + //Only valid if start date is in the future + EOS_CHECK( + endDate > startDate, + to_str("End date must happen after start date: ", startDate, " to ", endDate) + ); + } + + UpvoteElection::UpvoteElection(dao& dao, uint64_t dao_id, Data data) + : TypedDocument(dao, types::UPVOTE_ELECTION) + { + auto cgs = convert(std::move(data)); + + initializeDocument(dao, cgs); + + validate(); + + //Also check there are no ongoing elections + //TODO: Might want to check if there is an ongoing election, it will + //finish before the start date so then it is valid + EOS_CHECK( + dao.getGraph().getEdgesFrom(dao_id, links::ONGOING_ELECTION).empty(), + _s("There is an ongoing election for this DAO") + ) + + //Validate that there are no other upcoming elections + EOS_CHECK( + dao.getGraph().getEdgesFrom(dao_id, links::UPCOMING_ELECTION).empty(), + _s("DAOs can only have 1 upcoming election") + ) + + Edge( + getDao().get_self(), + getDao().get_self(), + getId(), + dao_id, + hypha::common::DAO + ); + + Edge( + getDao().get_self(), + getDao().get_self(), + dao_id, + getId(), + links::ELECTION + ); + + Edge( + getDao().get_self(), + getDao().get_self(), + dao_id, + getId(), + links::UPCOMING_ELECTION + ); + } + + uint64_t UpvoteElection::getDaoID() const + { + return Edge::get( + getDao().get_self(), + getId(), + hypha::common::DAO + ).getToNode(); + } + + UpvoteElection UpvoteElection::getUpcomingElection(dao& dao, uint64_t dao_id) + { + return UpvoteElection( + dao, + Edge::get(dao.get_self(), dao_id, links::UPCOMING_ELECTION).getToNode() + ); + } + + std::vector UpvoteElection::getRounds() const + { + std::vector rounds; + + auto start = Edge::get( + getDao().get_self(), + getId(), + links::START_ROUND + ).getToNode(); + + rounds.emplace_back(getDao(), start); + + std::unique_ptr next; + + while ((next = rounds.back().getNextRound())) { + rounds.emplace_back(getDao(), next->getId()); + } + + // a getter should never throw?! + // EOS_CHECK( + // rounds.size() >= 2, + // "There has to be at least 2 election rounds" + // ); + + return rounds; + } + + void UpvoteElection::setStartRound(ElectionRound* startRound) const + { + //TODO: Check if there is no start round already + + Edge( + getDao().get_self(), + getDao().get_self(), + getId(), + startRound->getId(), + links::START_ROUND + ); + } + + void UpvoteElection::setCurrentRound(ElectionRound* currenttRound) const + { + Edge( + getDao().get_self(), + getDao().get_self(), + getId(), + currenttRound->getId(), + links::CURRENT_ROUND + ); + } + + void UpvoteElection::updateSeed(eosio::input_stream& bytes) + // This method is based on Edenia code + // Modified to work with older eosio cdt libraries. + { + auto electionStartDate = getStartDate(); + eosio::time_point_sec electionStartDateSec = electionStartDate; + + // TODO: Change start and end time for the BTC block to the actual start and end times + eosio::time_point_sec endTimeSec = electionStartDateSec; + eosio::time_point_sec startTimeSec = electionStartDateSec - 86400; // 24 h before + + auto current = getSeed(); + + eosio::check(bytes.remaining() >= 80, "Stream overrun"); + auto hash1 = eosio::sha256(bytes.pos, 80); + auto hash2 = eosio::sha256(reinterpret_cast(hash1.extract_as_byte_array().data()), 32); + // NOTE: OG Edenia code! I guess reverse because of endian issues. + std::reverse((unsigned char*)&hash2, (unsigned char*)(&hash2 + 1)); + eosio::check(hash2 < current, "New seed block must have greater POW than previous seed."); + + uint32_t utc_seconds; + bytes.skip(4 + 32 + 32); + eosio::from_bin(utc_seconds, bytes); + eosio::time_point_sec block_time(utc_seconds); + bytes.skip(4 + 4); + eosio::check(block_time >= endTimeSec, "Seed block is too early"); + eosio::check(block_time < startTimeSec, "Seed block is too late"); + + setSeed(hash2); + update(); } - // a getter should never throw?! - // EOS_CHECK( - // rounds.size() >= 2, - // "There has to be at least 2 election rounds" - // ); - - return rounds; -} - -void UpvoteElection::setStartRound(ElectionRound* startRound) const -{ - //TODO: Check if there is no start round already - - Edge( - getDao().get_self(), - getDao().get_self(), - getId(), - startRound->getId(), - links::START_ROUND - ); -} - -void UpvoteElection::setCurrentRound(ElectionRound* currenttRound) const -{ - Edge( - getDao().get_self(), - getDao().get_self(), - getId(), - currenttRound->getId(), - links::CURRENT_ROUND - ); -} - -ElectionRound UpvoteElection::getStartRound() const -{ - return ElectionRound( - getDao(), - Edge::get(getDao().get_self(), getId(), links::START_ROUND).getToNode() - ); -} - -ElectionRound UpvoteElection::getCurrentRound() const -{ - return ElectionRound( - getDao(), - Edge::get(getDao().get_self(), getId(), links::CURRENT_ROUND).getToNode() - ); -} - -ElectionRound UpvoteElection::getChiefRound() const -{ - return ElectionRound( - getDao(), - Edge::get(getDao().get_self(), getId(), links::CHIEF_ROUND).getToNode() - ); -} - -void UpvoteElection::validate() -{ - auto startDate = getStartDate(); - validateStartDate(startDate); - validateEndDate(startDate, getEndDate()); -} + + ElectionRound UpvoteElection::getStartRound() const + { + return ElectionRound( + getDao(), + Edge::get(getDao().get_self(), getId(), links::START_ROUND).getToNode() + ); + } + + ElectionRound UpvoteElection::getCurrentRound() const + { + return ElectionRound( + getDao(), + Edge::get(getDao().get_self(), getId(), links::CURRENT_ROUND).getToNode() + ); + } + + ElectionRound UpvoteElection::getChiefRound() const + { + return ElectionRound( + getDao(), + Edge::get(getDao().get_self(), getId(), links::CHIEF_ROUND).getToNode() + ); + } + + void UpvoteElection::validate() + { + auto startDate = getStartDate(); + validateStartDate(startDate); + validateEndDate(startDate, getEndDate()); + } } \ No newline at end of file From 08c8f9e2a50b183aa1ba60870ff7f757d4e821e8 Mon Sep 17 00:00:00 2001 From: NIK Date: Thu, 28 Sep 2023 13:07:49 +0800 Subject: [PATCH 25/51] winner needs to have voted for themselves --- src/upvote_election/election_group.cpp | 36 +++++++++----------------- 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/src/upvote_election/election_group.cpp b/src/upvote_election/election_group.cpp index 6693f28..b1cfdd7 100644 --- a/src/upvote_election/election_group.cpp +++ b/src/upvote_election/election_group.cpp @@ -72,10 +72,10 @@ void ElectionGroup::vote(int64_t from, int64_t to) // iterate over vote edges - calculate who has the most votes and find the "from" edge std::unordered_map voteCount; + std::unordered_map selfVotes; bool existingVote = false; - bool hasWinner = false; - int64_t winner = 0; + int64_t majorityWinner = -1; for (auto& edge : voteEdges) { UpVoteVote upvote(getDao(), edge.getToNode()); @@ -92,19 +92,16 @@ void ElectionGroup::vote(int64_t from, int64_t to) voted_id = to; } - - // Count all votes - // if (voteCount.find(voted_id) == voteCount.end()) { - // voteCount[voted_id] = 1; - // } else { - // voteCount[voted_id] = voteCount[voted_id] + 1; - // } voteCount[voted_id]++; // check for winner if (voteCount[voted_id] >= votesToWin) { - hasWinner = true; - winner = voted_id; + majorityWinner = voted_id; + } + + // keep track of self votes + if (voted_id == voter_id) { + selfVotes[voted_id] = true; } } @@ -115,25 +112,16 @@ void ElectionGroup::vote(int64_t from, int64_t to) .voted_id = to }); - // Count - // if (voteCount.find(to) == voteCount.end()) { - // voteCount[to] = 1; - // } else { - // voteCount[to] = voteCount[to] + 1; - // } voteCount[to]++; // check for winner if (voteCount[to] >= votesToWin) { - hasWinner = true; - winner = to; + majorityWinner = to; } - - } - - if (hasWinner) { - setWinner(winner); + + if (majorityWinner > 0 && selfVotes[majorityWinner]) { + setWinner(majorityWinner); update(); } From d052172000bf2e3ebd46eda8004b0c98fbc6a628 Mon Sep 17 00:00:00 2001 From: NIK Date: Fri, 29 Sep 2023 12:19:24 +0800 Subject: [PATCH 26/51] intermediary check in --- include/upvote_election/common.hpp | 6 +- include/upvote_election/election_round.hpp | 4 +- include/upvote_election/upvote_election.hpp | 8 +- include/upvote_election/vote_group.hpp | 50 ++--- src/CMakeLists.txt | 1 - src/upvote_election/actions.cpp | 77 +++---- src/upvote_election/election_group.cpp | 5 +- src/upvote_election/election_round.cpp | 58 +---- src/upvote_election/up_vote_vote.cpp | 79 ------- src/upvote_election/upvote_election.cpp | 56 +++-- src/upvote_election/vote_group.cpp | 225 ++++++++++---------- templates/cmake/CMakeLists_src.dune.txt | 1 - templates/cmake/CMakeLists_src.local.txt | 1 - 13 files changed, 215 insertions(+), 356 deletions(-) diff --git a/include/upvote_election/common.hpp b/include/upvote_election/common.hpp index 1146c11..a6afeb9 100644 --- a/include/upvote_election/common.hpp +++ b/include/upvote_election/common.hpp @@ -5,6 +5,7 @@ namespace hypha::upvote_election::common { namespace round_types { + // TODO: We can remove round types - not needed inline constexpr auto DELEGATE = "delegate"; inline constexpr auto CHIEF = "chief"; inline constexpr auto HEAD = "head"; @@ -39,10 +40,8 @@ namespace links { inline constexpr auto ELECTION = eosio::name("election"); inline constexpr auto START_ROUND = eosio::name("startround"); inline constexpr auto CURRENT_ROUND = eosio::name("currentround"); - inline constexpr auto CHIEF_ROUND = eosio::name("chiefround"); + inline constexpr auto ELECTION_ROUND = eosio::name("ue.round"); inline constexpr auto ELECTION_ROUND_MEMBER = eosio::name("ue.rd.member"); - inline constexpr auto HEAD_ROUND = eosio::name("headround"); - inline constexpr auto ROUND = eosio::name("round"); inline constexpr auto ELECTION_GROUP_LINK = eosio::name("ue.group.lnk"); inline constexpr auto NEXT_ROUND = eosio::name("nextround"); inline constexpr auto ROUND_CANDIDATE = eosio::name("candidate"); @@ -59,7 +58,6 @@ namespace items { inline constexpr auto UPVOTE_STARTDATE = "upvote_start_date_time"; inline constexpr auto UPVOTE_DURATION = "upvote_duration"; inline constexpr auto ROUND_DURATION = "duration"; - // inline constexpr auto PASSING_AMOUNT = "passing_count"; // TODO: remove inline constexpr auto ROUND_ID = "round_id"; inline constexpr auto ROUND_TYPE = "type"; inline constexpr auto WINNER = "winner"; diff --git a/include/upvote_election/election_round.hpp b/include/upvote_election/election_round.hpp index 075f40c..d044d76 100644 --- a/include/upvote_election/election_round.hpp +++ b/include/upvote_election/election_round.hpp @@ -18,10 +18,8 @@ class ElectionRound : public TypedDocument PROPERTY(type, std::string, Type, USE_GET), PROPERTY(start_date, eosio::time_point, StartDate, USE_GET), PROPERTY(end_date, eosio::time_point, EndDate, USE_GET), - PROPERTY(duration, int64_t, Duration, USE_GET), + PROPERTY(round_duration, int64_t, RoundDuration, USE_GET), PROPERTY(delegate_power, int64_t, DelegatePower, USE_GET) - // PROPERTY(passing_count, int64_t, PassingCount, USE_GET) - //PROPERTY(round_id, int64_t, RoundId, USE_GET) ) public: ElectionRound(dao& dao, uint64_t id); diff --git a/include/upvote_election/upvote_election.hpp b/include/upvote_election/upvote_election.hpp index c04607d..cd566fb 100644 --- a/include/upvote_election/upvote_election.hpp +++ b/include/upvote_election/upvote_election.hpp @@ -18,7 +18,8 @@ class UpvoteElection : public TypedDocument PROPERTY(end_date, eosio::time_point, EndDate, USE_GETSET), PROPERTY(seed, eosio::checksum256, Seed, USE_GETSET), PROPERTY(status, std::string, Status, USE_GETSET), - PROPERTY(duration, int64_t, Duration, USE_GETSET) + PROPERTY(duration, int64_t, Duration, USE_GETSET), + PROPERTY(round_duration, int64_t, RoundDuration, USE_GET) ) public: UpvoteElection(dao& dao, uint64_t id); @@ -28,12 +29,11 @@ class UpvoteElection : public TypedDocument uint64_t getDaoID() const; - std::vector getRounds() const; + ElectionRound addRound(); - //std::unique_ptr getStartRound(); + std::vector getRounds() const; ElectionRound getCurrentRound() const; ElectionRound getStartRound() const; - ElectionRound getChiefRound() const; void setStartRound(ElectionRound* startRound) const; void setCurrentRound(ElectionRound* currenttRound) const; diff --git a/include/upvote_election/vote_group.hpp b/include/upvote_election/vote_group.hpp index a7bc757..2aced23 100644 --- a/include/upvote_election/vote_group.hpp +++ b/include/upvote_election/vote_group.hpp @@ -1,33 +1,33 @@ -#pragma once +// #pragma once -#include -#include +// #include +// #include -namespace hypha::upvote_election -{ +// namespace hypha::upvote_election +// { -class ElectionRound; +// class ElectionRound; -class VoteGroup : public TypedDocument -{ - DECLARE_DOCUMENT( - Data, - PROPERTY(round_id, int64_t, RoundID, USE_GET) - ) -public: - VoteGroup(dao& dao, uint64_t id); - VoteGroup(dao& dao, uint64_t memberId, Data data); +// class VoteGroup : public TypedDocument +// { +// DECLARE_DOCUMENT( +// Data, +// PROPERTY(round_id, int64_t, RoundID, USE_GET) +// ) +// public: +// VoteGroup(dao& dao, uint64_t id); +// VoteGroup(dao& dao, uint64_t memberId, Data data); - uint64_t getOwner(); +// uint64_t getOwner(); - static std::optional getFromRound(dao& dao, uint64_t roundId, uint64_t memberId); -private: - virtual const std::string buildNodeLabel(ContentGroups &content) override - { - return "Vote Group"; - } -}; +// static std::optional getFromRound(dao& dao, uint64_t roundId, uint64_t memberId); +// private: +// virtual const std::string buildNodeLabel(ContentGroups &content) override +// { +// return "Vote Group"; +// } +// }; -using VoteGroupData = VoteGroup::Data; +// using VoteGroupData = VoteGroup::Data; -} // namespace hypha::upvote_election \ No newline at end of file +// } // namespace hypha::upvote_election \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 93f97bc..954b059 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -23,7 +23,6 @@ add_contract( dao dao upvote_election/election_group.cpp upvote_election/upvote_election.cpp upvote_election/up_vote_vote.cpp - upvote_election/vote_group.cpp treasury/actions.cpp treasury/balance.cpp treasury/payment.cpp diff --git a/src/upvote_election/actions.cpp b/src/upvote_election/actions.cpp index 1a40c4f..e2a96fa 100644 --- a/src/upvote_election/actions.cpp +++ b/src/upvote_election/actions.cpp @@ -1,7 +1,6 @@ #include #include "upvote_election/upvote_election.hpp" #include "upvote_election/election_round.hpp" -#include "upvote_election/vote_group.hpp" #include "upvote_election/election_group.hpp" #include "upvote_election/up_vote_vote.hpp" @@ -28,8 +27,6 @@ using upvote_election::ElectionRound; using upvote_election::ElectionRoundData; using upvote_election::ElectionGroup; using upvote_election::ElectionGroupData; -using upvote_election::VoteGroup; -using upvote_election::VoteGroupData; using upvote_election::UpVoteVote; using upvote_election::UpVoteVoteData; @@ -139,7 +136,8 @@ static uint32_t checksum256_to_uint32(const eosio::checksum256& checksum) { // This creates rounds based on the election data - // We can create a maximum number of rounds from this? -// +// Or else just create 1 round at a time. +// parsing extermal document static std::map getRounds(ContentGroups& electionConfig, time_point& endDate) { auto cw = ContentWrapper(electionConfig); @@ -152,16 +150,6 @@ static std::map getRounds(ContentGroups& electionCon ElectionRoundData data; - // data.passing_count = cw.getOrFail( - // i, - // upvote_common::items::PASSING_AMOUNT - // ).second->getAs(); - - // EOS_CHECK( - // data.passing_count >= 1, - // "Passing count must be greater or equal to 1" - // ) - data.type = cw.getOrFail( i, upvote_common::items::ROUND_TYPE @@ -190,7 +178,7 @@ static std::map getRounds(ContentGroups& electionCon upvote_common::items::ROUND_DURATION ).second->getAs(); - data.duration = duration; + data.round_duration = duration; endDate += eosio::seconds(duration); @@ -205,6 +193,9 @@ static constexpr int64_t getDelegatePower(int64_t roundId) { return roundId * 1 << roundId; } +// move out this code - only create a start round +// then add more rounds as needed +// Analyze election round data and see what we need - start and end date for sure static void createRounds(dao& dao, UpvoteElection& election, std::map& rounds, time_point startDate, time_point endDate) { std::unique_ptr prevRound; @@ -221,7 +212,7 @@ static void createRounds(dao& dao, UpvoteElection& election, std::mapgetAs(); - time_point endDate = startDate; + auto round_duration = cw.getOrFail( + DETAILS, + upvote_common::items::ROUND_DURATION + )->getAs(); - //Will calculate also what is the endDate for the upvote election - auto roundsMap = getRounds(election_config, endDate); + time_point endDate = startDate; UpvoteElection upvoteElection(*this, dao_id, UpvoteElectionData{ .start_date = startDate, .end_date = endDate, .status = upvote_common::upvote_status::UPCOMING, - .duration = duration + .duration = duration, + .round_duration = round_duration }); - createRounds(*this, upvoteElection, roundsMap, startDate, endDate); + // add start round + upvoteElection.addRound(); scheduleElectionUpdate(*this, upvoteElection, startDate); } +// TODO combine this with create - so we extract the data only in one place + void dao::editupvelc(uint64_t election_id, ContentGroups& election_config) { //TODO: Change existing delegate badges @@ -1048,7 +1025,9 @@ void dao::editupvelc(uint64_t election_id, ContentGroups& election_config) upvoteElection.validate(); upvoteElection.update(); - createRounds(*this, upvoteElection, rounds, startDate, endDate); + auto startRound = upvoteElection.getStartRound(); + startRound.erase(); + upvoteElection.addRound(); scheduleElectionUpdate(*this, upvoteElection, startDate); } diff --git a/src/upvote_election/election_group.cpp b/src/upvote_election/election_group.cpp index b1cfdd7..250d0e6 100644 --- a/src/upvote_election/election_group.cpp +++ b/src/upvote_election/election_group.cpp @@ -64,8 +64,6 @@ void ElectionGroup::vote(int64_t from, int64_t to) // get all existing vote edges std::vector voteEdges = getDao().getGraph().getEdgesFrom(getId(), links::UP_VOTE_VOTE); - // make sure both members are part of this group - // get the member count auto memcount = getMemberCount(); uint64_t votesToWin = memcount * 2 / 3 + 1; @@ -106,6 +104,9 @@ void ElectionGroup::vote(int64_t from, int64_t to) } if (!existingVote) { + if (from == to) { + selfVotes[from] = true; + } // Create a new vote UpVoteVote vote(getDao(), getId(), UpVoteVoteData{ .voter_id = from, diff --git a/src/upvote_election/election_round.cpp b/src/upvote_election/election_round.cpp index b55fe68..294439d 100644 --- a/src/upvote_election/election_round.cpp +++ b/src/upvote_election/election_round.cpp @@ -90,49 +90,12 @@ namespace hypha::upvote_election { auto type = getType(); - if (type == round_types::CHIEF) { - EOS_CHECK( - !Edge::getIfExists(dao.get_self(), election_id, links::CHIEF_ROUND).first, - "There is another chief delegate round already" - ) - - // TODO: I don't think we need to know what kinds of rounds there are. - // We can estimate the maximum number of rounds but since any group may not have a winner - // it could be over after round 1. - Edge( - getDao().get_self(), - getDao().get_self(), - election_id, - getId(), - links::CHIEF_ROUND - ); - } - else if (type == round_types::HEAD) { - EOS_CHECK( - !Edge::getIfExists(dao.get_self(), election_id, links::HEAD_ROUND).first, - "There is another head delegate round already" - ) - - Edge( - getDao().get_self(), - getDao().get_self(), - election_id, - getId(), - links::HEAD_ROUND - ); - - // EOS_CHECK( - // getPassingCount() == 1, - // "There can be only 1 Head Delegate" - // ) - } - Edge( getDao().get_self(), getDao().get_self(), election_id, getId(), - links::ROUND + links::ELECTION_ROUND ); Edge( @@ -194,25 +157,6 @@ namespace hypha::upvote_election { return {}; } - // bool ElectionRound::isCandidate(uint64_t accountId) - // { - // return Edge::exists( - // getDao().get_self(), - // getId(), - // accountId, - // links::ROUND_CANDIDATE - // ); - // } - - // int64_t ElectionRound::getAccountPower(uint64_t accountId) - // { - // if (isCandidate(accountId)) { - // return std::max(int64_t{1}, getDelegatePower()); - // } - - // return 1; - // } - void ElectionRound::addElectionGroup(std::vector accound_ids) { ElectionGroup electionGroup( diff --git a/src/upvote_election/up_vote_vote.cpp b/src/upvote_election/up_vote_vote.cpp index 7079841..45108db 100644 --- a/src/upvote_election/up_vote_vote.cpp +++ b/src/upvote_election/up_vote_vote.cpp @@ -46,84 +46,5 @@ uint64_t UpVoteVote::getElectionGroup() ).getFromNode(); } -// std::optional UpVoteVote::getFromRound(dao& dao, uint64_t roundId, uint64_t memberId) -// { -// auto groups = dao.getGraph().getEdgesFrom(memberId, common::links::ELECTION_GROUP); - -// for (auto& group : groups) { -// if (Edge::exists(dao.get_self(), group.getToNode(), roundId, links::ROUND)) { -// return UpVoteVote(dao, group.getToNode()); -// } -// } - -// return std::nullopt; -// } - - -// limit the members field to 1 -// check the member is member of the same group -// This is one vote for multiple members in a large group - we won't have this anymore -// Vote group is tied to a single member - -// We can change the meaning of this - -// void UpVoteVote::castVotes(ElectionRound& round, std::vector members) -// { -// auto roundId = getRoundID(); -// auto power = round.getAccountPower(getOwner()); - -// EOS_CHECK( -// roundId == round.getId(), -// "Missmatch between stored round id and round parameter" -// ); - -// auto contract = getDao().get_self(); - -// //We need to first erase previous votes if any -// // Because someone might change their vote -// auto prevVotes = getDao().getGraph().getEdgesFrom(getId(), links::VOTE); - -// dao::election_vote_table elctn_t(contract, roundId); - -// for (auto& edge : prevVotes) { -// auto memId = edge.getToNode(); -// auto voteEntry = elctn_t.get(memId, "Member entry doesn't exists"); -// elctn_t.modify(voteEntry, eosio::same_payer, [&](dao::ElectionVote& vote){ -// vote.total_amount -= power; -// }); -// edge.erase(); -// } - -// for (auto memId : members) { -// //Verify member is a candidate -// EOS_CHECK( -// round.isCandidate(memId), -// "Member must be a candidate to be voted" -// ); - -// auto voteIt = elctn_t.find(memId); - -// if (voteIt != elctn_t.end()) { -// elctn_t.modify(voteIt, eosio::same_payer, [&](dao::ElectionVote& vote){ -// vote.total_amount += power; -// }); -// } -// else { -// elctn_t.emplace(contract, [&](dao::ElectionVote& vote){ -// vote.total_amount += power; -// vote.account_id = memId; -// }); -// } - -// Edge( -// contract, -// contract, -// getId(), -// memId, -// links::VOTE -// ); -// } -// } - } diff --git a/src/upvote_election/upvote_election.cpp b/src/upvote_election/upvote_election.cpp index 40e1a3a..43c425a 100644 --- a/src/upvote_election/upvote_election.cpp +++ b/src/upvote_election/upvote_election.cpp @@ -117,19 +117,49 @@ namespace hypha::upvote_election { rounds.emplace_back(getDao(), next->getId()); } - // a getter should never throw?! - // EOS_CHECK( - // rounds.size() >= 2, - // "There has to be at least 2 election rounds" - // ); - return rounds; } - void UpvoteElection::setStartRound(ElectionRound* startRound) const + ElectionRound UpvoteElection::addRound() { - //TODO: Check if there is no start round already + ElectionRoundData roundData; + roundData.type = round_types::DELEGATE; + roundData.round_duration = getRoundDuration(); + eosio::time_point startDate; + std::unique_ptr currentRound; + + auto [exists, currentRoundEdge] = Edge::getIfExists(getDao().get_self(), getId(), links::CURRENT_ROUND); + if (!exists) { + startDate = getStartDate(); + } else { + currentRound = std::make_unique( + getDao(), + currentRoundEdge.getToNode() + ); + startDate = currentRound->getEndDate(); + //currentRound = std::move(current); + } + roundData.start_date = startDate; + roundData.end_date = startDate + eosio::seconds(roundData.round_duration); + auto round = std::make_unique( + getDao(), + getId(), + roundData + ); + + setEndDate(roundData.end_date); + if (!exists) { + setStartRound(round.get()); + } else { + currentRound->setNextRound(round.get()); + } + } + + + + void UpvoteElection::setStartRound(ElectionRound* startRound) const + { Edge( getDao().get_self(), getDao().get_self(), @@ -195,15 +225,7 @@ namespace hypha::upvote_election { { return ElectionRound( getDao(), - Edge::get(getDao().get_self(), getId(), links::CURRENT_ROUND).getToNode() - ); - } - - ElectionRound UpvoteElection::getChiefRound() const - { - return ElectionRound( - getDao(), - Edge::get(getDao().get_self(), getId(), links::CHIEF_ROUND).getToNode() + Edge::get(getDao().get_self(), getId(), links::ELECTION_ROUND).getToNode() ); } diff --git a/src/upvote_election/vote_group.cpp b/src/upvote_election/vote_group.cpp index d16cb90..1979613 100644 --- a/src/upvote_election/vote_group.cpp +++ b/src/upvote_election/vote_group.cpp @@ -1,136 +1,135 @@ -#include "upvote_election/vote_group.hpp" +// #include "upvote_election/vote_group.hpp" -#include "upvote_election/common.hpp" +// #include "upvote_election/common.hpp" -#include "upvote_election/election_round.hpp" +// #include "upvote_election/election_round.hpp" -#include "dao.hpp" +// #include "dao.hpp" -// this document keeps all votes a member has made in a round -// this will need to change since each member can only vote once in a round now, not as many as they want +// // this document keeps all votes a member has made in a round +// // this will need to change since each member can only vote once in a round now, not as many as they want -namespace hypha::upvote_election { +// namespace hypha::upvote_election { -using namespace upvote_election::common; +// using namespace upvote_election::common; -VoteGroup::VoteGroup(dao& dao, uint64_t id) - : TypedDocument(dao, id, types::ELECTION_VOTE_GROUP) -{} +// VoteGroup::VoteGroup(dao& dao, uint64_t id) +// : TypedDocument(dao, id, types::ELECTION_VOTE_GROUP) +// {} -VoteGroup::VoteGroup(dao& dao, uint64_t memberId, Data data) - : TypedDocument(dao, types::ELECTION_VOTE_GROUP) -{ - auto cgs = convert(std::move(data)); - - initializeDocument(dao, cgs); - - Edge( - getDao().get_self(), - getDao().get_self(), - memberId, - getId(), - links::ELECTION_GROUP - ); - - Edge( - getDao().get_self(), - getDao().get_self(), - //memberId, - getId(), - getRoundID(), - links::ROUND - //name(getRoundID()) - ); -} - -uint64_t VoteGroup::getOwner() -{ - return Edge::getTo( - getDao().get_self(), - getId(), - links::ELECTION_GROUP - ).getFromNode(); -} - -std::optional VoteGroup::getFromRound(dao& dao, uint64_t roundId, uint64_t memberId) -{ - auto groups = dao.getGraph().getEdgesFrom(memberId, common::links::ELECTION_GROUP); - - for (auto& group : groups) { - if (Edge::exists(dao.get_self(), group.getToNode(), roundId, links::ROUND)) { - return VoteGroup(dao, group.getToNode()); - } - } - - return std::nullopt; -} - - -// limit the members field to 1 -// check the member is member of the same group -// This is one vote for multiple members in a large group - we won't have this anymore -// Vote group is tied to a single member - -// We can change the meaning of this - -// void VoteGroup::castVotes(ElectionRound& round, std::vector members) +// VoteGroup::VoteGroup(dao& dao, uint64_t memberId, Data data) +// : TypedDocument(dao, types::ELECTION_VOTE_GROUP) // { -// auto roundId = getRoundID(); -// auto power = round.getAccountPower(getOwner()); +// auto cgs = convert(std::move(data)); + +// initializeDocument(dao, cgs); -// EOS_CHECK( -// roundId == round.getId(), -// "Missmatch between stored round id and round parameter" +// Edge( +// getDao().get_self(), +// getDao().get_self(), +// memberId, +// getId(), +// links::ELECTION_GROUP // ); -// auto contract = getDao().get_self(); +// Edge( +// getDao().get_self(), +// getDao().get_self(), +// getId(), +// getRoundID(), +// links::ELECTION_ROUND +// //name(getRoundID()) +// ); +// } -// //We need to first erase previous votes if any -// // Because someone might change their vote -// auto prevVotes = getDao().getGraph().getEdgesFrom(getId(), links::VOTE); +// uint64_t VoteGroup::getOwner() +// { +// return Edge::getTo( +// getDao().get_self(), +// getId(), +// links::ELECTION_GROUP +// ).getFromNode(); +// } -// dao::election_vote_table elctn_t(contract, roundId); +// std::optional VoteGroup::getFromRound(dao& dao, uint64_t roundId, uint64_t memberId) +// { +// auto groups = dao.getGraph().getEdgesFrom(memberId, common::links::ELECTION_GROUP); -// for (auto& edge : prevVotes) { -// auto memId = edge.getToNode(); -// auto voteEntry = elctn_t.get(memId, "Member entry doesn't exists"); -// elctn_t.modify(voteEntry, eosio::same_payer, [&](dao::ElectionVote& vote){ -// vote.total_amount -= power; -// }); -// edge.erase(); +// for (auto& group : groups) { +// if (Edge::exists(dao.get_self(), group.getToNode(), roundId, links::ROUND)) { +// return VoteGroup(dao, group.getToNode()); +// } // } -// for (auto memId : members) { -// //Verify member is a candidate -// EOS_CHECK( -// round.isCandidate(memId), -// "Member must be a candidate to be voted" -// ); +// return std::nullopt; +// } -// auto voteIt = elctn_t.find(memId); -// if (voteIt != elctn_t.end()) { -// elctn_t.modify(voteIt, eosio::same_payer, [&](dao::ElectionVote& vote){ -// vote.total_amount += power; -// }); -// } -// else { -// elctn_t.emplace(contract, [&](dao::ElectionVote& vote){ -// vote.total_amount += power; -// vote.account_id = memId; -// }); -// } +// // limit the members field to 1 +// // check the member is member of the same group +// // This is one vote for multiple members in a large group - we won't have this anymore +// // Vote group is tied to a single member + +// // We can change the meaning of this + +// // void VoteGroup::castVotes(ElectionRound& round, std::vector members) +// // { +// // auto roundId = getRoundID(); +// // auto power = round.getAccountPower(getOwner()); + +// // EOS_CHECK( +// // roundId == round.getId(), +// // "Missmatch between stored round id and round parameter" +// // ); + +// // auto contract = getDao().get_self(); + +// // //We need to first erase previous votes if any +// // // Because someone might change their vote +// // auto prevVotes = getDao().getGraph().getEdgesFrom(getId(), links::VOTE); + +// // dao::election_vote_table elctn_t(contract, roundId); + +// // for (auto& edge : prevVotes) { +// // auto memId = edge.getToNode(); +// // auto voteEntry = elctn_t.get(memId, "Member entry doesn't exists"); +// // elctn_t.modify(voteEntry, eosio::same_payer, [&](dao::ElectionVote& vote){ +// // vote.total_amount -= power; +// // }); +// // edge.erase(); +// // } + +// // for (auto memId : members) { +// // //Verify member is a candidate +// // EOS_CHECK( +// // round.isCandidate(memId), +// // "Member must be a candidate to be voted" +// // ); + +// // auto voteIt = elctn_t.find(memId); + +// // if (voteIt != elctn_t.end()) { +// // elctn_t.modify(voteIt, eosio::same_payer, [&](dao::ElectionVote& vote){ +// // vote.total_amount += power; +// // }); +// // } +// // else { +// // elctn_t.emplace(contract, [&](dao::ElectionVote& vote){ +// // vote.total_amount += power; +// // vote.account_id = memId; +// // }); +// // } + +// // Edge( +// // contract, +// // contract, +// // getId(), +// // memId, +// // links::VOTE +// // ); +// // } +// // } -// Edge( -// contract, -// contract, -// getId(), -// memId, -// links::VOTE -// ); -// } // } -} - diff --git a/templates/cmake/CMakeLists_src.dune.txt b/templates/cmake/CMakeLists_src.dune.txt index a25c368..308d9ef 100644 --- a/templates/cmake/CMakeLists_src.dune.txt +++ b/templates/cmake/CMakeLists_src.dune.txt @@ -59,7 +59,6 @@ add_contract( dao dao upvote_election/actions.cpp upvote_election/election_round.cpp upvote_election/upvote_election.cpp - upvote_election/vote_group.cpp ../document-graph/src/document_graph/document_graph.cpp ../document-graph/src/document_graph/document.cpp ../document-graph/src/document_graph/content_wrapper.cpp diff --git a/templates/cmake/CMakeLists_src.local.txt b/templates/cmake/CMakeLists_src.local.txt index 9ec51c6..c415180 100644 --- a/templates/cmake/CMakeLists_src.local.txt +++ b/templates/cmake/CMakeLists_src.local.txt @@ -60,7 +60,6 @@ add_contract( dao dao upvote_election/actions.cpp upvote_election/election_round.cpp upvote_election/upvote_election.cpp - upvote_election/vote_group.cpp ../document-graph/src/document_graph/document_graph.cpp ../document-graph/src/document_graph/document.cpp ../document-graph/src/document_graph/content_wrapper.cpp From 1f4d8f22cee40dd329192d5822773d93434bccab Mon Sep 17 00:00:00 2001 From: NIK Date: Mon, 2 Oct 2023 13:13:16 +0800 Subject: [PATCH 27/51] updates --- include/badges/common.hpp | 4 +- include/dao.hpp | 6 +- .../random_number_generator.hpp | 31 + include/upvote_election/upvote_election.hpp | 1 + src/dao.cpp | 16 +- src/upvote_election/actions.cpp | 1655 ++++++++--------- 6 files changed, 866 insertions(+), 847 deletions(-) create mode 100644 include/upvote_election/random_number_generator.hpp diff --git a/include/badges/common.hpp b/include/badges/common.hpp index 554d0ed..70af680 100644 --- a/include/badges/common.hpp +++ b/include/badges/common.hpp @@ -29,10 +29,10 @@ namespace items { } namespace links { - // inline constexpr auto TREASURY_BADGE = "treasurerbdg"; + inline constexpr auto TREASURY_BADGE = eosio::name("treasurerbdg"); inline constexpr auto ADMIN_BADGE = eosio::name("adminbdg"); inline constexpr auto ENROLLER_BADGE = eosio::name("enrollerbdg"); - // inline constexpr auto NORTH_STAR_BADGE = "northstarbdg"; + inline constexpr auto NORTH_STAR_BADGE = eosio::name("northstarbdg"); inline constexpr auto VOTER = eosio::name("voter"); inline constexpr auto DELEGATE = eosio::name("delegate"); inline constexpr auto HEAD_DELEGATE = eosio::name("headdelegate"); diff --git a/include/dao.hpp b/include/dao.hpp index 0ffe813..f97a3e2 100644 --- a/include/dao.hpp +++ b/include/dao.hpp @@ -19,6 +19,9 @@ #include #include +// TODO: Move this to upvote code section +#include + #include using eosio::multi_index; @@ -330,6 +333,7 @@ namespace pricing { //Upvote System ACTION testgrouprng(std::vector ids, uint32_t seed); ACTION testgroupr1(uint32_t num_members, uint32_t seed); + ACTION castupvote(uint64_t round_id, uint64_t group_id, name voter, uint64_t voted_id); ACTION createupvelc(uint64_t dao_id, ContentGroups& election_config); @@ -505,7 +509,7 @@ namespace pricing { private: - std::vector shuffleVector(std::vector& ids, uint32_t seed); + std::vector shuffleVector(std::vector& ids, UERandomGenerator rng); std::vector> createGroups(const std::vector& ids, int minGroupSize); void onRewardTransfer(const name& from, const name& to, const asset& amount); diff --git a/include/upvote_election/random_number_generator.hpp b/include/upvote_election/random_number_generator.hpp new file mode 100644 index 0000000..1684db7 --- /dev/null +++ b/include/upvote_election/random_number_generator.hpp @@ -0,0 +1,31 @@ +#pragma once + +struct UERandomGenerator { + using result_type = uint32_t; + + static constexpr result_type min() { + return 0; // Minimum possible random value + } + + static constexpr result_type max() { + return UINT32_MAX; // Maximum possible random value + } + + uint32_t seed; + uint32_t value; + + UERandomGenerator(uint32_t seed, uint32_t value) { + this->seed = seed; + this->value = value; + } + + result_type operator()() { + const uint64_t a = 1103515245; + const uint64_t c = 12345; + + seed = (uint32_t)((a * seed + c) % 0x7fffffff); + value = ((uint64_t)seed * max()) >> 31; + + return value; + } +}; diff --git a/include/upvote_election/upvote_election.hpp b/include/upvote_election/upvote_election.hpp index cd566fb..1e894b0 100644 --- a/include/upvote_election/upvote_election.hpp +++ b/include/upvote_election/upvote_election.hpp @@ -17,6 +17,7 @@ class UpvoteElection : public TypedDocument PROPERTY(start_date, eosio::time_point, StartDate, USE_GETSET), PROPERTY(end_date, eosio::time_point, EndDate, USE_GETSET), PROPERTY(seed, eosio::checksum256, Seed, USE_GETSET), + PROPERTY(runningSeed, int32_t, RunningSeed, USE_GETSET), PROPERTY(status, std::string, Status, USE_GETSET), PROPERTY(duration, int64_t, Duration, USE_GETSET), PROPERTY(round_duration, int64_t, RoundDuration, USE_GET) diff --git a/src/dao.cpp b/src/dao.cpp index 91435f4..f4ea1c0 100644 --- a/src/dao.cpp +++ b/src/dao.cpp @@ -1365,7 +1365,11 @@ void dao::initSysBadges() { createSystemBadge(badges::common::links::ADMIN_BADGE, "Admin Badge", "https://assets.hypha.earth/badges/badge_admin.png"); createSystemBadge(badges::common::links::ENROLLER_BADGE, "Enroller Badge", "https://assets.hypha.earth/badges/badge_enroller.png"); - createSystemBadge(badges::common::links::ENROLLER_BADGE, "Enroller Badge", "https://assets.hypha.earth/badges/badge_enroller.png"); + createSystemBadge(badges::common::links::DELEGATE, "Upvote Delegate Badge", ""); + createSystemBadge(badges::common::links::CHIEF_DELEGATE, "Chief Delegate Badge", ""); + createSystemBadge(badges::common::links::HEAD_DELEGATE, "Head Delegate Badge", ""); + createSystemBadge(badges::common::links::TREASURY_BADGE, "Treasurer Badge", "https://assets.hypha.earth/badges/badge_treasurer.png"); + createSystemBadge(badges::common::links::NORTH_STAR_BADGE, "North Star Badge", "https://assets.hypha.earth/badges/badge_north_star.png"); } @@ -1376,6 +1380,16 @@ void dao::createSystemBadge(name badge_edge, string label, string icon) { systemBadgeType = badges::SystemBadgeType::Admin; } else if (badge_edge == badges::common::links::ENROLLER_BADGE) { systemBadgeType = badges::SystemBadgeType::Enroller; + } else if (badge_edge == badges::common::links::NORTH_STAR_BADGE) { + systemBadgeType = badges::SystemBadgeType::NorthStar; + } else if (badge_edge == badges::common::links::TREASURY_BADGE) { + systemBadgeType = badges::SystemBadgeType::Treasurer; + } else if (badge_edge == badges::common::links::DELEGATE) { + systemBadgeType = badges::SystemBadgeType::Delegate; + } else if (badge_edge == badges::common::links::CHIEF_DELEGATE) { + systemBadgeType = badges::SystemBadgeType::ChiefDelegate; + } else if (badge_edge == badges::common::links::HEAD_DELEGATE) { + systemBadgeType = badges::SystemBadgeType::HeadDelegate; } else { eosio::check(false, "unknown system type"); } diff --git a/src/upvote_election/actions.cpp b/src/upvote_election/actions.cpp index e2a96fa..df8ab4d 100644 --- a/src/upvote_election/actions.cpp +++ b/src/upvote_election/actions.cpp @@ -10,6 +10,8 @@ #include "badges/badges.hpp" #include "upvote_election/common.hpp" +#include + #include "recurring_activity.hpp" #include @@ -21,1016 +23,983 @@ namespace hypha { -using upvote_election::UpvoteElection; -using upvote_election::UpvoteElectionData; -using upvote_election::ElectionRound; -using upvote_election::ElectionRoundData; -using upvote_election::ElectionGroup; -using upvote_election::ElectionGroupData; -using upvote_election::UpVoteVote; -using upvote_election::UpVoteVoteData; - -namespace upvote_common = upvote_election::common; - - -// we capture a checksum256 seed, then convert it to a uint32_t to use for our rng -// we always store the last seed in the election document, so we can keep using it -// our rng changes the seed on every operation -static uint32_t checksum256_to_uint32(const eosio::checksum256& checksum) { - // Hash the data using SHA-256 - auto hash = eosio::sha256(reinterpret_cast(checksum.extract_as_byte_array().data()), 32); - - // Extract the first 4 bytes and convert to uint32_t - uint32_t result = 0; - for (int i = 0; i < 4; ++i) { - result = (result << 8) | hash.extract_as_byte_array()[i]; - } + using upvote_election::UpvoteElection; + using upvote_election::UpvoteElectionData; + using upvote_election::ElectionRound; + using upvote_election::ElectionRoundData; + using upvote_election::ElectionGroup; + using upvote_election::ElectionGroupData; + using upvote_election::UpVoteVote; + using upvote_election::UpVoteVoteData; + + namespace upvote_common = upvote_election::common; + + + // we capture a checksum256 seed, then convert it to a uint32_t to use for our rng + // we always store the last seed in the election document, so we can keep using it + // our rng changes the seed on every operation + static uint32_t checksum256_to_uint32(const eosio::checksum256& checksum) { + // Hash the data using SHA-256 + auto hash = eosio::sha256(reinterpret_cast(checksum.extract_as_byte_array().data()), 32); + + // Extract the first 4 bytes and convert to uint32_t + uint32_t result = 0; + for (int i = 0; i < 4; ++i) { + result = (result << 8) | hash.extract_as_byte_array()[i]; + } - return result; -} + return result; + } - static uint32_t int_pow(uint32_t base, uint32_t exponent) - { - uint32_t result = 1; - for (uint32_t i = 0; i < exponent; ++i) - { - result *= base; - } - return result; - } + static uint32_t int_pow(uint32_t base, uint32_t exponent) + { + uint32_t result = 1; + for (uint32_t i = 0; i < exponent; ++i) + { + result *= base; + } + return result; + } // From Eden Code - static uint32_t int_root(uint32_t x, uint32_t y) - { - // find z, such that $z^y \le x < (z+1)^y$ - // - // hard coded limits based on the election constraints - uint32_t low = 0, high = 12; - while (high - low > 1) - { - uint32_t mid = (high + low) / 2; - if (x < int_pow(mid, y)) - { - high = mid; - } - else - { - low = mid; - } - } - return low; - } - - static std::size_t count_rounds(uint32_t num_members) - { - std::size_t result = 1; - for (uint32_t i = 12; i <= num_members; i *= 4) - { - ++result; - } - return result; - } - - static std::vector get_group_sizes(uint32_t num_members, std::size_t num_rounds) - { - auto basic_group_size = int_root(num_members, num_rounds); - if (basic_group_size == 3) - { - std::vector result(num_rounds, 4); - // result.front() is always 4, but for some reason, that causes clang to miscompile this. - // TODO: look for UB... - auto large_rounds = - static_cast(std::log(static_cast(num_members) / - int_pow(result.front(), num_rounds - 1) / 3) / - std::log(1.25)); - result.back() = 3; - eosio::check(large_rounds <= 1, - "More that one large round is unexpected when the final group size is 3."); - for (std::size_t i = result.size() - large_rounds - 1; i < result.size() - 1; ++i) - { - result[i] = 5; - } - return result; - } - else if (basic_group_size >= 6) - { - // 5,6,...,6,N - std::vector result(num_rounds, 6); - result.front() = 5; - auto divisor = int_pow(6, num_rounds - 1); - result.back() = (num_members + divisor - 1) / divisor; - return result; - } - else - { - // \lfloor \log_{(G+1)/G}\frac{N}{G^R} \rfloor - auto large_rounds = static_cast( - std::log(static_cast(num_members) / int_pow(basic_group_size, num_rounds)) / - std::log((basic_group_size + 1.0) / basic_group_size)); - // x,x,x,x,x,x - std::vector result(num_rounds, basic_group_size + 1); - std::fill_n(result.begin(), num_rounds - large_rounds, basic_group_size); - return result; - } - } - -// This creates rounds based on the election data - -// We can create a maximum number of rounds from this? -// Or else just create 1 round at a time. -// parsing extermal document -static std::map getRounds(ContentGroups& electionConfig, time_point& endDate) -{ - auto cw = ContentWrapper(electionConfig); - //Store rounds groups sorted by their round id, in case they aren't sorted - std::map rounds; - - for (size_t i = 0; i < electionConfig.size(); ++i) { - auto& group = electionConfig[i]; - if (cw.getGroupLabel(group) == upvote_common::groups::ROUND) { - - ElectionRoundData data; - - data.type = cw.getOrFail( - i, - upvote_common::items::ROUND_TYPE - ).second->getAs(); - - EOS_CHECK( - data.type == upvote_common::round_types::CHIEF || - data.type == upvote_common::round_types::HEAD || - data.type == upvote_common::round_types::DELEGATE, - to_str("Invalid round type: ", data.type) - ); - - auto roundId = cw.getOrFail( - i, - upvote_common::items::ROUND_ID - ).second->getAs(); - - //Let's check if the round was already defined - EOS_CHECK( - rounds.count(roundId) == 0, - "Duplicated round entry in election_config" - ); - - auto duration = cw.getOrFail( - i, - upvote_common::items::ROUND_DURATION - ).second->getAs(); - - data.round_duration = duration; - - endDate += eosio::seconds(duration); + static uint32_t int_root(uint32_t x, uint32_t y) + { + // find z, such that $z^y \le x < (z+1)^y$ + // + // hard coded limits based on the election constraints + uint32_t low = 0, high = 12; + while (high - low > 1) + { + uint32_t mid = (high + low) / 2; + if (x < int_pow(mid, y)) + { + high = mid; + } + else + { + low = mid; + } + } + return low; + } - rounds.insert({roundId, data}); + static std::size_t count_rounds(uint32_t num_members) + { + std::size_t result = 1; + for (uint32_t i = 12; i <= num_members; i *= 4) + { + ++result; } + return result; } - return rounds; -} + static std::vector get_group_sizes(uint32_t num_members, std::size_t num_rounds) + { + auto basic_group_size = int_root(num_members, num_rounds); + if (basic_group_size == 3) + { + std::vector result(num_rounds, 4); + // result.front() is always 4, but for some reason, that causes clang to miscompile this. + // TODO: look for UB... + auto large_rounds = + static_cast(std::log(static_cast(num_members) / + int_pow(result.front(), num_rounds - 1) / 3) / + std::log(1.25)); + result.back() = 3; + eosio::check(large_rounds <= 1, + "More that one large round is unexpected when the final group size is 3."); + for (std::size_t i = result.size() - large_rounds - 1; i < result.size() - 1; ++i) + { + result[i] = 5; + } + return result; + } + else if (basic_group_size >= 6) + { + // 5,6,...,6,N + std::vector result(num_rounds, 6); + result.front() = 5; + auto divisor = int_pow(6, num_rounds - 1); + result.back() = (num_members + divisor - 1) / divisor; + return result; + } + else + { + // \lfloor \log_{(G+1)/G}\frac{N}{G^R} \rfloor + auto large_rounds = static_cast( + std::log(static_cast(num_members) / int_pow(basic_group_size, num_rounds)) / + std::log((basic_group_size + 1.0) / basic_group_size)); + // x,x,x,x,x,x + std::vector result(num_rounds, basic_group_size + 1); + std::fill_n(result.begin(), num_rounds - large_rounds, basic_group_size); + return result; + } + } -static constexpr int64_t getDelegatePower(int64_t roundId) { - return roundId * 1 << roundId; -} + // This creates rounds based on the election data - + // We can create a maximum number of rounds from this? + // Or else just create 1 round at a time. + // parsing extermal document + static std::map getRounds(ContentGroups& electionConfig, time_point& endDate) + { + auto cw = ContentWrapper(electionConfig); + //Store rounds groups sorted by their round id, in case they aren't sorted + std::map rounds; + + for (size_t i = 0; i < electionConfig.size(); ++i) { + auto& group = electionConfig[i]; + if (cw.getGroupLabel(group) == upvote_common::groups::ROUND) { + + ElectionRoundData data; + + data.type = cw.getOrFail( + i, + upvote_common::items::ROUND_TYPE + ).second->getAs(); + + EOS_CHECK( + data.type == upvote_common::round_types::CHIEF || + data.type == upvote_common::round_types::HEAD || + data.type == upvote_common::round_types::DELEGATE, + to_str("Invalid round type: ", data.type) + ); + + auto roundId = cw.getOrFail( + i, + upvote_common::items::ROUND_ID + ).second->getAs(); + + //Let's check if the round was already defined + EOS_CHECK( + rounds.count(roundId) == 0, + "Duplicated round entry in election_config" + ); + + auto duration = cw.getOrFail( + i, + upvote_common::items::ROUND_DURATION + ).second->getAs(); + + data.round_duration = duration; + + endDate += eosio::seconds(duration); + + rounds.insert({ roundId, data }); + } + } -// move out this code - only create a start round -// then add more rounds as needed -// Analyze election round data and see what we need - start and end date for sure -static void createRounds(dao& dao, UpvoteElection& election, std::map& rounds, time_point startDate, time_point endDate) -{ - std::unique_ptr prevRound; + return rounds; + } - // bool hasChief = false; // I don't think we need to check rounds.. + static constexpr int64_t getDelegatePower(int64_t roundId) { + return roundId * 1 << roundId; + } - for (auto& [roundId, roundData] : rounds) { - - // TODO: What is this?? - roundData.delegate_power = getDelegatePower(roundId); + // move out this code - only create a start round + // then add more rounds as needed + // Analyze election round data and see what we need - start and end date for sure + static void createRounds(dao& dao, UpvoteElection& election, std::map& rounds, time_point startDate, time_point endDate) + { + std::unique_ptr prevRound; - roundData.start_date = startDate; + // bool hasChief = false; // I don't think we need to check rounds.. - // Nik: Let's not modify start date? - //startDate += eosio::seconds(roundData.duration); + for (auto& [roundId, roundData] : rounds) { - roundData.end_date = startDate + eosio::seconds(roundData.round_duration); + // TODO: What is this?? + roundData.delegate_power = getDelegatePower(roundId); - // if (roundData.type == upvote_common::round_types::HEAD) { - // EOS_CHECK( - // prevRound && prevRound->getType() == upvote_common::round_types::CHIEF, - // "There has to be a Chief round previous to Head Delegate round" - // ); - // } - // else { - // EOS_CHECK( - // !hasChief, - // to_str("Cannot create ", roundData.type, "type rounds after a Chief round") - // ); + roundData.start_date = startDate; - // hasChief = hasChief || roundData.type == upvote_common::round_types::CHIEF; - // } + // Nik: Let's not modify start date? + //startDate += eosio::seconds(roundData.duration); - auto electionRound = std::make_unique( - dao, - election.getId(), - roundData - ); + roundData.end_date = startDate + eosio::seconds(roundData.round_duration); - if (prevRound) { - - // Nik: Not sure what this check is for - we're not using getPassingCount anymore anyway - // EOS_CHECK( - // prevRound->getPassingCount() > electionRound->getPassingCount(), - // "Passing count has to be decremental" - // ); + // if (roundData.type == upvote_common::round_types::HEAD) { + // EOS_CHECK( + // prevRound && prevRound->getType() == upvote_common::round_types::CHIEF, + // "There has to be a Chief round previous to Head Delegate round" + // ); + // } + // else { + // EOS_CHECK( + // !hasChief, + // to_str("Cannot create ", roundData.type, "type rounds after a Chief round") + // ); - prevRound->setNextRound(electionRound.get()); - } - else { - election.setStartRound(electionRound.get()); - } + // hasChief = hasChief || roundData.type == upvote_common::round_types::CHIEF; + // } - prevRound = std::move(electionRound); - } + auto electionRound = std::make_unique( + dao, + election.getId(), + roundData + ); - // EOS_CHECK( - // prevRound->getType() == upvote_common::round_types::CHIEF || - // prevRound->getType() == upvote_common::round_types::HEAD, - // "Last round must be of type Chief or Head" - // ) + if (prevRound) { - //Verify we have a chief round - // election.getChiefRound(); + // Nik: Not sure what this check is for - we're not using getPassingCount anymore anyway + // EOS_CHECK( + // prevRound->getPassingCount() > electionRound->getPassingCount(), + // "Passing count has to be decremental" + // ); - //At the end both start date and end date should be the same - EOS_CHECK( - startDate == endDate, - to_str("End date missmatch: ", startDate, " ", endDate) - ); -} + prevRound->setNextRound(electionRound.get()); + } + else { + election.setStartRound(electionRound.get()); + } -static void scheduleElectionUpdate(dao& dao, UpvoteElection& election, time_point date) -{ - if (date < eosio::current_time_point()) return; + prevRound = std::move(electionRound); + } - //Schedule a trx to close the proposal - eosio::transaction trx; - trx.actions.emplace_back(eosio::action( - eosio::permission_level(dao.get_self(), eosio::name("active")), - dao.get_self(), - eosio::name("updateupvelc"), - std::make_tuple(election.getId(), true) - )); + // EOS_CHECK( + // prevRound->getType() == upvote_common::round_types::CHIEF || + // prevRound->getType() == upvote_common::round_types::HEAD, + // "Last round must be of type Chief or Head" + // ) - EOS_CHECK ( - date > eosio::current_time_point(), - "Can only schedule for dates in the future" - ); + //Verify we have a chief round + // election.getChiefRound(); - constexpr auto aditionalDelaySec = 10; - trx.delay_sec = (date - eosio::current_time_point()).to_seconds() + aditionalDelaySec; + //At the end both start date and end date should be the same + EOS_CHECK( + startDate == endDate, + to_str("End date missmatch: ", startDate, " ", endDate) + ); + } - auto dhoSettings = dao.getSettingsDocument(); + static void scheduleElectionUpdate(dao& dao, UpvoteElection& election, time_point date) + { + if (date < eosio::current_time_point()) return; - auto nextID = dhoSettings->getSettingOrDefault("next_schedule_id", int64_t(0)); + //Schedule a trx to close the proposal + eosio::transaction trx; + trx.actions.emplace_back(eosio::action( + eosio::permission_level(dao.get_self(), eosio::name("active")), + dao.get_self(), + eosio::name("updateupvelc"), + std::make_tuple(election.getId(), true) + )); - trx.send(nextID, dao.get_self()); + EOS_CHECK( + date > eosio::current_time_point(), + "Can only schedule for dates in the future" + ); - dhoSettings->setSetting(Content{"next_schedule_id", nextID + 1}); -} + constexpr auto aditionalDelaySec = 10; + trx.delay_sec = (date - eosio::current_time_point()).to_seconds() + aditionalDelaySec; -static void assignDelegateBadges(dao& dao, uint64_t daoId, uint64_t electionId, const std::vector& chiefDelegates, std::optional headDelegate, eosio::transaction* trx = nullptr) -{ - //Generate proposals for each one of the delegates + auto dhoSettings = dao.getSettingsDocument(); - auto createAssignment = [&](const std::string& title, uint64_t member, uint64_t badge) { + auto nextID = dhoSettings->getSettingOrDefault("next_schedule_id", int64_t(0)); - Member mem(dao, member); + trx.send(nextID, dao.get_self()); - auto memAccount = mem.getAccount(); + dhoSettings->setSetting(Content{ "next_schedule_id", nextID + 1 }); + } - auto action = eosio::action( - eosio::permission_level(dao.get_self(), eosio::name("active")), - dao.get_self(), - eosio::name("propose"), - std::make_tuple( - daoId, - dao.get_self(), - common::ASSIGN_BADGE, - ContentGroups{ - ContentGroup{ - Content{ CONTENT_GROUP_LABEL, DETAILS }, - Content{ TITLE, title }, - Content{ DESCRIPTION, title }, - Content{ ASSIGNEE, memAccount }, - Content{ BADGE_STRING, static_cast(badge) }, - Content{ badges::common::items::UPVOTE_ELECTION_ID, static_cast(electionId) } - }}, - true - ) - ); + static void assignDelegateBadges(dao& dao, uint64_t daoId, uint64_t electionId, const std::vector& chiefDelegates, std::optional headDelegate, eosio::transaction* trx = nullptr) + { + //Generate proposals for each one of the delegates + + auto createAssignment = [&](const std::string& title, uint64_t member, uint64_t badge) { + + Member mem(dao, member); + + auto memAccount = mem.getAccount(); + + auto action = eosio::action( + eosio::permission_level(dao.get_self(), eosio::name("active")), + dao.get_self(), + eosio::name("propose"), + std::make_tuple( + daoId, + dao.get_self(), + common::ASSIGN_BADGE, + ContentGroups{ + ContentGroup{ + Content{ CONTENT_GROUP_LABEL, DETAILS }, + Content{ TITLE, title }, + Content{ DESCRIPTION, title }, + Content{ ASSIGNEE, memAccount }, + Content{ BADGE_STRING, static_cast(badge) }, + Content{ badges::common::items::UPVOTE_ELECTION_ID, static_cast(electionId) } + } }, + true + ) + ); - if (trx) { - trx->actions.emplace_back(std::move(action)); - } - else { - action.send(); - } - }; + if (trx) { + trx->actions.emplace_back(std::move(action)); + } + else { + action.send(); + } + }; - auto chiefBadgeEdge = Edge::get(dao.get_self(), dao.getRootID(), upvote_common::links::CHIEF_DELEGATE); - auto chiefBadge = TypedDocument::withType(dao, chiefBadgeEdge.getToNode(), common::BADGE_NAME); + auto chiefBadgeEdge = Edge::get(dao.get_self(), dao.getRootID(), upvote_common::links::CHIEF_DELEGATE); + auto chiefBadge = TypedDocument::withType(dao, chiefBadgeEdge.getToNode(), common::BADGE_NAME); - auto headBadgeEdge = Edge::get(dao.get_self(), dao.getRootID(), upvote_common::links::HEAD_DELEGATE); - auto headBadge = TypedDocument::withType(dao, headBadgeEdge.getToNode(), common::BADGE_NAME); + auto headBadgeEdge = Edge::get(dao.get_self(), dao.getRootID(), upvote_common::links::HEAD_DELEGATE); + auto headBadge = TypedDocument::withType(dao, headBadgeEdge.getToNode(), common::BADGE_NAME); - for (auto& chief : chiefDelegates) { - createAssignment("Chief Delegate", chief, chiefBadge.getID()); - } + for (auto& chief : chiefDelegates) { + createAssignment("Chief Delegate", chief, chiefBadge.getID()); + } - if (headDelegate) { - createAssignment("Head Delegate", *headDelegate, headBadge.getID()); + if (headDelegate) { + createAssignment("Head Delegate", *headDelegate, headBadge.getID()); + } } -} #ifdef EOS_BUILD -void dao::importelct(uint64_t dao_id, bool deferred) -{ - verifyDaoType(dao_id); - checkAdminsAuth(dao_id); - - //Schedule a trx to archive and to crate new badges - eosio::transaction trx; - - //Remove existing Head Delegate/Chief Delegate badges if any - auto cleanBadgesOf = [&](const name& badgeEdge) { - auto badgeId = Edge::get(get_self(), getRootID(), badgeEdge).getToNode(); - - auto badgeAssignmentEdges = getGraph().getEdgesFrom(badgeId, common::ASSIGNMENT); - - //Filter out those that are not from the specified DAO - auto badgeAssignments = std::vector{}; - badgeAssignments.reserve(badgeAssignmentEdges.size()); - - std::transform( - badgeAssignmentEdges.begin(), - badgeAssignmentEdges.end(), - std::back_inserter(badgeAssignments), - [](const Edge& edge){ - return edge.to_node; - } - ); - - badgeAssignments.erase( - std::remove_if( - badgeAssignments.begin(), - badgeAssignments.end(), - [&](uint64_t id) { - return !Edge::exists(get_self(), id, dao_id, common::DAO); + void dao::importelct(uint64_t dao_id, bool deferred) + { + verifyDaoType(dao_id); + checkAdminsAuth(dao_id); + + //Schedule a trx to archive and to crate new badges + eosio::transaction trx; + + //Remove existing Head Delegate/Chief Delegate badges if any + auto cleanBadgesOf = [&](const name& badgeEdge) { + auto badgeId = Edge::get(get_self(), getRootID(), badgeEdge).getToNode(); + + auto badgeAssignmentEdges = getGraph().getEdgesFrom(badgeId, common::ASSIGNMENT); + + //Filter out those that are not from the specified DAO + auto badgeAssignments = std::vector{}; + badgeAssignments.reserve(badgeAssignmentEdges.size()); + + std::transform( + badgeAssignmentEdges.begin(), + badgeAssignmentEdges.end(), + std::back_inserter(badgeAssignments), + [](const Edge& edge) { + return edge.to_node; } - ), - badgeAssignments.end() - ); - - for (auto& id : badgeAssignments) { - auto doc = TypedDocument::withType(*this, id, common::ASSIGN_BADGE); - - auto cw = doc.getContentWrapper(); + ); - cw.insertOrReplace(*cw.getGroupOrFail(SYSTEM), Content { - "force_archive", - 1 - }); + badgeAssignments.erase( + std::remove_if( + badgeAssignments.begin(), + badgeAssignments.end(), + [&](uint64_t id) { + return !Edge::exists(get_self(), id, dao_id, common::DAO); + } + ), + badgeAssignments.end() + ); - cw.insertOrReplace(*cw.getGroupOrFail(DETAILS), Content { - END_TIME, - eosio::current_time_point() - }); + for (auto& id : badgeAssignments) { + auto doc = TypedDocument::withType(*this, id, common::ASSIGN_BADGE); - doc.update(); + auto cw = doc.getContentWrapper(); - trx.actions.emplace_back(eosio::action( - eosio::permission_level(get_self(), eosio::name("active")), - get_self(), - eosio::name("archiverecur"), - std::make_tuple(id) - )); - } - }; - - cleanBadgesOf(upvote_common::links::HEAD_DELEGATE); - cleanBadgesOf(upvote_common::links::CHIEF_DELEGATE); - - struct [[eosio::table("elect.state"), eosio::contract("genesis.eden")]] election_state_v0 { - name lead_representative; - std::vector board; - eosio::block_timestamp_type last_election_time; - }; - - using election_state_singleton = eosio::singleton<"elect.state"_n, std::variant>; - - election_state_singleton election_s("genesis.eden"_n, 0); - auto state = election_s.get(); - - std::vector chiefs; - std::optional head = 0; - - std::visit([&](election_state_v0& election){ - - //If we want to prevent head del to be twice in the board array - //we can use a simple find condition, for now it doesn't - //matter if the head del is duplicated as we only assign one head - //variable - // if (std::find( - // election.board.begin(), - // election.board.end(), - // election.lead_representative - // ) == election.board.end()) { - // election.board.push_back(election.lead_representative); - // } + cw.insertOrReplace(*cw.getGroupOrFail(SYSTEM), Content{ + "force_archive", + 1 + }); - for (auto& mem : election.board) { - if (mem) { - auto member = getOrCreateMember(mem); + cw.insertOrReplace(*cw.getGroupOrFail(DETAILS), Content{ + END_TIME, + eosio::current_time_point() + }); - //Make community member if not core or communnity member already - if (!Member::isMember(*this, dao_id, mem) && - !Member::isCommunityMember(*this, dao_id, mem)) { - Edge(get_self(), get_self(), dao_id, member.getID(), common::COMMEMBER); - } + doc.update(); - if (mem == election.lead_representative) { - head = member.getID(); - } - else { - chiefs.push_back(member.getID()); + trx.actions.emplace_back(eosio::action( + eosio::permission_level(get_self(), eosio::name("active")), + get_self(), + eosio::name("archiverecur"), + std::make_tuple(id) + )); + } + }; + + cleanBadgesOf(upvote_common::links::HEAD_DELEGATE); + cleanBadgesOf(upvote_common::links::CHIEF_DELEGATE); + + struct [[eosio::table("elect.state"), eosio::contract("genesis.eden")]] election_state_v0 { + name lead_representative; + std::vector board; + eosio::block_timestamp_type last_election_time; + }; + + using election_state_singleton = eosio::singleton<"elect.state"_n, std::variant>; + + election_state_singleton election_s("genesis.eden"_n, 0); + auto state = election_s.get(); + + std::vector chiefs; + std::optional head = 0; + + std::visit([&](election_state_v0& election) { + + //If we want to prevent head del to be twice in the board array + //we can use a simple find condition, for now it doesn't + //matter if the head del is duplicated as we only assign one head + //variable + // if (std::find( + // election.board.begin(), + // election.board.end(), + // election.lead_representative + // ) == election.board.end()) { + // election.board.push_back(election.lead_representative); + // } + + for (auto& mem : election.board) { + if (mem) { + auto member = getOrCreateMember(mem); + + //Make community member if not core or communnity member already + if (!Member::isMember(*this, dao_id, mem) && + !Member::isCommunityMember(*this, dao_id, mem)) { + Edge(get_self(), get_self(), dao_id, member.getID(), common::COMMEMBER); + } + + if (mem == election.lead_representative) { + head = member.getID(); + } + else { + chiefs.push_back(member.getID()); + } } } - } - }, state); + }, state); - //Send election id as 0 meaning that election was done outside - assignDelegateBadges(*this, dao_id, 0, chiefs, head, &trx); + //Send election id as 0 meaning that election was done outside + assignDelegateBadges(*this, dao_id, 0, chiefs, head, &trx); - //Trigger all cleanup and propose actions - if (deferred) { - constexpr auto aditionalDelaySec = 5; - trx.delay_sec = aditionalDelaySec; + //Trigger all cleanup and propose actions + if (deferred) { + constexpr auto aditionalDelaySec = 5; + trx.delay_sec = aditionalDelaySec; - auto dhoSettings = getSettingsDocument(); + auto dhoSettings = getSettingsDocument(); - auto nextID = dhoSettings->getSettingOrDefault("next_schedule_id", int64_t(0)); + auto nextID = dhoSettings->getSettingOrDefault("next_schedule_id", int64_t(0)); - trx.send(nextID, get_self()); + trx.send(nextID, get_self()); - dhoSettings->setSetting(Content{"next_schedule_id", nextID + 1}); - } - else { - for (auto& action : trx.actions) { - action.send(); + dhoSettings->setSetting(Content{ "next_schedule_id", nextID + 1 }); + } + else { + for (auto& action : trx.actions) { + action.send(); + } } + } - -} #endif -// Function to convert eosio::sha256 to a 64-bit uint -uint64_t sha256ToUint64(const eosio::checksum256& sha256Hash) { - const uint64_t* hashData = reinterpret_cast(sha256Hash.data()); - // Assuming sha256Hash is 32 bytes, take the first two 64-bit values - return hashData[0] ^ hashData[1]; -} + // Function to convert eosio::sha256 to a 64-bit uint + uint64_t sha256ToUint64(const eosio::checksum256& sha256Hash) { + const uint64_t* hashData = reinterpret_cast(sha256Hash.data()); + // Assuming sha256Hash is 32 bytes, take the first two 64-bit values + return hashData[0] ^ hashData[1]; + } -/// @brief Test random group creation -/// @param ids -void dao::testgroupr1(uint32_t num_members, uint32_t seed) { + /// @brief Test random group creation + /// @param ids + void dao::testgroupr1(uint32_t num_members, uint32_t seed) { - - require_auth(get_self()); - std::vector ids(num_members); // Initialize a vector with 100 elements + require_auth(get_self()); - // Set each element's value to its index - for (size_t i = 0; i < num_members; ++i) { - ids[i] = static_cast(i); - } - testgrouprng(ids, seed); - - // for (uint32_t n = 0; n < num_members; ++n) { - // auto rounds = count_rounds(n); + std::vector ids(num_members); // Initialize a vector with 100 elements + + // Set each element's value to its index + for (size_t i = 0; i < num_members; ++i) { + ids[i] = static_cast(i); + } + testgrouprng(ids, seed); - // std::vector group_sizes = get_group_sizes(n, rounds); - - // eosio::print(" N: ", n, " rounds: ", rounds, " g_sizes: "); + // for (uint32_t n = 0; n < num_members; ++n) { + // auto rounds = count_rounds(n); - // for (uint32_t s : group_sizes) { - // eosio::print(s, " "); - // } - - // eosio::print(" ::: "); + // std::vector group_sizes = get_group_sizes(n, rounds); + // eosio::print(" N: ", n, " rounds: ", rounds, " g_sizes: "); - // } + // for (uint32_t s : group_sizes) { + // eosio::print(s, " "); + // } + // eosio::print(" ::: "); -} -void dao::testgrouprng(std::vector ids, uint32_t seed) { - - require_auth(get_self()); - - eosio::print(" ids: "); - for (const uint64_t& element : ids) { - eosio::print(element, " "); - } + // } - auto randomIds = shuffleVector(ids, seed); - eosio::print(" random ids: "); - for (const uint64_t& element : randomIds) { - eosio::print(element, " "); } - //// Eden defines min group size as 4, but depending on - //// the numbers, Edenia code does something slightly different - // int minGroupsSize = 4; - - //// use Edenia code to figure out group size - auto n = ids.size(); - auto rounds = count_rounds(n); - std::vector group_sizes = get_group_sizes(n, rounds); - int minGroupsSize = group_sizes[0]; - auto groups = createGroups(randomIds, minGroupsSize); - - eosio::print(" groups min size: ", minGroupsSize); - for (uint32_t i = 0; i < groups.size(); ++i) { - auto group = groups[i]; - eosio::print(" group: ", i, "(", group.size(), "): "); - for (const uint64_t& element : group) { + void dao::testgrouprng(std::vector ids, uint32_t seed) { + + require_auth(get_self()); + + eosio::print(" ids: "); + for (const uint64_t& element : ids) { eosio::print(element, " "); } - } -} + UERandomGenerator rng(seed, 0); -std::vector dao::shuffleVector(std::vector& ids, uint32_t seed) { - struct MyRandomGenerator { - using result_type = uint32_t; + auto randomIds = shuffleVector(ids, rng); - static constexpr result_type min() { - return 0; // Minimum possible random value + eosio::print(" random ids: "); + for (const uint64_t& element : randomIds) { + eosio::print(element, " "); } - static constexpr result_type max() { - return UINT32_MAX; // Maximum possible random value + //// Eden defines min group size as 4, but depending on + //// the numbers, Edenia code does something slightly different + // int minGroupsSize = 4; + + //// use Edenia code to figure out group size + auto n = ids.size(); + auto rounds = count_rounds(n); + std::vector group_sizes = get_group_sizes(n, rounds); + int minGroupsSize = group_sizes[0]; + auto groups = createGroups(randomIds, minGroupsSize); + + eosio::print(" groups min size: ", minGroupsSize); + for (uint32_t i = 0; i < groups.size(); ++i) { + auto group = groups[i]; + eosio::print(" group: ", i, "(", group.size(), "): "); + for (const uint64_t& element : group) { + eosio::print(element, " "); + } } - uint32_t seed; - uint32_t value; + } - MyRandomGenerator(uint32_t seed, uint32_t value) { - this->seed = seed; - this->value = value; - } + std::vector dao::shuffleVector(std::vector& ids, UERandomGenerator rng) { - result_type operator()() { - const uint64_t a = 1103515245; - const uint64_t c = 12345; + // Shuffle the vector using the RNG + std::shuffle(ids.begin(), ids.end(), rng); - seed = (uint32_t)((a * seed + c) % 0x7fffffff); - value = ((uint64_t)seed * max()) >> 31; + return ids; - return value; - } - }; + } - // Create a random number generator with the seed - MyRandomGenerator rng(seed, 0); - // Shuffle the vector using the RNG - std::shuffle(ids.begin(), ids.end(), rng); + // 36 / 6 = 6 => 1 round, 1 HD round + size_t numrounds(size_t num_delegates) { + size_t rounds = 1; + if (num_delegates > 12) { + size_t numGroups = std::ceil(static_cast(num_delegates) / 6); - return ids; + } + return rounds; + } -} + // From EDEN contracts - decides group max size by num groups and num participants + // struct election_round_config + // { + // uint16_t num_participants; + // uint16_t num_groups; + // constexpr uint8_t group_max_size() const + // { + // return (num_participants + num_groups - 1) / num_groups; + // } + // constexpr uint16_t num_short_groups() const + // { + // return group_max_size() * num_groups - num_participants; + // } + + // constexpr uint32_t num_large_groups() const { return num_groups - num_short_groups(); } + // constexpr uint32_t group_min_size() const { return group_max_size() - 1; } + // uint32_t member_index_to_group(uint32_t idx) const; + // uint32_t group_to_first_member_index(uint32_t idx) const; + // // invariants: + // // num_groups * group_max_size - num_short_groups = num_participants + // // group_max_size <= 12 + // // num_short_groups < num_groups + // }; + + + // Requirements: + // - Except for the last round, the group size shall be in [4,6] + // - The last round has a minimum group size of 3 + // - The maximum group size shall be as small as possible + // - The group sizes within a round shall have a maximum difference of 1 + std::vector> dao::createGroups(const std::vector& ids, int minGroupSize) { + std::vector> groups; + + // Calculate the number of groups needed - group size fills up to 6 max, except when there's + // only 1 group, which goes up to 11 + int numGroups = ids.size() / minGroupSize; + + // Create the groups with an initial capacity of 4 + for (int i = 0; i < numGroups; ++i) { + groups.push_back(std::vector()); + groups.back().reserve(4); + } + // Initialize group iterators + auto currentGroup = groups.begin(); + auto endGroup = groups.end(); -// 36 / 6 = 6 => 1 round, 1 HD round -size_t numrounds(size_t num_delegates) { - size_t rounds = 1; - if (num_delegates > 12) { - size_t numGroups = std::ceil(static_cast(num_delegates) / 6); + // Iterate over the IDs and distribute them into groups + for (const auto& id : ids) { + // Add the ID to the current group + currentGroup->push_back(id); - } - return rounds; -} + // Move to the next group (take turns) + ++currentGroup; + + // Wrap back to the beginning of the groups + if (currentGroup == endGroup) { + currentGroup = groups.begin(); + } + } -// From EDEN contracts - decides group max size by num groups and num participants -// struct election_round_config -// { -// uint16_t num_participants; -// uint16_t num_groups; -// constexpr uint8_t group_max_size() const -// { -// return (num_participants + num_groups - 1) / num_groups; -// } -// constexpr uint16_t num_short_groups() const -// { -// return group_max_size() * num_groups - num_participants; -// } - -// constexpr uint32_t num_large_groups() const { return num_groups - num_short_groups(); } -// constexpr uint32_t group_min_size() const { return group_max_size() - 1; } -// uint32_t member_index_to_group(uint32_t idx) const; -// uint32_t group_to_first_member_index(uint32_t idx) const; -// // invariants: -// // num_groups * group_max_size - num_short_groups = num_participants -// // group_max_size <= 12 -// // num_short_groups < num_groups -// }; - - - // Requirements: - // - Except for the last round, the group size shall be in [4,6] - // - The last round has a minimum group size of 3 - // - The maximum group size shall be as small as possible - // - The group sizes within a round shall have a maximum difference of 1 -std::vector> dao::createGroups(const std::vector& ids, int minGroupSize) { - std::vector> groups; - - // Calculate the number of groups needed - group size fills up to 6 max, except when there's - // only 1 group, which goes up to 11 - int numGroups = ids.size() / minGroupSize; - - // Create the groups with an initial capacity of 4 - for (int i = 0; i < numGroups; ++i) { - groups.push_back(std::vector()); - groups.back().reserve(4); + return groups; } - // Initialize group iterators - auto currentGroup = groups.begin(); - auto endGroup = groups.end(); + //Check if we need to update an ongoing elections status: + //upcoming -> ongoing + //ongoing -> finished + //or change the current round + void dao::updateupvelc(uint64_t election_id, bool reschedule) + { + //eosio::require_auth(); + UpvoteElection election(*this, election_id); - // Iterate over the IDs and distribute them into groups - for (const auto& id : ids) { - // Add the ID to the current group - currentGroup->push_back(id); + auto status = election.getStatus(); - // Move to the next group (take turns) - ++currentGroup; + auto now = eosio::current_time_point(); - // Wrap back to the beginning of the groups - if (currentGroup == endGroup) { - currentGroup = groups.begin(); - } - } + auto daoId = election.getDaoID(); - return groups; -} + if (status == upvote_common::upvote_status::UPCOMING) { + auto start = election.getStartDate(); -//Check if we need to update an ongoing elections status: -//upcoming -> ongoing -//ongoing -> finished -//or change the current round -void dao::updateupvelc(uint64_t election_id, bool reschedule) -{ - //eosio::require_auth(); - UpvoteElection election(*this, election_id); + //Let's update as we already started + if (start <= now) { + // START.... - auto status = election.getStatus(); + Edge::get(get_self(), daoId, election.getId(), upvote_common::links::UPCOMING_ELECTION).erase(); - auto now = eosio::current_time_point(); + Edge(get_self(), get_self(), daoId, election.getId(), upvote_common::links::ONGOING_ELECTION); - auto daoId = election.getDaoID(); + election.setStatus(upvote_common::upvote_status::ONGOING); - // auto setupCandidates = [&](uint64_t roundId, const std::vector& members){ - // election_vote_table elctn_t(get_self(), roundId); + auto startRound = election.getStartRound(); + election.setCurrentRound(&startRound); - // for (auto& memId : members) { - // elctn_t.emplace(get_self(), [memId](ElectionVote& vote) { - // vote.total_amount = 0; - // vote.account_id = memId; - // }); - // } - // }; + //Setup all candidates + auto delegates = getGraph().getEdgesFrom(daoId, badges::common::links::DELEGATE); - if (status == upvote_common::upvote_status::UPCOMING) { - auto start = election.getStartDate(); + std::vector delegateIds; + delegateIds.reserve(delegates.size()); - //Let's update as we already started - if (start <= now) { - // START.... - - Edge::get(get_self(), daoId, election.getId(), upvote_common::links::UPCOMING_ELECTION).erase(); - - Edge(get_self(), get_self(), daoId, election.getId(), upvote_common::links::ONGOING_ELECTION); - - election.setStatus(upvote_common::upvote_status::ONGOING); - - auto startRound = election.getStartRound(); - election.setCurrentRound(&startRound); - - //Setup all candidates - auto delegates = getGraph().getEdgesFrom(daoId, badges::common::links::DELEGATE); - - std::vector delegateIds; - delegateIds.reserve(delegates.size()); - - for (auto& delegate : delegates) { - delegateIds.push_back(delegate.getToNode()); - } + for (auto& delegate : delegates) { + delegateIds.push_back(delegate.getToNode()); + } - uint64_t seed = 0; // TODO: Get seed from table + auto initialSeed = election.getSeed(); - auto randomIds = shuffleVector(delegateIds, seed); - - auto groups = createGroups(randomIds, 4); // TODO: CHANGE THIS TO EDENIA CODE + uint64_t seed = checksum256_to_uint32(initialSeed); - for (auto& groupMembers : groups) { - startRound.addElectionGroup(groupMembers); - } + // Create a random number generator with the seed + UERandomGenerator rng(seed, 0); - // analyze what this is and if we need it - // setupCandidates(startRound.getId(), delegateIds); + auto randomIds = shuffleVector(delegateIds, rng); - scheduleElectionUpdate(*this, election, startRound.getEndDate()); - } - else if (reschedule) { - scheduleElectionUpdate(*this, election, start); - } - } - else if (status == upvote_common::upvote_status::ONGOING) { - auto currentRound = election.getCurrentRound(); - auto end = currentRound.getEndDate(); - if (end <= now) { - - auto winners = currentRound.getWinners(); - - for (auto& winner : winners) { - Edge(get_self(), get_self(), currentRound.getId(), winner, upvote_common::links::ROUND_WINNER); - } + election.setRunningSeed(rng.seed); + election.update(); + + auto groups = createGroups(randomIds, 4); // TODO: CHANGE THIS TO EDENIA CODE - Edge::get(get_self(), election_id, upvote_common::links::CURRENT_ROUND).erase(); + for (auto& groupMembers : groups) { + startRound.addElectionGroup(groupMembers); + } - if (auto nextRound = currentRound.getNextRound()) { - - // set up the next round + // analyze what this is and if we need it + // setupCandidates(startRound.getId(), delegateIds); - election.setCurrentRound(nextRound.get()); + scheduleElectionUpdate(*this, election, startRound.getEndDate()); + } + else if (reschedule) { + scheduleElectionUpdate(*this, election, start); + } + } + else if (status == upvote_common::upvote_status::ONGOING) { + auto currentRound = election.getCurrentRound(); + auto end = currentRound.getEndDate(); + if (end <= now) { - // setupCandidates(nextRound->getId(), winners); + auto winners = currentRound.getWinners(); - // round.addCandidate adds candidates for (auto& winner : winners) { - // TODO: Change all this - Nik - //nextRound->addCandidate(winner); + Edge(get_self(), get_self(), currentRound.getId(), winner, upvote_common::links::ROUND_WINNER); } - scheduleElectionUpdate(*this, election, nextRound->getEndDate()); - } - else { + Edge::get(get_self(), election_id, upvote_common::links::CURRENT_ROUND).erase(); + + if (auto nextRound = currentRound.getNextRound()) { - // The election has ended. - - Edge::get(get_self(), daoId, election.getId(), upvote_common::links::ONGOING_ELECTION).erase(); - - Edge(get_self(), get_self(), daoId, election.getId(), upvote_common::links::PREVIOUS_ELECTION); - - //TODO: Setup head & chief badges - - if (currentRound.getType() == upvote_common::round_types::HEAD) { - //Get previous round for chief delegates - auto chiefs = election.getCurrentRound().getWinners(); - - //Remove head delegate - chiefs.erase( - std::remove_if( - chiefs.begin(), - chiefs.end(), - [head = winners.at(0)](uint64_t id){ return id == head; } - ), - chiefs.end() - ); - - assignDelegateBadges(*this, daoId, election.getId(), chiefs, winners.at(0)); + // set up the next round + + election.setCurrentRound(nextRound.get()); + + // setupCandidates(nextRound->getId(), winners); + + // round.addCandidate adds candidates + for (auto& winner : winners) { + // TODO: Change all this - Nik + //nextRound->addCandidate(winner); + } + + scheduleElectionUpdate(*this, election, nextRound->getEndDate()); } - //No head delegate else { - assignDelegateBadges(*this, daoId, election.getId(), winners, std::nullopt); - } - - election.setStatus(upvote_common::upvote_status::FINISHED); + // The election has ended. + + Edge::get(get_self(), daoId, election.getId(), upvote_common::links::ONGOING_ELECTION).erase(); + + Edge(get_self(), get_self(), daoId, election.getId(), upvote_common::links::PREVIOUS_ELECTION); + + //TODO: Setup head & chief badges + + if (currentRound.getType() == upvote_common::round_types::HEAD) { + //Get previous round for chief delegates + auto chiefs = election.getCurrentRound().getWinners(); + + //Remove head delegate + chiefs.erase( + std::remove_if( + chiefs.begin(), + chiefs.end(), + [head = winners.at(0)](uint64_t id) { return id == head; } + ), + chiefs.end() + ); + + assignDelegateBadges(*this, daoId, election.getId(), chiefs, winners.at(0)); + } + //No head delegate + else { + assignDelegateBadges(*this, daoId, election.getId(), winners, std::nullopt); + } + + + election.setStatus(upvote_common::upvote_status::FINISHED); + } + } + else if (reschedule) { + scheduleElectionUpdate(*this, election, end); } } - else if (reschedule){ - scheduleElectionUpdate(*this, election, end); + else { + EOS_CHECK( + false, + "Election already finished or canceled" + ) } - } - else { - EOS_CHECK( - false, - "Election already finished or canceled" - ) - } - election.update(); -} + election.update(); + } -void dao::cancelupvelc(uint64_t election_id) -{ - UpvoteElection election(*this, election_id); + void dao::cancelupvelc(uint64_t election_id) + { + UpvoteElection election(*this, election_id); - auto daoId = election.getDaoID(); + auto daoId = election.getDaoID(); - checkAdminsAuth(daoId); + checkAdminsAuth(daoId); - auto status = election.getStatus(); + auto status = election.getStatus(); - // TODO - // canceling an ongoing election - debug feature? - bool isOngoing = status == upvote_common::upvote_status::ONGOING; + // TODO + // canceling an ongoing election - debug feature? + bool isOngoing = status == upvote_common::upvote_status::ONGOING; - EOS_CHECK( - isOngoing || - status == upvote_common::upvote_status::UPCOMING, - to_str("Cannot cancel election with ", status, " status") - ); + EOS_CHECK( + isOngoing || + status == upvote_common::upvote_status::UPCOMING, + to_str("Cannot cancel election with ", status, " status") + ); - if (isOngoing) { - /// TODO get all rounds, then delete them all - Edge::get(get_self(), daoId, election.getId(), upvote_common::links::ONGOING_ELECTION).erase(); - } - else { - Edge::get(get_self(), daoId, election.getId(), upvote_common::links::UPCOMING_ELECTION).erase(); - } + if (isOngoing) { + /// TODO get all rounds, then delete them all + Edge::get(get_self(), daoId, election.getId(), upvote_common::links::ONGOING_ELECTION).erase(); + } + else { + Edge::get(get_self(), daoId, election.getId(), upvote_common::links::UPCOMING_ELECTION).erase(); + } - election.setStatus(upvote_common::upvote_status::CANCELED); + election.setStatus(upvote_common::upvote_status::CANCELED); - election.update(); -} + election.update(); + } -void dao::castupvote(uint64_t round_id, uint64_t group_id, name voter, uint64_t voted_id) -{ - // Check auth - eosio::require_auth(voter); - auto memberId = getMemberID(voter); + void dao::castupvote(uint64_t round_id, uint64_t group_id, name voter, uint64_t voted_id) + { + // Check auth + eosio::require_auth(voter); + auto memberId = getMemberID(voter); - // Check the round is valid - ElectionRound round(*this, round_id); - UpvoteElection election = round.getElection(); - auto currentRound = election.getCurrentRound(); + // Check the round is valid + ElectionRound round(*this, round_id); + UpvoteElection election = round.getElection(); + auto currentRound = election.getCurrentRound(); - EOS_CHECK( - currentRound.getId() == round_id, - "You can only vote on the current round" - ); + EOS_CHECK( + currentRound.getId() == round_id, + "You can only vote on the current round" + ); - // Check the group membership of voter and voted - ElectionGroup group(*this, group_id); + // Check the group membership of voter and voted + ElectionGroup group(*this, group_id); - EOS_CHECK( - group.isElectionRoundMember(memberId), - "Only members of the group can vote." - ); + EOS_CHECK( + group.isElectionRoundMember(memberId), + "Only members of the group can vote." + ); - EOS_CHECK( - group.isElectionRoundMember(voted_id), - "Only members of the group can be voted for." - ); + EOS_CHECK( + group.isElectionRoundMember(voted_id), + "Only members of the group can be voted for." + ); - group.vote(memberId, voted_id); + group.vote(memberId, voted_id); -} + } -/* -election_config: [ - [ - { "label": "content_group_label", "value": ["string", "details"] }, - //Upvote start date-time - { "label": "upvote_start_date_time", "value": ["timepoint", "..."] }, - //How much will chief/head Delegates hold their badges after upvote is finished - { "label": "upvote_duration", "value": ["int64", 7776000] }, - // Round Duration in seconds - { "label": "round_duration", "value": ["int64", 3600] }, - - ], - [ - { "label":, "content_group_label", "value": ["string", "round"] }, - //Duration in seconds - { "label": "duration", "value": ["int64", 9000] }, - //One of "delegate"|"chief"|"head" - { "label": "type", "value": ["string", "delegate"] }, - //Number that indicates the order of this round, should start at 0 and increment by 1 for each round - { "label": "round_id", "value": ["int64", 0] }, - //Number of candidates that will pass after the round is over - - ], - [ - { "label":, "content_group_label", "value": ["string", "round"] }, - { "label": "duration", "value": ["int64", 9000] }, //Duration in seconds - { "label": "type", "value": ["string", "chief"] }, - { "label": "round_id", "value": ["int64", 1] }, - ], - [ - { "label":, "content_group_label", "value": ["string", "round"] }, - { "label": "duration", "value": ["int64", 9000] }, //Duration in seconds - { "label": "type", "value": ["string", "head"] }, - { "label": "round_id", "value": ["int64", 1] }, + /* + election_config: [ + [ + { "label": "content_group_label", "value": ["string", "details"] }, + //Upvote start date-time + { "label": "upvote_start_date_time", "value": ["timepoint", "..."] }, + //How much will chief/head Delegates hold their badges after upvote is finished + { "label": "upvote_duration", "value": ["int64", 7776000] }, + // Round Duration in seconds + { "label": "round_duration", "value": ["int64", 3600] }, + + ], + [ + { "label":, "content_group_label", "value": ["string", "round"] }, + //Duration in seconds + { "label": "duration", "value": ["int64", 9000] }, + //One of "delegate"|"chief"|"head" + { "label": "type", "value": ["string", "delegate"] }, + //Number that indicates the order of this round, should start at 0 and increment by 1 for each round + { "label": "round_id", "value": ["int64", 0] }, + //Number of candidates that will pass after the round is over + + ], + [ + { "label":, "content_group_label", "value": ["string", "round"] }, + { "label": "duration", "value": ["int64", 9000] }, //Duration in seconds + { "label": "type", "value": ["string", "chief"] }, + { "label": "round_id", "value": ["int64", 1] }, + ], + [ + { "label":, "content_group_label", "value": ["string", "round"] }, + { "label": "duration", "value": ["int64", 9000] }, //Duration in seconds + { "label": "type", "value": ["string", "head"] }, + { "label": "round_id", "value": ["int64", 1] }, + ] ] -] -*/ -void dao::createupvelc(uint64_t dao_id, ContentGroups& election_config) -{ - verifyDaoType(dao_id); - checkAdminsAuth(dao_id); - - auto cw = ContentWrapper(election_config); - - auto startDate = cw.getOrFail( - DETAILS, - upvote_common::items::UPVOTE_STARTDATE - )->getAs(); - - auto duration = cw.getOrFail( - DETAILS, - upvote_common::items::UPVOTE_DURATION - )->getAs(); - - auto round_duration = cw.getOrFail( - DETAILS, - upvote_common::items::ROUND_DURATION - )->getAs(); - - time_point endDate = startDate; - - UpvoteElection upvoteElection(*this, dao_id, UpvoteElectionData{ - .start_date = startDate, - .end_date = endDate, - .status = upvote_common::upvote_status::UPCOMING, - .duration = duration, - .round_duration = round_duration - }); - - // add start round - upvoteElection.addRound(); - - scheduleElectionUpdate(*this, upvoteElection, startDate); -} + */ + void dao::createupvelc(uint64_t dao_id, ContentGroups& election_config) + { + verifyDaoType(dao_id); + checkAdminsAuth(dao_id); + + auto cw = ContentWrapper(election_config); + + auto startDate = cw.getOrFail( + DETAILS, + upvote_common::items::UPVOTE_STARTDATE + )->getAs(); + + auto duration = cw.getOrFail( + DETAILS, + upvote_common::items::UPVOTE_DURATION + )->getAs(); + + auto round_duration = cw.getOrFail( + DETAILS, + upvote_common::items::ROUND_DURATION + )->getAs(); + + time_point endDate = startDate; + + UpvoteElection upvoteElection(*this, dao_id, UpvoteElectionData{ + .start_date = startDate, + .end_date = endDate, + .status = upvote_common::upvote_status::UPCOMING, + .duration = duration, + .round_duration = round_duration + }); + + // add start round + upvoteElection.addRound(); -// TODO combine this with create - so we extract the data only in one place - -void dao::editupvelc(uint64_t election_id, ContentGroups& election_config) -{ - //TODO: Change existing delegate badges - //end time according to the new upvoteElection.getEndDate(), - //also reschedule for archive - UpvoteElection upvoteElection(*this, election_id); - auto daoId = upvoteElection.getDaoID(); - - verifyDaoType(daoId); - checkAdminsAuth(daoId); - - EOS_CHECK( - upvoteElection.getStatus() == upvote_common::upvote_status::UPCOMING, - "Only upcoming elections can be edited" - ); - - for (auto& rounds : upvoteElection.getRounds()) { - rounds.erase(); + scheduleElectionUpdate(*this, upvoteElection, startDate); } - auto cw = ContentWrapper(election_config); + // TODO combine this with create - so we extract the data only in one place - auto startDate = cw.getOrFail( - DETAILS, - upvote_common::items::UPVOTE_STARTDATE - )->getAs(); + void dao::editupvelc(uint64_t election_id, ContentGroups& election_config) + { + //TODO: Change existing delegate badges + //end time according to the new upvoteElection.getEndDate(), + //also reschedule for archive + UpvoteElection upvoteElection(*this, election_id); + auto daoId = upvoteElection.getDaoID(); - auto duration = cw.getOrFail( - DETAILS, - upvote_common::items::UPVOTE_DURATION - )->getAs(); + verifyDaoType(daoId); + checkAdminsAuth(daoId); - time_point endDate = startDate; + EOS_CHECK( + upvoteElection.getStatus() == upvote_common::upvote_status::UPCOMING, + "Only upcoming elections can be edited" + ); - //Will calculate also what is the endDate for the upvote election - auto rounds = getRounds(election_config, endDate); + for (auto& rounds : upvoteElection.getRounds()) { + rounds.erase(); + } - upvoteElection.setStartDate(startDate); - upvoteElection.setEndDate(endDate); - upvoteElection.setDuration(duration); - upvoteElection.validate(); - upvoteElection.update(); + auto cw = ContentWrapper(election_config); - auto startRound = upvoteElection.getStartRound(); - startRound.erase(); - upvoteElection.addRound(); + auto startDate = cw.getOrFail( + DETAILS, + upvote_common::items::UPVOTE_STARTDATE + )->getAs(); - scheduleElectionUpdate(*this, upvoteElection, startDate); -} + auto duration = cw.getOrFail( + DETAILS, + upvote_common::items::UPVOTE_DURATION + )->getAs(); + + time_point endDate = startDate; + + //Will calculate also what is the endDate for the upvote election + auto rounds = getRounds(election_config, endDate); + + upvoteElection.setStartDate(startDate); + upvoteElection.setEndDate(endDate); + upvoteElection.setDuration(duration); + upvoteElection.validate(); + upvoteElection.update(); + + auto startRound = upvoteElection.getStartRound(); + startRound.erase(); + upvoteElection.addRound(); + + scheduleElectionUpdate(*this, upvoteElection, startDate); + } } From 5d214950288a67d1ea775af9b6899e94afdd9268 Mon Sep 17 00:00:00 2001 From: NIK Date: Wed, 4 Oct 2023 01:13:56 +0800 Subject: [PATCH 28/51] updates and fixes - return value on addRound fix --- include/dao.hpp | 1 + include/upvote_election/common.hpp | 30 +++---- include/upvote_election/election_round.hpp | 3 +- include/upvote_election/upvote_election.hpp | 2 +- src/upvote_election/actions.cpp | 39 +++++++++- src/upvote_election/election_round.cpp | 44 +++++------ src/upvote_election/upvote_election.cpp | 86 +++++++++++++-------- 7 files changed, 131 insertions(+), 74 deletions(-) diff --git a/include/dao.hpp b/include/dao.hpp index f97a3e2..c047a55 100644 --- a/include/dao.hpp +++ b/include/dao.hpp @@ -333,6 +333,7 @@ namespace pricing { //Upvote System ACTION testgrouprng(std::vector ids, uint32_t seed); ACTION testgroupr1(uint32_t num_members, uint32_t seed); + ACTION testround(uint64_t dao_id); ACTION castupvote(uint64_t round_id, uint64_t group_id, name voter, uint64_t voted_id); diff --git a/include/upvote_election/common.hpp b/include/upvote_election/common.hpp index a6afeb9..4aab82b 100644 --- a/include/upvote_election/common.hpp +++ b/include/upvote_election/common.hpp @@ -23,7 +23,7 @@ namespace types { inline constexpr auto ELECTION_UP_VOTE = eosio::name("upvt.vote"); // TODO: Remove this - inline constexpr auto ELECTION_VOTE_GROUP = eosio::name("vote.group"); + inline constexpr auto ELECTION_VOTE_GROUP = eosio::name("vote.group");//?? } namespace upvote_status { @@ -34,24 +34,24 @@ namespace upvote_status { } namespace links { - inline constexpr auto UPCOMING_ELECTION = eosio::name("upcomingelct"); - inline constexpr auto ONGOING_ELECTION = eosio::name("ongoingelct"); - inline constexpr auto PREVIOUS_ELECTION = eosio::name("previouselct"); - inline constexpr auto ELECTION = eosio::name("election"); - inline constexpr auto START_ROUND = eosio::name("startround"); - inline constexpr auto CURRENT_ROUND = eosio::name("currentround"); + inline constexpr auto ELECTION = eosio::name("ue.election"); + inline constexpr auto UPCOMING_ELECTION = eosio::name("ue.upcoming"); + inline constexpr auto ONGOING_ELECTION = eosio::name("ue.ongoing"); + inline constexpr auto PREVIOUS_ELECTION = eosio::name("ue.previous"); + inline constexpr auto START_ROUND = eosio::name("ue.startrnd"); + inline constexpr auto CURRENT_ROUND = eosio::name("ue.currnd"); inline constexpr auto ELECTION_ROUND = eosio::name("ue.round"); inline constexpr auto ELECTION_ROUND_MEMBER = eosio::name("ue.rd.member"); inline constexpr auto ELECTION_GROUP_LINK = eosio::name("ue.group.lnk"); - inline constexpr auto NEXT_ROUND = eosio::name("nextround"); - inline constexpr auto ROUND_CANDIDATE = eosio::name("candidate"); - inline constexpr auto ROUND_WINNER = eosio::name("winner"); - inline constexpr auto ELECTION_GROUP = eosio::name("elctngroup"); + inline constexpr auto NEXT_ROUND = eosio::name("ue.nextrnd"); + inline constexpr auto ROUND_CANDIDATE = eosio::name("ue.candidate"); + inline constexpr auto ROUND_WINNER = eosio::name("ue.winner"); + inline constexpr auto ELECTION_GROUP = eosio::name("ue.elctngrp"); inline constexpr auto UP_VOTE_VOTE = eosio::name("ue.vote"); inline constexpr auto UPVOTE_GROUP_WINNER = eosio::name("ue.winner"); - inline constexpr auto VOTE = eosio::name("vote"); - inline constexpr auto CHIEF_DELEGATE = eosio::name("chiefdelegate"); - inline constexpr auto HEAD_DELEGATE = eosio::name("headdelegate"); + inline constexpr auto VOTE = eosio::name("vote"); // ?? + inline constexpr auto CHIEF_DELEGATE = eosio::name("ue.chiefdel"); + inline constexpr auto HEAD_DELEGATE = eosio::name("ue.headdel"); } namespace items { @@ -60,7 +60,7 @@ namespace items { inline constexpr auto ROUND_DURATION = "duration"; inline constexpr auto ROUND_ID = "round_id"; inline constexpr auto ROUND_TYPE = "type"; - inline constexpr auto WINNER = "winner"; + inline constexpr auto WINNER = "winner"; // ?? delete? } } // namespace hypha::upvote diff --git a/include/upvote_election/election_round.hpp b/include/upvote_election/election_round.hpp index d044d76..8399761 100644 --- a/include/upvote_election/election_round.hpp +++ b/include/upvote_election/election_round.hpp @@ -18,8 +18,7 @@ class ElectionRound : public TypedDocument PROPERTY(type, std::string, Type, USE_GET), PROPERTY(start_date, eosio::time_point, StartDate, USE_GET), PROPERTY(end_date, eosio::time_point, EndDate, USE_GET), - PROPERTY(round_duration, int64_t, RoundDuration, USE_GET), - PROPERTY(delegate_power, int64_t, DelegatePower, USE_GET) + PROPERTY(round_duration, int64_t, RoundDuration, USE_GET) ) public: ElectionRound(dao& dao, uint64_t id); diff --git a/include/upvote_election/upvote_election.hpp b/include/upvote_election/upvote_election.hpp index 1e894b0..b5eff55 100644 --- a/include/upvote_election/upvote_election.hpp +++ b/include/upvote_election/upvote_election.hpp @@ -30,7 +30,7 @@ class UpvoteElection : public TypedDocument uint64_t getDaoID() const; - ElectionRound addRound(); + uint64_t addRound(); std::vector getRounds() const; ElectionRound getCurrentRound() const; diff --git a/src/upvote_election/actions.cpp b/src/upvote_election/actions.cpp index df8ab4d..86f5235 100644 --- a/src/upvote_election/actions.cpp +++ b/src/upvote_election/actions.cpp @@ -207,7 +207,7 @@ namespace hypha { for (auto& [roundId, roundData] : rounds) { // TODO: What is this?? - roundData.delegate_power = getDelegatePower(roundId); + //roundData.delegate_power = getDelegatePower(roundId); roundData.start_date = startDate; @@ -537,6 +537,39 @@ namespace hypha { } + void dao::testround(uint64_t dao_id) { + require_auth(get_self()); + + auto edge = Edge::get(get_self(), dao_id, upvote_common::links::ELECTION); + + auto electionId = edge.getToNode(); + + eosio::print(" election id: ", electionId, " ::: "); + + UpvoteElection upvoteElection(*this, electionId); + + upvoteElection.addRound(); + + // for (uint32_t n = 0; n < num_members; ++n) { + // auto rounds = count_rounds(n); + + // std::vector group_sizes = get_group_sizes(n, rounds); + + // eosio::print(" N: ", n, " rounds: ", rounds, " g_sizes: "); + + // for (uint32_t s : group_sizes) { + // eosio::print(s, " "); + // } + + // eosio::print(" ::: "); + + + // } + + + } + + void dao::testgrouprng(std::vector ids, uint32_t seed) { require_auth(get_self()); @@ -933,7 +966,7 @@ namespace hypha { upvote_common::items::ROUND_DURATION )->getAs(); - time_point endDate = startDate; + time_point endDate = startDate + eosio::seconds(round_duration); UpvoteElection upvoteElection(*this, dao_id, UpvoteElectionData{ .start_date = startDate, @@ -944,7 +977,7 @@ namespace hypha { }); // add start round - upvoteElection.addRound(); + auto roundId = upvoteElection.addRound(); scheduleElectionUpdate(*this, upvoteElection, startDate); } diff --git a/src/upvote_election/election_round.cpp b/src/upvote_election/election_round.cpp index 294439d..7553c38 100644 --- a/src/upvote_election/election_round.cpp +++ b/src/upvote_election/election_round.cpp @@ -50,23 +50,23 @@ namespace hypha::upvote_election { using namespace upvote_election::common; - static void validateStartDate(const time_point& startDate) - { - //Only valid if start date is in the future - EOS_CHECK( - startDate > eosio::current_time_point(), - _s("Election start date must be in the future") - ) - } + // static void validateStartDate(const time_point& startDate) + // { + // //Only valid if start date is in the future + // EOS_CHECK( + // startDate > eosio::current_time_point(), + // _s("Election start date must be in the future") + // ) + // } - static void validateEndDate(const time_point& startDate, const time_point& endDate) - { - //Only valid if start date is in the future - EOS_CHECK( - endDate > startDate, - to_str("End date must happen after start date: ", startDate, " to ", endDate) - ); - } + // static void validateEndDate(const time_point& startDate, const time_point& endDate) + // { + // //Only valid if start date is in the future + // EOS_CHECK( + // endDate > startDate, + // to_str("End date must happen after start date: ", startDate, " to ", endDate) + // ); + // } ElectionRound::ElectionRound(dao& dao, uint64_t id) : TypedDocument(dao, id, types::ELECTION_ROUND) @@ -75,14 +75,14 @@ namespace hypha::upvote_election { ElectionRound::ElectionRound(dao& dao, uint64_t election_id, Data data) : TypedDocument(dao, types::ELECTION_ROUND) { - EOS_CHECK( - data.delegate_power >= 0, - "Delegate Power must be greater or equal to 0" - ); + // EOS_CHECK( + // data.delegate_power >= 0, + // "Delegate Power must be greater or equal to 0" + // ); - validateStartDate(data.start_date); + // validateStartDate(data.start_date); - validateEndDate(data.start_date, data.end_date); + // validateEndDate(data.start_date, data.end_date); auto cgs = convert(std::move(data)); diff --git a/src/upvote_election/upvote_election.cpp b/src/upvote_election/upvote_election.cpp index 43c425a..0536520 100644 --- a/src/upvote_election/upvote_election.cpp +++ b/src/upvote_election/upvote_election.cpp @@ -16,23 +16,23 @@ namespace hypha::upvote_election { : TypedDocument(dao, id, types::UPVOTE_ELECTION) {} - static void validateStartDate(const time_point& startDate) - { - //Only valid if start date is in the future - EOS_CHECK( - startDate > eosio::current_time_point(), - _s("Election start date must be in the future") - ) - } - - static void validateEndDate(const time_point& startDate, const time_point& endDate) - { - //Only valid if start date is in the future - EOS_CHECK( - endDate > startDate, - to_str("End date must happen after start date: ", startDate, " to ", endDate) - ); - } + // static void validateStartDate(const time_point& startDate) + // { + // //Only valid if start date is in the future + // EOS_CHECK( + // startDate > eosio::current_time_point(), + // _s("Election start date must be in the future") + // ) + // } + + // static void validateEndDate(const time_point& startDate, const time_point& endDate) + // { + // //Only valid if start date is in the future + // EOS_CHECK( + // endDate > startDate, + // to_str("End date must happen after start date: ", startDate, " to ", endDate) + // ); + // } UpvoteElection::UpvoteElection(dao& dao, uint64_t dao_id, Data data) : TypedDocument(dao, types::UPVOTE_ELECTION) @@ -120,46 +120,70 @@ namespace hypha::upvote_election { return rounds; } - ElectionRound UpvoteElection::addRound() + uint64_t UpvoteElection::addRound() { - ElectionRoundData roundData; - roundData.type = round_types::DELEGATE; - roundData.round_duration = getRoundDuration(); + eosio::time_point startDate; - std::unique_ptr currentRound; + auto roundDuration = getRoundDuration(); + + eosio::print(" round dur ", roundDuration); + std::unique_ptr currentRound; auto [exists, currentRoundEdge] = Edge::getIfExists(getDao().get_self(), getId(), links::CURRENT_ROUND); if (!exists) { startDate = getStartDate(); + eosio::print(" first round ", std::to_string(startDate.sec_since_epoch())); } else { currentRound = std::make_unique( getDao(), currentRoundEdge.getToNode() ); + + eosio::print(" current round ", currentRound->getId()); + startDate = currentRound->getEndDate(); - //currentRound = std::move(current); } - roundData.start_date = startDate; - roundData.end_date = startDate + eosio::seconds(roundData.round_duration); - auto round = std::make_unique( + auto endDate = startDate + eosio::seconds(roundDuration); + + ElectionRoundData roundData{ + .type = round_types::DELEGATE, + .start_date = startDate, + .end_date = endDate, + .round_duration = roundDuration + }; + + eosio::print(" round: start date: ", startDate.sec_since_epoch(), " dur ", roundDuration, " end date ", endDate.sec_since_epoch()); + + ElectionRound round( getDao(), getId(), roundData ); - setEndDate(roundData.end_date); + setEndDate(std::move(endDate)); + setEndDate(endDate); + update(); + + eosio::print(" upd "); if (!exists) { - setStartRound(round.get()); + eosio::print(" new round - exists: ", exists); + setStartRound(&round); } else { - currentRound->setNextRound(round.get()); + eosio::print(" round exists: ", exists, " ", currentRound->getId()); + + currentRound->setNextRound(&round); } + eosio::print(" 1 addRound finished. "); + + return round.getId(); } void UpvoteElection::setStartRound(ElectionRound* startRound) const { + Edge( getDao().get_self(), getDao().get_self(), @@ -167,6 +191,7 @@ namespace hypha::upvote_election { startRound->getId(), links::START_ROUND ); + } void UpvoteElection::setCurrentRound(ElectionRound* currenttRound) const @@ -232,8 +257,7 @@ namespace hypha::upvote_election { void UpvoteElection::validate() { auto startDate = getStartDate(); - validateStartDate(startDate); - validateEndDate(startDate, getEndDate()); + // validateStartDate(startDate); } } \ No newline at end of file From e726c40a30e0196943053f83b365ec49497b632a Mon Sep 17 00:00:00 2001 From: Gerard097 Date: Tue, 3 Oct 2023 15:22:03 -0600 Subject: [PATCH 29/51] Change Voice default multiplier to x1 --- src/proposals/assignment_proposal.cpp | 2 +- src/proposals/payout_proposal.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/proposals/assignment_proposal.cpp b/src/proposals/assignment_proposal.cpp index cd54fb0..4be2a76 100644 --- a/src/proposals/assignment_proposal.cpp +++ b/src/proposals/assignment_proposal.cpp @@ -179,7 +179,7 @@ namespace hypha .periodSalary = normalizeToken(usdSalaryPerPeriod.getAs()) * (timeShare / 100.0), .rewardToPegRatio = normalizeToken(rewardPegVal), .deferredPerc = deferred / 100.0, - .voiceMultipler = getMultiplier(m_daoSettings, common::VOICE_MULTIPLIER, 2.0), + .voiceMultipler = getMultiplier(m_daoSettings, common::VOICE_MULTIPLIER, 1.0), .rewardMultipler = getMultiplier(m_daoSettings, common::REWARD_MULTIPLIER, 1.0), .pegMultipler = getMultiplier(m_daoSettings, common::PEG_MULTIPLIER, 1.0) }; diff --git a/src/proposals/payout_proposal.cpp b/src/proposals/payout_proposal.cpp index 2d9fcc1..cbc1198 100644 --- a/src/proposals/payout_proposal.cpp +++ b/src/proposals/payout_proposal.cpp @@ -46,7 +46,7 @@ void PayoutProposal::checkTokenItems(Settings* daoSettings, ContentWrapper conte .periodSalary = normalizeToken(usd), .rewardToPegRatio = normalizeToken(rewardPegVal), .deferredPerc = deferred / 100.0, - .voiceMultipler = getMultiplier(daoSettings, common::VOICE_MULTIPLIER, 2.0), + .voiceMultipler = getMultiplier(daoSettings, common::VOICE_MULTIPLIER, 1.0), .rewardMultipler = getMultiplier(daoSettings, common::REWARD_MULTIPLIER, 1.0), .pegMultipler = getMultiplier(daoSettings, common::PEG_MULTIPLIER, 1.0) }, tokens); From e1bbd5a2f64ac22afd494c9fa8cf301b2e5b4137 Mon Sep 17 00:00:00 2001 From: NIK Date: Fri, 6 Oct 2023 00:15:27 +0800 Subject: [PATCH 30/51] updates --- include/dao.hpp | 3 +- src/upvote_election/actions.cpp | 57 ++++++++++++++++++++++--- src/upvote_election/election_group.cpp | 24 ++--------- src/upvote_election/election_round.cpp | 2 + src/upvote_election/upvote_election.cpp | 11 ----- 5 files changed, 58 insertions(+), 39 deletions(-) diff --git a/include/dao.hpp b/include/dao.hpp index c047a55..935df05 100644 --- a/include/dao.hpp +++ b/include/dao.hpp @@ -336,11 +336,12 @@ namespace pricing { ACTION testround(uint64_t dao_id); ACTION castupvote(uint64_t round_id, uint64_t group_id, name voter, uint64_t voted_id); + ACTION uesubmitseed(uint64_t dao_id, eosio::checksum256 seed, name account); ACTION createupvelc(uint64_t dao_id, ContentGroups& election_config); ACTION editupvelc(uint64_t election_id, ContentGroups& election_config); ACTION cancelupvelc(uint64_t election_id); - ACTION updateupvelc(uint64_t election_id, bool reschedule); + ACTION updateupvelc(uint64_t election_id, bool reschedule, bool force); diff --git a/src/upvote_election/actions.cpp b/src/upvote_election/actions.cpp index 86f5235..37071fb 100644 --- a/src/upvote_election/actions.cpp +++ b/src/upvote_election/actions.cpp @@ -280,7 +280,7 @@ namespace hypha { eosio::permission_level(dao.get_self(), eosio::name("active")), dao.get_self(), eosio::name("updateupvelc"), - std::make_tuple(election.getId(), true) + std::make_tuple(election.getId(), true, false) )); EOS_CHECK( @@ -695,11 +695,16 @@ namespace hypha { } //Check if we need to update an ongoing elections status: - //upcoming -> ongoing - //ongoing -> finished - //or change the current round - void dao::updateupvelc(uint64_t election_id, bool reschedule) + // upcoming -> ongoing + // ongoing -> finished + // or change the current round + // reschedule flag: Whether or not to put another deferred transaction to try again (should be true in normal use) + // force flag: force update of status - for debugging, not used in normal use - true requires self authorization + void dao::updateupvelc(uint64_t election_id, bool reschedule, bool force) { + if (force) { + require_auth(get_self()); + } //eosio::require_auth(); UpvoteElection election(*this, election_id); @@ -712,9 +717,15 @@ namespace hypha { if (status == upvote_common::upvote_status::UPCOMING) { auto start = election.getStartDate(); + std::string timeStrNow = eosio::time_point_sec(now).to_string(); + std::string timeStrStart = eosio::time_point_sec(start).to_string(); + + eosio::print(" upcoming ", timeStrNow, " ", timeStrStart); + //Let's update as we already started - if (start <= now) { + if (start <= now || force) { // START.... + eosio::print(" -> starting "); Edge::get(get_self(), daoId, election.getId(), upvote_common::links::UPCOMING_ELECTION).erase(); @@ -739,6 +750,8 @@ namespace hypha { uint64_t seed = checksum256_to_uint32(initialSeed); + eosio::print(" init sd: ", initialSeed, " seed ", seed); + // Create a random number generator with the seed UERandomGenerator rng(seed, 0); @@ -750,6 +763,12 @@ namespace hypha { auto groups = createGroups(randomIds, 4); // TODO: CHANGE THIS TO EDENIA CODE for (auto& groupMembers : groups) { + + eosio::print(" add gr: ", groupMembers.size(), ": "); + for (uint64_t m : groupMembers) { + eosio::print(m, ", "); + } + startRound.addElectionGroup(groupMembers); } @@ -759,13 +778,21 @@ namespace hypha { scheduleElectionUpdate(*this, election, startRound.getEndDate()); } else if (reschedule) { + eosio::print(" -> not ready, rescheduling "); + scheduleElectionUpdate(*this, election, start); } } else if (status == upvote_common::upvote_status::ONGOING) { + + eosio::print(" ongoing "); + auto currentRound = election.getCurrentRound(); auto end = currentRound.getEndDate(); - if (end <= now) { + if (end <= now || force) { + + + eosio::print(" current round ended "); auto winners = currentRound.getWinners(); @@ -873,6 +900,22 @@ namespace hypha { election.update(); } + void dao::uesubmitseed(uint64_t dao_id, eosio::checksum256 seed, name account) { + eosio::require_auth(account); + + auto edge = Edge::get(get_self(), dao_id, upvote_common::links::ELECTION); + + auto electionId = edge.getToNode(); + + UpvoteElection upvoteElection(*this, electionId); + + upvoteElection.setSeed(seed); + + upvoteElection.update(); + + + } + void dao::castupvote(uint64_t round_id, uint64_t group_id, name voter, uint64_t voted_id) { // Check auth diff --git a/src/upvote_election/election_group.cpp b/src/upvote_election/election_group.cpp index 250d0e6..bf5418f 100644 --- a/src/upvote_election/election_group.cpp +++ b/src/upvote_election/election_group.cpp @@ -26,6 +26,8 @@ ElectionGroup::ElectionGroup(dao& dao, uint64_t round_id, std::vector EOS_CHECK(member_ids.size() <= 6, "max 6 members in group"); + eosio::print(" adding election group ", getId(), " to round ", round_id); + // create edges to all members (max 6) for (size_t i = 0; i < member_ids.size(); ++i) { Edge( @@ -37,6 +39,8 @@ ElectionGroup::ElectionGroup(dao& dao, uint64_t round_id, std::vector ); } + eosio::print(" link from ", round_id, " to ", getId(), " "); + // Create edge from the round to this group Edge( getDao().get_self(), @@ -126,26 +130,6 @@ void ElectionGroup::vote(int64_t from, int64_t to) update(); } - - // if from edge, delete the edge and create a new upvote doc - or change its voted field and save it back? - // if no from edge, create a new UpVoteVote doc - - - - - // if (exists) { - // vote = UpVoteVote(getDao(), edge.getToNode()); - // } else { - // vote = UpVoteVote() - // } - - // Edge( - // getDao().get_self(), - // getDao().get_self(), - // getId(), - // nextRound->getId(), - // links::NEXT_ROUND - // ); } diff --git a/src/upvote_election/election_round.cpp b/src/upvote_election/election_round.cpp index 7553c38..096ef85 100644 --- a/src/upvote_election/election_round.cpp +++ b/src/upvote_election/election_round.cpp @@ -90,6 +90,8 @@ namespace hypha::upvote_election { auto type = getType(); + // eosio::print(" add edge - election id: ", election_id, " -> round id: ", getId(), " name: ", links::ELECTION_ROUND); + Edge( getDao().get_self(), getDao().get_self(), diff --git a/src/upvote_election/upvote_election.cpp b/src/upvote_election/upvote_election.cpp index 0536520..91e6f8c 100644 --- a/src/upvote_election/upvote_election.cpp +++ b/src/upvote_election/upvote_election.cpp @@ -125,22 +125,17 @@ namespace hypha::upvote_election { eosio::time_point startDate; auto roundDuration = getRoundDuration(); - - eosio::print(" round dur ", roundDuration); std::unique_ptr currentRound; auto [exists, currentRoundEdge] = Edge::getIfExists(getDao().get_self(), getId(), links::CURRENT_ROUND); if (!exists) { startDate = getStartDate(); - eosio::print(" first round ", std::to_string(startDate.sec_since_epoch())); } else { currentRound = std::make_unique( getDao(), currentRoundEdge.getToNode() ); - eosio::print(" current round ", currentRound->getId()); - startDate = currentRound->getEndDate(); } auto endDate = startDate + eosio::seconds(roundDuration); @@ -152,8 +147,6 @@ namespace hypha::upvote_election { .round_duration = roundDuration }; - eosio::print(" round: start date: ", startDate.sec_since_epoch(), " dur ", roundDuration, " end date ", endDate.sec_since_epoch()); - ElectionRound round( getDao(), getId(), @@ -164,8 +157,6 @@ namespace hypha::upvote_election { setEndDate(endDate); update(); - eosio::print(" upd "); - if (!exists) { eosio::print(" new round - exists: ", exists); setStartRound(&round); @@ -174,8 +165,6 @@ namespace hypha::upvote_election { currentRound->setNextRound(&round); } - eosio::print(" 1 addRound finished. "); - return round.getId(); } From 4b0d1311dfa298bda8f1cd087f5b9013d976cf4a Mon Sep 17 00:00:00 2001 From: Gerard097 Date: Thu, 5 Oct 2023 16:22:42 -0600 Subject: [PATCH 31/51] Disable membership check to create dao action --- src/dao.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/dao.cpp b/src/dao.cpp index b36d9ad..1ebe785 100644 --- a/src/dao.cpp +++ b/src/dao.cpp @@ -2007,17 +2007,17 @@ void dao::createdao(ContentGroups& config) else { //Just do this check on mainnet - if (!isTestnet()) { + // if (!isTestnet()) { - auto onboarder = configCW.getOrFail(DETAILS, common::ONBOARDER_ACCOUNT)->getAs(); + // auto onboarder = configCW.getOrFail(DETAILS, common::ONBOARDER_ACCOUNT)->getAs(); - auto hyphaId = getDAOID("hypha"_n); + // auto hyphaId = getDAOID("hypha"_n); - EOS_CHECK( - hyphaId.has_value() && Member::isMember(*this, *hyphaId, onboarder), - "You are not allowed to call this action" - ); - } + // EOS_CHECK( + // hyphaId.has_value() && Member::isMember(*this, *hyphaId, onboarder), + // "You are not allowed to call this action" + // ); + // } //Regular DAO creation auto daoDoc = createDao(nullptr); From 37c6c1daf31e5a4b5476f6ba1708b82e09558723 Mon Sep 17 00:00:00 2001 From: NIK Date: Fri, 6 Oct 2023 23:14:47 +0800 Subject: [PATCH 32/51] reffactoring, creating subsequent rounds and winner round --- include/dao.hpp | 3 - include/upvote_election/upvote_election.hpp | 4 +- src/proposals/badge_assignment_proposal.cpp | 11 +- src/upvote_election/actions.cpp | 220 ++++++++++---------- src/upvote_election/election_group.cpp | 2 +- src/upvote_election/election_round.cpp | 10 +- src/upvote_election/upvote_election.cpp | 5 +- 7 files changed, 124 insertions(+), 131 deletions(-) diff --git a/include/dao.hpp b/include/dao.hpp index 935df05..b0e0521 100644 --- a/include/dao.hpp +++ b/include/dao.hpp @@ -511,9 +511,6 @@ namespace pricing { private: - std::vector shuffleVector(std::vector& ids, UERandomGenerator rng); - std::vector> createGroups(const std::vector& ids, int minGroupSize); - void onRewardTransfer(const name& from, const name& to, const asset& amount); //AssetBatch calculatePendingClaims(uint64_t assignmentID, const AssetBatch& daoTokens); diff --git a/include/upvote_election/upvote_election.hpp b/include/upvote_election/upvote_election.hpp index b5eff55..7104abd 100644 --- a/include/upvote_election/upvote_election.hpp +++ b/include/upvote_election/upvote_election.hpp @@ -17,7 +17,7 @@ class UpvoteElection : public TypedDocument PROPERTY(start_date, eosio::time_point, StartDate, USE_GETSET), PROPERTY(end_date, eosio::time_point, EndDate, USE_GETSET), PROPERTY(seed, eosio::checksum256, Seed, USE_GETSET), - PROPERTY(runningSeed, int32_t, RunningSeed, USE_GETSET), + PROPERTY(runningSeed, int64_t, RunningSeed, USE_GETSET), PROPERTY(status, std::string, Status, USE_GETSET), PROPERTY(duration, int64_t, Duration, USE_GETSET), PROPERTY(round_duration, int64_t, RoundDuration, USE_GET) @@ -30,7 +30,7 @@ class UpvoteElection : public TypedDocument uint64_t getDaoID() const; - uint64_t addRound(); + ElectionRound addRound(); std::vector getRounds() const; ElectionRound getCurrentRound() const; diff --git a/src/proposals/badge_assignment_proposal.cpp b/src/proposals/badge_assignment_proposal.cpp index 130763f..c9c281c 100644 --- a/src/proposals/badge_assignment_proposal.cpp +++ b/src/proposals/badge_assignment_proposal.cpp @@ -95,7 +95,8 @@ namespace hypha return; } - //Voter badge and Delegate badge has to be auto approved + // Voter badge and Delegate badge has to be auto approved + // Chief and head delegates are also auto approved, but the proposal can only be created by the DAO if (badges::isSelfApproveBadge(badgeInfo.systemType)) { selfApprove = true; @@ -116,11 +117,13 @@ namespace hypha int64_t duration = 0; - //0 value 45means that the upvote election was imported + //0 value means that the upvote election was imported if (election) { upvote_election::UpvoteElection upvoteElection(m_dao, election); - //Set start as when the election is finished - start = upvoteElection.getEndDate(); + //Set start as when the election started, + // this way duration syncs with the duration of the election + + start = upvoteElection.getStartDate(); duration = upvoteElection.getDuration(); } else { diff --git a/src/upvote_election/actions.cpp b/src/upvote_election/actions.cpp index 37071fb..42ed3d9 100644 --- a/src/upvote_election/actions.cpp +++ b/src/upvote_election/actions.cpp @@ -270,6 +270,83 @@ namespace hypha { ); } + // Requirements: + // - Except for the last round, the group size shall be in [4,6] + // - The last round has a minimum group size of 3 + // - The maximum group size shall be as small as possible + // - The group sizes within a round shall have a maximum difference of 1 + static std::vector> createGroups(const std::vector& ids, int minGroupSize) { + std::vector> groups; + + // Calculate the number of groups needed - group size fills up to 6 max, except when there's + // only 1 group, which goes up to 11 + int numGroups = ids.size() / minGroupSize; + + // Create the groups with an initial capacity of 4 + for (int i = 0; i < numGroups; ++i) { + groups.push_back(std::vector()); + groups.back().reserve(4); + } + + // Initialize group iterators + auto currentGroup = groups.begin(); + auto endGroup = groups.end(); + + // Iterate over the IDs and distribute them into groups + for (const auto& id : ids) { + // Add the ID to the current group + currentGroup->push_back(id); + + // Move to the next group (take turns) + ++currentGroup; + + // Wrap back to the beginning of the groups + if (currentGroup == endGroup) { + currentGroup = groups.begin(); + } + } + + return groups; + } + + /// @brief Use the Edenia method to figure out number of rounds and group sizes + /// @param ids a list of member Ids + /// @return A list of groups of member Ids. + static std::vector> createGroupsEden(const std::vector& ids) { + // Edenia code for counting number of rounds and min group sizes for the rounds + auto n = ids.size(); + auto rounds = count_rounds(n); + std::vector group_sizes = get_group_sizes(n, rounds); + int minGroupsSize = group_sizes[0]; + return createGroups(ids, minGroupsSize); + } + + static void initRound(UpvoteElection& election, ElectionRound& round, uint32_t seed, std::vector delegateIds) { + + // Create a random number generator with the seed + UERandomGenerator rng(seed, 0); + + auto randomIds = delegateIds; + + std::shuffle(randomIds.begin(), randomIds.end(), rng); + + election.setRunningSeed(rng.seed); + election.update(); + + auto groups = createGroupsEden(randomIds); + + for (auto& groupMembers : groups) { + + eosio::print(" add gr: ", groupMembers.size(), ": "); + for (uint64_t m : groupMembers) { + eosio::print(m, ", "); + } + + round.addElectionGroup(groupMembers); + } + + } + static void scheduleElectionUpdate(dao& dao, UpvoteElection& election, time_point date) { if (date < eosio::current_time_point()) return; @@ -300,9 +377,10 @@ namespace hypha { dhoSettings->setSetting(Content{ "next_schedule_id", nextID + 1 }); } - static void assignDelegateBadges(dao& dao, uint64_t daoId, uint64_t electionId, const std::vector& chiefDelegates, std::optional headDelegate, eosio::transaction* trx = nullptr) + static void assignDelegateBadges(dao& dao, uint64_t daoId, uint64_t electionId, const std::vector& chiefDelegates, uint64_t headDelegate, eosio::transaction* trx = nullptr) { - //Generate proposals for each one of the delegates + // Generate proposals for each one of the delegates + // Note: These are auto-approved and instantly on. auto createAssignment = [&](const std::string& title, uint64_t member, uint64_t badge) { @@ -346,11 +424,13 @@ namespace hypha { auto headBadge = TypedDocument::withType(dao, headBadgeEdge.getToNode(), common::BADGE_NAME); for (auto& chief : chiefDelegates) { - createAssignment("Chief Delegate", chief, chiefBadge.getID()); + if (chief != headDelegate) { + createAssignment("Chief Delegate", chief, chiefBadge.getID()); + } } if (headDelegate) { - createAssignment("Head Delegate", *headDelegate, headBadge.getID()); + createAssignment("Head Delegate", headDelegate, headBadge.getID()); } } @@ -581,7 +661,9 @@ namespace hypha { UERandomGenerator rng(seed, 0); - auto randomIds = shuffleVector(ids, rng); + auto randomIds = ids; + + std::shuffle(randomIds.begin(), randomIds.end(), rng); eosio::print(" random ids: "); for (const uint64_t& element : randomIds) { @@ -610,16 +692,6 @@ namespace hypha { } - std::vector dao::shuffleVector(std::vector& ids, UERandomGenerator rng) { - - // Shuffle the vector using the RNG - std::shuffle(ids.begin(), ids.end(), rng); - - return ids; - - } - - // 36 / 6 = 6 => 1 round, 1 HD round size_t numrounds(size_t num_delegates) { size_t rounds = 1; @@ -655,44 +727,6 @@ namespace hypha { // }; - // Requirements: - // - Except for the last round, the group size shall be in [4,6] - // - The last round has a minimum group size of 3 - // - The maximum group size shall be as small as possible - // - The group sizes within a round shall have a maximum difference of 1 - std::vector> dao::createGroups(const std::vector& ids, int minGroupSize) { - std::vector> groups; - - // Calculate the number of groups needed - group size fills up to 6 max, except when there's - // only 1 group, which goes up to 11 - int numGroups = ids.size() / minGroupSize; - - // Create the groups with an initial capacity of 4 - for (int i = 0; i < numGroups; ++i) { - groups.push_back(std::vector()); - groups.back().reserve(4); - } - - // Initialize group iterators - auto currentGroup = groups.begin(); - auto endGroup = groups.end(); - - // Iterate over the IDs and distribute them into groups - for (const auto& id : ids) { - // Add the ID to the current group - currentGroup->push_back(id); - - // Move to the next group (take turns) - ++currentGroup; - - // Wrap back to the beginning of the groups - if (currentGroup == endGroup) { - currentGroup = groups.begin(); - } - } - - return groups; - } //Check if we need to update an ongoing elections status: // upcoming -> ongoing @@ -745,35 +779,11 @@ namespace hypha { for (auto& delegate : delegates) { delegateIds.push_back(delegate.getToNode()); } - auto initialSeed = election.getSeed(); - - uint64_t seed = checksum256_to_uint32(initialSeed); - + uint32_t seed = checksum256_to_uint32(initialSeed); eosio::print(" init sd: ", initialSeed, " seed ", seed); - // Create a random number generator with the seed - UERandomGenerator rng(seed, 0); - - auto randomIds = shuffleVector(delegateIds, rng); - - election.setRunningSeed(rng.seed); - election.update(); - - auto groups = createGroups(randomIds, 4); // TODO: CHANGE THIS TO EDENIA CODE - - for (auto& groupMembers : groups) { - - eosio::print(" add gr: ", groupMembers.size(), ": "); - for (uint64_t m : groupMembers) { - eosio::print(m, ", "); - } - - startRound.addElectionGroup(groupMembers); - } - - // analyze what this is and if we need it - // setupCandidates(startRound.getId(), delegateIds); + initRound(election, startRound, seed, delegateIds); scheduleElectionUpdate(*this, election, startRound.getEndDate()); } @@ -790,65 +800,49 @@ namespace hypha { auto currentRound = election.getCurrentRound(); auto end = currentRound.getEndDate(); if (end <= now || force) { - - eosio::print(" current round ended "); auto winners = currentRound.getWinners(); for (auto& winner : winners) { + eosio::print(winner, ", "); // DEBUG REMOVE Edge(get_self(), get_self(), currentRound.getId(), winner, upvote_common::links::ROUND_WINNER); } Edge::get(get_self(), election_id, upvote_common::links::CURRENT_ROUND).erase(); - if (auto nextRound = currentRound.getNextRound()) { + if (winners.size() > 11) { // set up the next round + auto round = election.addRound(); - election.setCurrentRound(nextRound.get()); + election.setCurrentRound(&round); + + int32_t seed = election.getRunningSeed(); - // setupCandidates(nextRound->getId(), winners); + initRound(election, round, seed, winners); - // round.addCandidate adds candidates - for (auto& winner : winners) { - // TODO: Change all this - Nik - //nextRound->addCandidate(winner); - } - - scheduleElectionUpdate(*this, election, nextRound->getEndDate()); + scheduleElectionUpdate(*this, election, round.getEndDate()); } else { + eosio::print(" election ended. "); // The election has ended. - + + // delete ongoing election link Edge::get(get_self(), daoId, election.getId(), upvote_common::links::ONGOING_ELECTION).erase(); + // add previous election link Edge(get_self(), get_self(), daoId, election.getId(), upvote_common::links::PREVIOUS_ELECTION); - //TODO: Setup head & chief badges - - if (currentRound.getType() == upvote_common::round_types::HEAD) { - //Get previous round for chief delegates - auto chiefs = election.getCurrentRound().getWinners(); - - //Remove head delegate - chiefs.erase( - std::remove_if( - chiefs.begin(), - chiefs.end(), - [head = winners.at(0)](uint64_t id) { return id == head; } - ), - chiefs.end() - ); + // select head + UERandomGenerator rng(seed, 0); + auto headIndex = rng.operator() % winners.size(); + auto headDelegate = winners[headIndex]; - assignDelegateBadges(*this, daoId, election.getId(), chiefs, winners.at(0)); - } - //No head delegate - else { - assignDelegateBadges(*this, daoId, election.getId(), winners, std::nullopt); - } + eosio::print(" head del ix: ", headIndex, " winners size: ", winners.size(), " head del: ", headDelegate, " "); + assignDelegateBadges(*this, daoId, election.getId(), winners, headDelegate); election.setStatus(upvote_common::upvote_status::FINISHED); } diff --git a/src/upvote_election/election_group.cpp b/src/upvote_election/election_group.cpp index bf5418f..d51a6e4 100644 --- a/src/upvote_election/election_group.cpp +++ b/src/upvote_election/election_group.cpp @@ -24,7 +24,7 @@ ElectionGroup::ElectionGroup(dao& dao, uint64_t round_id, std::vector initializeDocument(dao, cgs); - EOS_CHECK(member_ids.size() <= 6, "max 6 members in group"); + //EOS_CHECK(member_ids.size() <= 6, "max 6 members in group"); // the last group is up to 11 members?! eosio::print(" adding election group ", getId(), " to round ", round_id); diff --git a/src/upvote_election/election_round.cpp b/src/upvote_election/election_round.cpp index 096ef85..564cd38 100644 --- a/src/upvote_election/election_round.cpp +++ b/src/upvote_election/election_round.cpp @@ -123,10 +123,9 @@ namespace hypha::upvote_election { std::vector groupEdges = getDao().getGraph().getEdgesFrom(getId(), links::ELECTION_GROUP_LINK); for (auto& edge : groupEdges) { ElectionGroup group(getDao(), edge.getToNode()); - auto cw = group.getDocument().getContentWrapper(); - auto [idx, item] = cw.get(DETAILS, items::WINNER); - if (idx != -1) { - winners.push_back(item->getAs()); + int64_t winner = group.getWinner(); + if (winner != -1) { + winners.push_back(winner); } } @@ -166,7 +165,8 @@ namespace hypha::upvote_election { getId(), accound_ids, ElectionGroupData{ - .member_count = accound_ids.size() + .member_count = accound_ids.size(), + .winner = -1 } ); diff --git a/src/upvote_election/upvote_election.cpp b/src/upvote_election/upvote_election.cpp index 91e6f8c..9bff47b 100644 --- a/src/upvote_election/upvote_election.cpp +++ b/src/upvote_election/upvote_election.cpp @@ -120,7 +120,7 @@ namespace hypha::upvote_election { return rounds; } - uint64_t UpvoteElection::addRound() + ElectionRound UpvoteElection::addRound() { eosio::time_point startDate; @@ -154,7 +154,6 @@ namespace hypha::upvote_election { ); setEndDate(std::move(endDate)); - setEndDate(endDate); update(); if (!exists) { @@ -165,7 +164,7 @@ namespace hypha::upvote_election { currentRound->setNextRound(&round); } - return round.getId(); + return round; } From 0a47c4537fd494a7a023be46483f8be6e1b3e772 Mon Sep 17 00:00:00 2001 From: NIK Date: Mon, 9 Oct 2023 15:53:27 +0800 Subject: [PATCH 33/51] assignDelegateBadges can remove existing fix multiple definitions of delegate badge names --- include/upvote_election/common.hpp | 6 +- src/upvote_election/actions.cpp | 121 +++++++++++++++++++++++++---- 2 files changed, 110 insertions(+), 17 deletions(-) diff --git a/include/upvote_election/common.hpp b/include/upvote_election/common.hpp index 4aab82b..a848470 100644 --- a/include/upvote_election/common.hpp +++ b/include/upvote_election/common.hpp @@ -50,8 +50,10 @@ namespace links { inline constexpr auto UP_VOTE_VOTE = eosio::name("ue.vote"); inline constexpr auto UPVOTE_GROUP_WINNER = eosio::name("ue.winner"); inline constexpr auto VOTE = eosio::name("vote"); // ?? - inline constexpr auto CHIEF_DELEGATE = eosio::name("ue.chiefdel"); - inline constexpr auto HEAD_DELEGATE = eosio::name("ue.headdel"); + + // these are defined in badges::common::links + // inline constexpr auto CHIEF_DELEGATE + // inline constexpr auto HEAD_DELEGATE } namespace items { diff --git a/src/upvote_election/actions.cpp b/src/upvote_election/actions.cpp index 42ed3d9..0561d93 100644 --- a/src/upvote_election/actions.cpp +++ b/src/upvote_election/actions.cpp @@ -377,17 +377,107 @@ namespace hypha { dhoSettings->setSetting(Content{ "next_schedule_id", nextID + 1 }); } - static void assignDelegateBadges(dao& dao, uint64_t daoId, uint64_t electionId, const std::vector& chiefDelegates, uint64_t headDelegate, eosio::transaction* trx = nullptr) + static void assignDelegateBadges( + dao& dao, + uint64_t daoId, + uint64_t electionId, + const std::vector& chiefDelegates, + uint64_t headDelegate, + bool deleteExistingBadges, + eosio::transaction* trx = nullptr) { + // TODO This removes and adds badges. They're all pretty expensive operations but at most there's 11 badges + // So it should work in all cases. If not we could pack it into deferred transactions. + + // Delete existing if required + if (deleteExistingBadges) { + eosio::transaction removeOldBadgesTrx; + + //Remove existing Head Delegate/Chief Delegate badges if any + // TODO: Make this a static(?) helper method and invoke from where needed. + auto cleanBadgesOf = [&](const name& badgeEdge) { + auto badgeId = Edge::get(dao.get_self(), dao.getRootID(), badgeEdge).getToNode(); + + auto badgeAssignmentEdges = dao.getGraph().getEdgesFrom(badgeId, common::ASSIGNMENT); + + if (badgeAssignmentEdges.size() == 0) { + eosio::print(" no existing badge holders ", badgeEdge); + return; + } + + //Filter out those that are not from the specified DAO + auto badgeAssignments = std::vector{}; + badgeAssignments.reserve(badgeAssignmentEdges.size()); + + std::transform( + badgeAssignmentEdges.begin(), + badgeAssignmentEdges.end(), + std::back_inserter(badgeAssignments), + [](const Edge& edge) { + return edge.to_node; + } + ); + + badgeAssignments.erase( + std::remove_if( + badgeAssignments.begin(), + badgeAssignments.end(), + [&](uint64_t id) { + return !Edge::exists(dao.get_self(), id, daoId, common::DAO); + } + ), + badgeAssignments.end() + ); + + for (auto& id : badgeAssignments) { + auto doc = TypedDocument::withType(dao, id, common::ASSIGN_BADGE); + + auto cw = doc.getContentWrapper(); + + cw.insertOrReplace(*cw.getGroupOrFail(SYSTEM), Content{ + "force_archive", + 1 + }); + + cw.insertOrReplace(*cw.getGroupOrFail(DETAILS), Content{ + END_TIME, + eosio::current_time_point() + }); + + doc.update(); + + removeOldBadgesTrx.actions.emplace_back(eosio::action( + eosio::permission_level(dao.get_self(), eosio::name("active")), + dao.get_self(), + eosio::name("archiverecur"), + std::make_tuple(id) + )); + } + }; + + cleanBadgesOf(badges::common::links::HEAD_DELEGATE); + cleanBadgesOf(badges::common::links::CHIEF_DELEGATE); + + if (removeOldBadgesTrx.actions.size() > 0) { + auto dhoSettings = dao.getSettingsDocument(); + auto nextID = dhoSettings->getSettingOrDefault("next_schedule_id", int64_t(0)); + + removeOldBadgesTrx.send(nextID, dao.get_self()); + + dhoSettings->setSetting(Content{ "next_schedule_id", nextID + 1 }); + } + + } + // Generate proposals for each one of the delegates // Note: These are auto-approved and instantly on. - auto createAssignment = [&](const std::string& title, uint64_t member, uint64_t badge) { Member mem(dao, member); auto memAccount = mem.getAccount(); + // TODO: I wonder if we could call this directly auto action = eosio::action( eosio::permission_level(dao.get_self(), eosio::name("active")), dao.get_self(), @@ -417,20 +507,22 @@ namespace hypha { } }; - auto chiefBadgeEdge = Edge::get(dao.get_self(), dao.getRootID(), upvote_common::links::CHIEF_DELEGATE); - auto chiefBadge = TypedDocument::withType(dao, chiefBadgeEdge.getToNode(), common::BADGE_NAME); + auto chiefBadgeEdge = Edge::get(dao.get_self(), dao.getRootID(), badges::common::links::CHIEF_DELEGATE); + auto chiefBadgeId = chiefBadgeEdge.getToNode(); + // auto chiefBadge = TypedDocument::withType(dao, chiefBadgeId, common::BADGE_NAME); - auto headBadgeEdge = Edge::get(dao.get_self(), dao.getRootID(), upvote_common::links::HEAD_DELEGATE); - auto headBadge = TypedDocument::withType(dao, headBadgeEdge.getToNode(), common::BADGE_NAME); + auto headBadgeEdge = Edge::get(dao.get_self(), dao.getRootID(), badges::common::links::HEAD_DELEGATE); + auto headBadgeId = headBadgeEdge.getToNode(); + // auto headBadge = TypedDocument::withType(dao, headBadgeId, common::BADGE_NAME); for (auto& chief : chiefDelegates) { if (chief != headDelegate) { - createAssignment("Chief Delegate", chief, chiefBadge.getID()); + createAssignment("Chief Delegate", chief, chiefBadgeId); } } if (headDelegate) { - createAssignment("Head Delegate", headDelegate, headBadge.getID()); + createAssignment("Head Delegate", headDelegate, headBadgeId); } } @@ -499,8 +591,8 @@ namespace hypha { } }; - cleanBadgesOf(upvote_common::links::HEAD_DELEGATE); - cleanBadgesOf(upvote_common::links::CHIEF_DELEGATE); + cleanBadgesOf(badges::common::links::HEAD_DELEGATE); + cleanBadgesOf(badges::common::links::CHIEF_DELEGATE); struct [[eosio::table("elect.state"), eosio::contract("genesis.eden")]] election_state_v0 { name lead_representative; @@ -551,7 +643,7 @@ namespace hypha { }, state); //Send election id as 0 meaning that election was done outside - assignDelegateBadges(*this, dao_id, 0, chiefs, head, &trx); + assignDelegateBadges(*this, dao_id, 0, chiefs, head, false, &trx); //Trigger all cleanup and propose actions if (deferred) { @@ -811,6 +903,7 @@ namespace hypha { Edge::get(get_self(), election_id, upvote_common::links::CURRENT_ROUND).erase(); + int32_t seed = election.getRunningSeed(); if (winners.size() > 11) { // set up the next round @@ -818,8 +911,6 @@ namespace hypha { election.setCurrentRound(&round); - int32_t seed = election.getRunningSeed(); - initRound(election, round, seed, winners); scheduleElectionUpdate(*this, election, round.getEndDate()); @@ -837,12 +928,12 @@ namespace hypha { // select head UERandomGenerator rng(seed, 0); - auto headIndex = rng.operator() % winners.size(); + auto headIndex = rng() % winners.size(); auto headDelegate = winners[headIndex]; eosio::print(" head del ix: ", headIndex, " winners size: ", winners.size(), " head del: ", headDelegate, " "); - assignDelegateBadges(*this, daoId, election.getId(), winners, headDelegate); + assignDelegateBadges(*this, daoId, election.getId(), winners, headDelegate, true); election.setStatus(upvote_common::upvote_status::FINISHED); } From 3c23dc4f90c20c933666d72e75c019dc2c73e6c6 Mon Sep 17 00:00:00 2001 From: NIK Date: Tue, 10 Oct 2023 16:32:03 +0800 Subject: [PATCH 34/51] using transaction for removing of CD edges --- src/upvote_election/actions.cpp | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/upvote_election/actions.cpp b/src/upvote_election/actions.cpp index 0561d93..063309c 100644 --- a/src/upvote_election/actions.cpp +++ b/src/upvote_election/actions.cpp @@ -391,10 +391,12 @@ namespace hypha { // Delete existing if required if (deleteExistingBadges) { - eosio::transaction removeOldBadgesTrx; + // Note: This must happen before we assign new badges, since new and old badge holders + // might be the same + + // Note: This might not be needed since badges actually expire when their period is over. //Remove existing Head Delegate/Chief Delegate badges if any - // TODO: Make this a static(?) helper method and invoke from where needed. auto cleanBadgesOf = [&](const name& badgeEdge) { auto badgeId = Edge::get(dao.get_self(), dao.getRootID(), badgeEdge).getToNode(); @@ -446,27 +448,25 @@ namespace hypha { doc.update(); - removeOldBadgesTrx.actions.emplace_back(eosio::action( + auto action = eosio::action( eosio::permission_level(dao.get_self(), eosio::name("active")), dao.get_self(), eosio::name("archiverecur"), std::make_tuple(id) - )); + ); + + if (trx) { + trx->actions.emplace_back(std::move(action)); + } + else { + action.send(); + } } }; cleanBadgesOf(badges::common::links::HEAD_DELEGATE); cleanBadgesOf(badges::common::links::CHIEF_DELEGATE); - if (removeOldBadgesTrx.actions.size() > 0) { - auto dhoSettings = dao.getSettingsDocument(); - auto nextID = dhoSettings->getSettingOrDefault("next_schedule_id", int64_t(0)); - - removeOldBadgesTrx.send(nextID, dao.get_self()); - - dhoSettings->setSetting(Content{ "next_schedule_id", nextID + 1 }); - } - } // Generate proposals for each one of the delegates @@ -906,6 +906,8 @@ namespace hypha { int32_t seed = election.getRunningSeed(); if (winners.size() > 11) { + eosio::print(" -> next round with mem ", winners.size(), " "); + // set up the next round auto round = election.addRound(); From e5531f901a8a6fd4416107de2e1c8e985902d3dd Mon Sep 17 00:00:00 2001 From: NIK Date: Tue, 10 Oct 2023 17:27:10 +0800 Subject: [PATCH 35/51] bugfix - set seed on upcoming election --- src/upvote_election/actions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/upvote_election/actions.cpp b/src/upvote_election/actions.cpp index 063309c..b9d76bf 100644 --- a/src/upvote_election/actions.cpp +++ b/src/upvote_election/actions.cpp @@ -990,7 +990,7 @@ namespace hypha { void dao::uesubmitseed(uint64_t dao_id, eosio::checksum256 seed, name account) { eosio::require_auth(account); - auto edge = Edge::get(get_self(), dao_id, upvote_common::links::ELECTION); + auto edge = Edge::get(get_self(), dao_id, upvote_common::links::UPCOMING_ELECTION); auto electionId = edge.getToNode(); From 076fdfbe752ce926427f7ee31c65affa55361dc9 Mon Sep 17 00:00:00 2001 From: NIK Date: Wed, 11 Oct 2023 02:36:32 +0800 Subject: [PATCH 36/51] bugfixes --- src/upvote_election/actions.cpp | 14 +++++++++----- src/upvote_election/upvote_election.cpp | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/upvote_election/actions.cpp b/src/upvote_election/actions.cpp index b9d76bf..6234af9 100644 --- a/src/upvote_election/actions.cpp +++ b/src/upvote_election/actions.cpp @@ -861,6 +861,7 @@ namespace hypha { auto startRound = election.getStartRound(); election.setCurrentRound(&startRound); + election.update(); //Setup all candidates auto delegates = getGraph().getEdgesFrom(daoId, badges::common::links::DELEGATE); @@ -901,27 +902,30 @@ namespace hypha { Edge(get_self(), get_self(), currentRound.getId(), winner, upvote_common::links::ROUND_WINNER); } - Edge::get(get_self(), election_id, upvote_common::links::CURRENT_ROUND).erase(); int32_t seed = election.getRunningSeed(); if (winners.size() > 11) { - eosio::print(" -> next round with mem ", winners.size(), " "); // set up the next round auto round = election.addRound(); - + eosio::print(" -> next round with mem ", winners.size(), " rd: ", round.getId(), " "); + + Edge::get(get_self(), election_id, upvote_common::links::CURRENT_ROUND).erase(); election.setCurrentRound(&round); + election.update(); initRound(election, round, seed, winners); + scheduleElectionUpdate(*this, election, round.getEndDate()); } else { eosio::print(" election ended. "); // The election has ended. - + Edge::get(get_self(), election_id, upvote_common::links::CURRENT_ROUND).erase(); + // delete ongoing election link Edge::get(get_self(), daoId, election.getId(), upvote_common::links::ONGOING_ELECTION).erase(); @@ -1016,7 +1020,7 @@ namespace hypha { EOS_CHECK( currentRound.getId() == round_id, - "You can only vote on the current round" + "You can only vote on the current round" + std::to_string(currentRound.getId()) ); // Check the group membership of voter and voted diff --git a/src/upvote_election/upvote_election.cpp b/src/upvote_election/upvote_election.cpp index 9bff47b..474f265 100644 --- a/src/upvote_election/upvote_election.cpp +++ b/src/upvote_election/upvote_election.cpp @@ -238,7 +238,7 @@ namespace hypha::upvote_election { { return ElectionRound( getDao(), - Edge::get(getDao().get_self(), getId(), links::ELECTION_ROUND).getToNode() + Edge::get(getDao().get_self(), getId(), links::CURRENT_ROUND).getToNode() ); } From f3518f526ccc9c6228d8b20645f71d0f67e5873c Mon Sep 17 00:00:00 2001 From: Gerard097 Date: Wed, 11 Oct 2023 02:07:28 -0600 Subject: [PATCH 37/51] Removed Reward to Peg ration & fixed token multipliers --- include/assignment.hpp | 5 +- include/common.hpp | 1 - include/util.hpp | 1 - src/assignment.cpp | 60 +++++++------------ src/dao.cpp | 83 ++++++--------------------- src/proposals/assignment_proposal.cpp | 5 -- src/proposals/payout_proposal.cpp | 5 -- src/util.cpp | 2 +- 8 files changed, 42 insertions(+), 120 deletions(-) diff --git a/include/assignment.hpp b/include/assignment.hpp index 15eac53..8924924 100644 --- a/include/assignment.hpp +++ b/include/assignment.hpp @@ -13,6 +13,7 @@ namespace hypha class dao; class TimeShare; class Settings; + struct AssetBatch; class Assignment : public RecurringActivity { @@ -26,9 +27,7 @@ namespace hypha TimeShare getCurrentTimeShare(); TimeShare getLastTimeShare(); - eosio::asset getRewardSalary(); - eosio::asset getVoiceSalary(); - eosio::asset getPegSalary(); + AssetBatch getSalary(); inline uint64_t getDaoID() { return m_daoID; } private: diff --git a/include/common.hpp b/include/common.hpp index fb95dd2..6dae001 100644 --- a/include/common.hpp +++ b/include/common.hpp @@ -24,7 +24,6 @@ namespace hypha::common inline constexpr auto PEG_TOKEN = "peg_token"; inline constexpr auto VOICE_TOKEN = "voice_token"; inline constexpr auto REWARD_TOKEN = "reward_token"; - inline constexpr auto REWARD_TO_PEG_RATIO = "reward_to_peg_ratio"; inline constexpr auto NATIVE_TOKEN_TO_USD_RATIO = "native_token_to_usd_ratio"; inline constexpr symbol S_REWARD("BM", 2); inline constexpr symbol S_VOICE("BMV", 2); diff --git a/include/util.hpp b/include/util.hpp index 02eec37..31975b3 100644 --- a/include/util.hpp +++ b/include/util.hpp @@ -38,7 +38,6 @@ namespace hypha { // double annualSalary; double periodSalary; - double rewardToPegRatio; double deferredPerc; double voiceMultipler = 2.0; double rewardMultipler = 1.0; diff --git a/src/assignment.cpp b/src/assignment.cpp index 940ec5c..70179c7 100644 --- a/src/assignment.cpp +++ b/src/assignment.cpp @@ -57,55 +57,35 @@ namespace hypha return std::nullopt; } - eosio::asset Assignment::getRewardSalary() + AssetBatch Assignment::getSalary() { - auto rewardToken = m_daoSettings->getSettingOrDefault(common::REWARD_TOKEN); + //Since multipliers can change from time to time, we need to recalculate salary using the latest values + auto tokens = AssetBatch { + .reward = m_daoSettings->getSettingOrDefault(common::REWARD_TOKEN), + .peg = m_daoSettings->getSettingOrDefault(common::PEG_TOKEN), + .voice = m_daoSettings->getOrFail(common::VOICE_TOKEN) + }; - if (!rewardToken.is_valid()) return {}; - - //It's posible that the assignment was created before the reward token was setup, so let's check for that case auto cw = getContentWrapper(); - if (!cw.exists(DETAILS, common::REWARD_SALARY_PER_PERIOD)) return {}; - - asset usdPerPeriod = cw - .getOrFail(DETAILS, USD_SALARY_PER_PERIOD)->getAs(); + asset usdPerPeriod = cw.getOrFail(DETAILS, USD_SALARY_PER_PERIOD)->getAs(); - int64_t initialTimeshare = getInitialTimeShare() - .getContentWrapper() - .getOrFail(DETAILS, TIME_SHARE)->getAs(); + int64_t initialTimeshare = getInitialTimeShare().getContentWrapper() + .getOrFail(DETAILS, TIME_SHARE)->getAs(); - double usdPerPeriodCommitmentAdjusted = normalizeToken(usdPerPeriod) * (initialTimeshare / 100.0); + int64_t deferred = cw.getOrFail(DETAILS, DEFERRED)->getAs(); - double deferred = static_cast(cw.getOrFail(DETAILS, DEFERRED)->getAs()) / 100.0; - - auto rewardToPegVal = normalizeToken(m_daoSettings->getOrFail(common::REWARD_TO_PEG_RATIO)); - - auto rewardPerPeriod = usdPerPeriodCommitmentAdjusted * deferred / rewardToPegVal; - - return denormalizeToken(rewardPerPeriod, rewardToken); - } - - eosio::asset Assignment::getVoiceSalary() - { - return getContentWrapper() - .getOrFail(DETAILS, common::VOICE_SALARY_PER_PERIOD)->getAs(); - } - - eosio::asset Assignment::getPegSalary() - { - //Since this could be optional let's return invalid asset if not found - auto pegToken = m_daoSettings->getSettingOrDefault(common::PEG_TOKEN); - - if (!pegToken.is_valid()) return {}; - - //It's posible that the assignment was created before the reward token was setup, so let's check for that case - auto cw = getContentWrapper(); + SalaryConfig salaryConf { + .periodSalary = normalizeToken(usdPerPeriod) * (initialTimeshare / 100.0), + .deferredPerc = deferred / 100.0, + .voiceMultipler = getMultiplier(m_daoSettings, common::VOICE_MULTIPLIER, 1.0), + .rewardMultipler = getMultiplier(m_daoSettings, common::REWARD_MULTIPLIER, 1.0), + .pegMultipler = getMultiplier(m_daoSettings, common::PEG_MULTIPLIER, 1.0) + }; - if (!cw.exists(DETAILS, common::PEG_SALARY_PER_PERIOD)) return {}; + AssetBatch salary = calculateSalaries(salaryConf, tokens); - return getContentWrapper() - .getOrFail(DETAILS, common::PEG_SALARY_PER_PERIOD)->getAs(); + return salary; } TimeShare Assignment::getInitialTimeShare() diff --git a/src/dao.cpp b/src/dao.cpp index 1ebe785..a51445f 100644 --- a/src/dao.cpp +++ b/src/dao.cpp @@ -787,9 +787,11 @@ void dao::claimnextper(uint64_t assignment_id) .voice = daoSettings->getOrFail(common::VOICE_TOKEN) }; - const asset pegSalary = assignment.getPegSalary(); - const asset voiceSalary = assignment.getVoiceSalary(); - const asset rewardSalary = assignment.getRewardSalary(); + auto salary = assignment.getSalary(); + + const asset& pegSalary = salary.peg; + const asset& voiceSalary = salary.voice; + const asset& rewardSalary = salary.reward; const int64_t initTimeShare = assignment.getInitialTimeShare() .getContentWrapper() @@ -2620,44 +2622,29 @@ void dao::adjustdeferr(name issuer, uint64_t assignment_id, int64_t new_deferred approvedDeferredPerc, " - ", UPPER_LIMIT, "]:", new_deferred_perc_x100) ); - asset usdPerPeriod = cw.getOrFail(detailsIdx, USD_SALARY_PER_PERIOD) - .second->getAs(); - - int64_t initialTimeshare = assignment.getInitialTimeShare() - .getContentWrapper() - .getOrFail(DETAILS, TIME_SHARE)->getAs(); - - auto daoSettings = getSettingsDocument(assignment.getDaoID()); - - auto usdPerPeriodCommitmentAdjusted = normalizeToken(usdPerPeriod) * (initialTimeshare / 100.0); - - auto deferred = new_deferred_perc_x100 / 100.0; - - auto pegVal = usdPerPeriodCommitmentAdjusted * (1.0 - deferred); - //husdVal.symbol = common::S_PEG; - + //Update deferred value cw.insertOrReplace(*detailsGroup, Content{ DEFERRED, new_deferred_perc_x100 - }); - - auto rewardToPegVal = normalizeToken(daoSettings->getOrFail(common::REWARD_TO_PEG_RATIO)); - - auto rewardToken = daoSettings->getOrFail(common::REWARD_TOKEN); - - auto pegToken = daoSettings->getOrFail(common::PEG_TOKEN); + }); - auto rewardVal = usdPerPeriodCommitmentAdjusted * deferred / rewardToPegVal; + //Get new salaries with the updated deferred + auto salaries = assignment.getSalary(); cw.insertOrReplace(*detailsGroup, Content{ - common::REWARD_SALARY_PER_PERIOD, - denormalizeToken(rewardVal, rewardToken) - }); + common::REWARD_SALARY_PER_PERIOD, + salaries.reward + }); cw.insertOrReplace(*detailsGroup, Content{ common::PEG_SALARY_PER_PERIOD, - denormalizeToken(pegVal, pegToken) - }); + salaries.peg + }); + + cw.insertOrReplace(*detailsGroup, Content{ + common::VOICE_SALARY_PER_PERIOD, + salaries.voice + }); assignment.update(); } @@ -3142,39 +3129,8 @@ void dao::addDefaultSettings(ContentGroup& settingsGroup, const string& daoTitle void dao::pushRewardTokenSettings(name dao, uint64_t daoID, ContentGroup& settingsGroup, ContentWrapper configCW, int64_t detailsIdx, bool create) { - if (settingsGroup.size() < 4) { - - auto rewardMultiplier = configCW.get(detailsIdx, common::REWARD_MULTIPLIER).second; - auto rewardToPegTokenRatio = configCW.getOrFail(detailsIdx, common::REWARD_TO_PEG_RATIO).second; - - // If we need to add 1 other settings just change 4 to 5,6,7 etc. and add the null check here - size_t defined = size_t(rewardMultiplier != nullptr) + size_t(rewardToPegTokenRatio != nullptr); - - EOS_CHECK( - (settingsGroup.size() > 1) && - (defined == settingsGroup.size() - 1), - "Missing some expected parameters [utility_token_multiplier, reward_to_peg_ratio]" - ) - - if (rewardMultiplier) { - rewardMultiplier->getAs(); - ContentWrapper::insertOrReplace(settingsGroup, *rewardMultiplier); - } - - if (rewardToPegTokenRatio) { - rewardToPegTokenRatio->getAs(); - ContentWrapper::insertOrReplace(settingsGroup, std::move(*rewardToPegTokenRatio)); - } - - return; - } - auto rewardToken = configCW.getOrFail(detailsIdx, common::REWARD_TOKEN).second; - auto rewardToPegTokenRatio = configCW.getOrFail(detailsIdx, common::REWARD_TO_PEG_RATIO).second; - - rewardToPegTokenRatio->getAs(); - auto rewardTokenMaxSupply = configCW.getOrFail(detailsIdx, "reward_token_max_supply").second; EOS_CHECK( @@ -3206,7 +3162,6 @@ void dao::pushRewardTokenSettings(name dao, uint64_t daoID, ContentGroup& settin settingsGroup.push_back(*rewardToken); settingsGroup.push_back(std::move(rewardTokenName)); settingsGroup.push_back(*rewardTokenMaxSupply); - ContentWrapper::insertOrReplace(settingsGroup, std::move(*rewardToPegTokenRatio)); ContentWrapper::insertOrReplace(settingsGroup, *rewardMultiplier); if (create) { diff --git a/src/proposals/assignment_proposal.cpp b/src/proposals/assignment_proposal.cpp index 4be2a76..366273c 100644 --- a/src/proposals/assignment_proposal.cpp +++ b/src/proposals/assignment_proposal.cpp @@ -171,13 +171,8 @@ namespace hypha .voice = m_daoSettings->getOrFail(common::VOICE_TOKEN) }; - auto rewardPegVal = tokens.reward.is_valid() ? - m_daoSettings->getOrFail(common::REWARD_TO_PEG_RATIO) : - eosio::asset{}; - SalaryConfig salaryConf { .periodSalary = normalizeToken(usdSalaryPerPeriod.getAs()) * (timeShare / 100.0), - .rewardToPegRatio = normalizeToken(rewardPegVal), .deferredPerc = deferred / 100.0, .voiceMultipler = getMultiplier(m_daoSettings, common::VOICE_MULTIPLIER, 1.0), .rewardMultipler = getMultiplier(m_daoSettings, common::REWARD_MULTIPLIER, 1.0), diff --git a/src/proposals/payout_proposal.cpp b/src/proposals/payout_proposal.cpp index cbc1198..6c0633d 100644 --- a/src/proposals/payout_proposal.cpp +++ b/src/proposals/payout_proposal.cpp @@ -37,14 +37,9 @@ void PayoutProposal::checkTokenItems(Settings* daoSettings, ContentWrapper conte int64_t deferred = contentWrapper.getOrFail(DETAILS, DEFERRED)->getAs(); EOS_CHECK(deferred >= 0, DEFERRED + string(" must be greater than or equal to zero. You submitted: ") + std::to_string(deferred)); EOS_CHECK(deferred <= 100, DEFERRED + string(" must be less than or equal to 100 (=100%). You submitted: ") + std::to_string(deferred)); - - auto rewardPegVal = tokens.reward.is_valid() ? - daoSettings->getOrFail(common::REWARD_TO_PEG_RATIO) : - eosio::asset{}; auto salaries = calculateSalaries(SalaryConfig { .periodSalary = normalizeToken(usd), - .rewardToPegRatio = normalizeToken(rewardPegVal), .deferredPerc = deferred / 100.0, .voiceMultipler = getMultiplier(daoSettings, common::VOICE_MULTIPLIER, 1.0), .rewardMultipler = getMultiplier(daoSettings, common::REWARD_MULTIPLIER, 1.0), diff --git a/src/util.cpp b/src/util.cpp index 371a1f6..842adf0 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -40,7 +40,7 @@ namespace hypha } if (tokens.reward.is_valid()) { - double rewardSalaryPerPeriod = (salaryConf.periodSalary * salaryConf.deferredPerc) / salaryConf.rewardToPegRatio; + double rewardSalaryPerPeriod = (salaryConf.periodSalary * salaryConf.deferredPerc); salaries.reward = denormalizeToken(rewardSalaryPerPeriod * salaryConf.rewardMultipler, tokens.reward); } From 0745b17c2d1fbf9d57a5925b62b84db4ddceabc7 Mon Sep 17 00:00:00 2001 From: NIK Date: Wed, 11 Oct 2023 19:41:36 +0800 Subject: [PATCH 38/51] add group winner edge --- include/upvote_election/common.hpp | 1 + src/upvote_election/election_group.cpp | 11 +++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/include/upvote_election/common.hpp b/include/upvote_election/common.hpp index a848470..43e58ec 100644 --- a/include/upvote_election/common.hpp +++ b/include/upvote_election/common.hpp @@ -46,6 +46,7 @@ namespace links { inline constexpr auto NEXT_ROUND = eosio::name("ue.nextrnd"); inline constexpr auto ROUND_CANDIDATE = eosio::name("ue.candidate"); inline constexpr auto ROUND_WINNER = eosio::name("ue.winner"); + inline constexpr auto GROUP_WINNER = eosio::name("ue.group.win"); inline constexpr auto ELECTION_GROUP = eosio::name("ue.elctngrp"); inline constexpr auto UP_VOTE_VOTE = eosio::name("ue.vote"); inline constexpr auto UPVOTE_GROUP_WINNER = eosio::name("ue.winner"); diff --git a/src/upvote_election/election_group.cpp b/src/upvote_election/election_group.cpp index d51a6e4..5d8f036 100644 --- a/src/upvote_election/election_group.cpp +++ b/src/upvote_election/election_group.cpp @@ -124,11 +124,18 @@ void ElectionGroup::vote(int64_t from, int64_t to) majorityWinner = to; } } - + std::vector winnerEdges = getDao().getGraph().getEdgesFrom(getId(), links::GROUP_WINNER); + if (winnerEdges.size() > 0) { + winnerEdges[0].erase(); + } + if (majorityWinner > 0 && selfVotes[majorityWinner]) { setWinner(majorityWinner); - update(); + Edge(getDao().get_self(), getDao().get_self(), getId(), majorityWinner, links::GROUP_WINNER); + } else { + setWinner(-1); } + update(); } From 9700fa0124a5ce0b7bcdec1dd66d1fade36dc87e Mon Sep 17 00:00:00 2001 From: NIK Date: Wed, 11 Oct 2023 22:16:05 +0800 Subject: [PATCH 39/51] cleanup --- build_only.sh | 3 + include/dao.hpp | 10 ++- src/dao.cpp | 45 +++++------ src/upvote_election/actions.cpp | 131 ++++++++++++++------------------ 4 files changed, 88 insertions(+), 101 deletions(-) create mode 100755 build_only.sh diff --git a/build_only.sh b/build_only.sh new file mode 100755 index 0000000..e37053b --- /dev/null +++ b/build_only.sh @@ -0,0 +1,3 @@ +cd build +make -j10 +wasm-opt -O3 dao/dao.wasm -o dao/dao_O3.wasm diff --git a/include/dao.hpp b/include/dao.hpp index b0e0521..f482f43 100644 --- a/include/dao.hpp +++ b/include/dao.hpp @@ -199,7 +199,7 @@ namespace pricing { ACTION initcalendar(uint64_t calendar_id, uint64_t next_period); - ACTION reset(); // debugging - maybe with the dev flags + //ACTION reset(); // debugging - maybe with the dev flags #ifdef DEVELOP_BUILD_HELPERS @@ -302,6 +302,7 @@ namespace pricing { ACTION createroot(const std::string ¬es); ACTION createdao(ContentGroups &config); + ACTION createdaodft(ContentGroups &config); ACTION deletedaodft(uint64_t dao_draft_id); ACTION archiverecur(uint64_t document_id); @@ -331,9 +332,10 @@ namespace pricing { #endif #ifdef USE_UPVOTE_ELECTIONS //Upvote System - ACTION testgrouprng(std::vector ids, uint32_t seed); - ACTION testgroupr1(uint32_t num_members, uint32_t seed); - ACTION testround(uint64_t dao_id); + // ACTION testgrouprng(std::vector ids, uint32_t seed); + // ACTION testgroupr1(uint32_t num_members, uint32_t seed); + // ACTION testround(uint64_t dao_id); + ACTION inituebadges(); ACTION castupvote(uint64_t round_id, uint64_t group_id, name voter, uint64_t voted_id); ACTION uesubmitseed(uint64_t dao_id, eosio::checksum256 seed, name account); diff --git a/src/dao.cpp b/src/dao.cpp index f4ea1c0..60a07b5 100644 --- a/src/dao.cpp +++ b/src/dao.cpp @@ -1373,6 +1373,12 @@ void dao::initSysBadges() { } +void dao::inituebadges() { + createSystemBadge(badges::common::links::DELEGATE, "Upvote Delegate Badge", ""); + createSystemBadge(badges::common::links::CHIEF_DELEGATE, "Chief Delegate Badge", ""); + createSystemBadge(badges::common::links::HEAD_DELEGATE, "Head Delegate Badge", ""); +} + void dao::createSystemBadge(name badge_edge, string label, string icon) { badges::SystemBadgeType systemBadgeType; @@ -1403,6 +1409,7 @@ void dao::createSystemBadge(name badge_edge, string label, string icon) { Content(CONTENT_GROUP_LABEL, DETAILS), Content(common::STATE, common::STATE_APPROVED), Content(common::VOICE_COEFFICIENT, 10000), + Content(common::TITLE, label), Content(common::REWARD_COEFFICIENT, 10000), Content(common::PEG_COEFFICIENT, 10000), Content("dao", (int64_t)getRootID()), @@ -3555,34 +3562,24 @@ void dao::readDaoSettings(uint64_t daoID, const name& dao, ContentWrapper config Edge::write(get_self(), get_self(), daoID, settingsDoc.getID(), common::SETTINGS_EDGE); } -void dao::reset() { - require_auth(_self); - - delete_table(get_self(), 0); - delete_table(get_self(), 1); - delete_table(get_self(), 2); - delete_table(get_self(), 3); - delete_table(get_self(), get_self().value); - delete_table(get_self(), get_self().value); - delete_table(get_self(), get_self().value); - delete_table(get_self(), get_self().value); +// void dao::reset() { - delete_table(get_self(), get_self().value); - delete_table(get_self(), get_self().value); +// check(false, "reset is only for testing"); - // Document::document_table d_t(get_self(), get_self().value); - // auto d_itr = d_t.begin(); - // while (d_itr != d_t.end()) { - // d_itr = d_t.erase(d_itr); - // } +// require_auth(_self); - // Edge::edge_table e_t(get_self(), get_self().value); - // auto e_itr = e_t.begin(); - // while (e_itr != e_t.end()) { - // e_itr = e_t.erase(e_itr); - // } +// delete_table(get_self(), 0); +// delete_table(get_self(), 1); +// delete_table(get_self(), 2); +// delete_table(get_self(), 3); +// delete_table(get_self(), get_self().value); +// delete_table(get_self(), get_self().value); +// delete_table(get_self(), get_self().value); +// delete_table(get_self(), get_self().value); +// delete_table(get_self(), get_self().value); +// delete_table(get_self(), get_self().value); -} +// } } // namespace hypha diff --git a/src/upvote_election/actions.cpp b/src/upvote_election/actions.cpp index 6234af9..80da9c6 100644 --- a/src/upvote_election/actions.cpp +++ b/src/upvote_election/actions.cpp @@ -677,112 +677,97 @@ namespace hypha { /// @brief Test random group creation /// @param ids - void dao::testgroupr1(uint32_t num_members, uint32_t seed) { + // void dao::testgroupr1(uint32_t num_members, uint32_t seed) { - require_auth(get_self()); + // require_auth(get_self()); - std::vector ids(num_members); // Initialize a vector with 100 elements + // std::vector ids(num_members); // Initialize a vector with 100 elements - // Set each element's value to its index - for (size_t i = 0; i < num_members; ++i) { - ids[i] = static_cast(i); - } - testgrouprng(ids, seed); - - // for (uint32_t n = 0; n < num_members; ++n) { - // auto rounds = count_rounds(n); - - // std::vector group_sizes = get_group_sizes(n, rounds); - - // eosio::print(" N: ", n, " rounds: ", rounds, " g_sizes: "); - - // for (uint32_t s : group_sizes) { - // eosio::print(s, " "); - // } - - // eosio::print(" ::: "); + // // Set each element's value to its index + // for (size_t i = 0; i < num_members; ++i) { + // ids[i] = static_cast(i); + // } + // testgrouprng(ids, seed); - // } + // } - } - - void dao::testround(uint64_t dao_id) { - require_auth(get_self()); + // void dao::testround(uint64_t dao_id) { + // require_auth(get_self()); - auto edge = Edge::get(get_self(), dao_id, upvote_common::links::ELECTION); + // auto edge = Edge::get(get_self(), dao_id, upvote_common::links::ELECTION); - auto electionId = edge.getToNode(); + // auto electionId = edge.getToNode(); - eosio::print(" election id: ", electionId, " ::: "); + // eosio::print(" election id: ", electionId, " ::: "); - UpvoteElection upvoteElection(*this, electionId); + // UpvoteElection upvoteElection(*this, electionId); - upvoteElection.addRound(); + // upvoteElection.addRound(); - // for (uint32_t n = 0; n < num_members; ++n) { - // auto rounds = count_rounds(n); + // for (uint32_t n = 0; n < num_members; ++n) { + // auto rounds = count_rounds(n); - // std::vector group_sizes = get_group_sizes(n, rounds); + // std::vector group_sizes = get_group_sizes(n, rounds); - // eosio::print(" N: ", n, " rounds: ", rounds, " g_sizes: "); + // eosio::print(" N: ", n, " rounds: ", rounds, " g_sizes: "); - // for (uint32_t s : group_sizes) { - // eosio::print(s, " "); - // } + // for (uint32_t s : group_sizes) { + // eosio::print(s, " "); + // } - // eosio::print(" ::: "); + // eosio::print(" ::: "); - // } + // } - } + // } - void dao::testgrouprng(std::vector ids, uint32_t seed) { + // void dao::testgrouprng(std::vector ids, uint32_t seed) { - require_auth(get_self()); + // require_auth(get_self()); - eosio::print(" ids: "); - for (const uint64_t& element : ids) { - eosio::print(element, " "); - } + // eosio::print(" ids: "); + // for (const uint64_t& element : ids) { + // eosio::print(element, " "); + // } - UERandomGenerator rng(seed, 0); + // UERandomGenerator rng(seed, 0); - auto randomIds = ids; + // auto randomIds = ids; - std::shuffle(randomIds.begin(), randomIds.end(), rng); + // std::shuffle(randomIds.begin(), randomIds.end(), rng); - eosio::print(" random ids: "); - for (const uint64_t& element : randomIds) { - eosio::print(element, " "); - } + // eosio::print(" random ids: "); + // for (const uint64_t& element : randomIds) { + // eosio::print(element, " "); + // } - //// Eden defines min group size as 4, but depending on - //// the numbers, Edenia code does something slightly different - // int minGroupsSize = 4; + // //// Eden defines min group size as 4, but depending on + // //// the numbers, Edenia code does something slightly different + // // int minGroupsSize = 4; - //// use Edenia code to figure out group size - auto n = ids.size(); - auto rounds = count_rounds(n); - std::vector group_sizes = get_group_sizes(n, rounds); - int minGroupsSize = group_sizes[0]; - auto groups = createGroups(randomIds, minGroupsSize); - - eosio::print(" groups min size: ", minGroupsSize); - for (uint32_t i = 0; i < groups.size(); ++i) { - auto group = groups[i]; - eosio::print(" group: ", i, "(", group.size(), "): "); - for (const uint64_t& element : group) { - eosio::print(element, " "); - } - } + // //// use Edenia code to figure out group size + // auto n = ids.size(); + // auto rounds = count_rounds(n); + // std::vector group_sizes = get_group_sizes(n, rounds); + // int minGroupsSize = group_sizes[0]; + // auto groups = createGroups(randomIds, minGroupsSize); - } + // eosio::print(" groups min size: ", minGroupsSize); + // for (uint32_t i = 0; i < groups.size(); ++i) { + // auto group = groups[i]; + // eosio::print(" group: ", i, "(", group.size(), "): "); + // for (const uint64_t& element : group) { + // eosio::print(element, " "); + // } + // } + + // } // 36 / 6 = 6 => 1 round, 1 HD round size_t numrounds(size_t num_delegates) { From 332bb43e37be2216ce0cdd20309297c0ecba1978 Mon Sep 17 00:00:00 2001 From: NIK Date: Wed, 11 Oct 2023 22:44:12 +0800 Subject: [PATCH 40/51] remove title label again --- src/dao.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/dao.cpp b/src/dao.cpp index dacfdbb..976c8f1 100644 --- a/src/dao.cpp +++ b/src/dao.cpp @@ -1411,7 +1411,6 @@ void dao::createSystemBadge(name badge_edge, string label, string icon) { Content(CONTENT_GROUP_LABEL, DETAILS), Content(common::STATE, common::STATE_APPROVED), Content(common::VOICE_COEFFICIENT, 10000), - Content(common::TITLE, label), Content(common::REWARD_COEFFICIENT, 10000), Content(common::PEG_COEFFICIENT, 10000), Content("dao", (int64_t)getRootID()), From fbbc69869318db36c26af19bb7a01a25203adfaa Mon Sep 17 00:00:00 2001 From: NIK Date: Thu, 12 Oct 2023 20:46:41 +0800 Subject: [PATCH 41/51] fix node label on upvote --- include/upvote_election/up_vote_vote.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/upvote_election/up_vote_vote.hpp b/include/upvote_election/up_vote_vote.hpp index d1978b6..354c949 100644 --- a/include/upvote_election/up_vote_vote.hpp +++ b/include/upvote_election/up_vote_vote.hpp @@ -30,7 +30,7 @@ class UpVoteVote : public TypedDocument private: virtual const std::string buildNodeLabel(ContentGroups &content) override { - return "Vote Group"; + return "UpVote"; } }; From 1f258a810d7f39c8949f8ce1d5b0a7254ccee5b5 Mon Sep 17 00:00:00 2001 From: NIK Date: Fri, 13 Oct 2023 01:22:30 +0800 Subject: [PATCH 42/51] remove debug code --- include/dao.hpp | 1 - src/dao.cpp | 8 +------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/include/dao.hpp b/include/dao.hpp index f482f43..577b9ff 100644 --- a/include/dao.hpp +++ b/include/dao.hpp @@ -335,7 +335,6 @@ namespace pricing { // ACTION testgrouprng(std::vector ids, uint32_t seed); // ACTION testgroupr1(uint32_t num_members, uint32_t seed); // ACTION testround(uint64_t dao_id); - ACTION inituebadges(); ACTION castupvote(uint64_t round_id, uint64_t group_id, name voter, uint64_t voted_id); ACTION uesubmitseed(uint64_t dao_id, eosio::checksum256 seed, name account); diff --git a/src/dao.cpp b/src/dao.cpp index 976c8f1..3c87717 100644 --- a/src/dao.cpp +++ b/src/dao.cpp @@ -1375,12 +1375,6 @@ void dao::initSysBadges() { } -void dao::inituebadges() { - createSystemBadge(badges::common::links::DELEGATE, "Upvote Delegate Badge", ""); - createSystemBadge(badges::common::links::CHIEF_DELEGATE, "Chief Delegate Badge", ""); - createSystemBadge(badges::common::links::HEAD_DELEGATE, "Head Delegate Badge", ""); -} - void dao::createSystemBadge(name badge_edge, string label, string icon) { badges::SystemBadgeType systemBadgeType; @@ -3518,7 +3512,7 @@ void dao::readDaoSettings(uint64_t daoID, const name& dao, ContentWrapper config // void dao::reset() { -// check(false, "reset is only for testing"); +// // eosio::check(false, "reset is only for testing"); // require_auth(_self); From 18a8307056a9a5f9c93e0401ab5227010945533f Mon Sep 17 00:00:00 2001 From: NIK Date: Fri, 13 Oct 2023 02:37:39 +0800 Subject: [PATCH 43/51] init last round in election --- include/dao.hpp | 2 +- include/upvote_election/election_round.hpp | 2 +- src/dao.cpp | 28 +++++++++++----------- src/upvote_election/actions.cpp | 26 +++++++++++++------- src/upvote_election/election_round.cpp | 7 ++++-- 5 files changed, 38 insertions(+), 27 deletions(-) diff --git a/include/dao.hpp b/include/dao.hpp index 577b9ff..177763b 100644 --- a/include/dao.hpp +++ b/include/dao.hpp @@ -199,7 +199,7 @@ namespace pricing { ACTION initcalendar(uint64_t calendar_id, uint64_t next_period); - //ACTION reset(); // debugging - maybe with the dev flags + ACTION reset(); // debugging - maybe with the dev flags #ifdef DEVELOP_BUILD_HELPERS diff --git a/include/upvote_election/election_round.hpp b/include/upvote_election/election_round.hpp index 8399761..220e7ed 100644 --- a/include/upvote_election/election_round.hpp +++ b/include/upvote_election/election_round.hpp @@ -31,7 +31,7 @@ class ElectionRound : public TypedDocument void setNextRound(ElectionRound* nextRound) const; std::unique_ptr getNextRound() const; - void addElectionGroup(std::vector accound_ids); + void addElectionGroup(std::vector accound_ids, int64_t winner = -1); private: virtual const std::string buildNodeLabel(ContentGroups &content) override diff --git a/src/dao.cpp b/src/dao.cpp index 3c87717..b46ee7b 100644 --- a/src/dao.cpp +++ b/src/dao.cpp @@ -3510,24 +3510,24 @@ void dao::readDaoSettings(uint64_t daoID, const name& dao, ContentWrapper config Edge::write(get_self(), get_self(), daoID, settingsDoc.getID(), common::SETTINGS_EDGE); } -// void dao::reset() { +void dao::reset() { -// // eosio::check(false, "reset is only for testing"); + eosio::check(false, "reset is only for testing"); -// require_auth(_self); + require_auth(_self); -// delete_table(get_self(), 0); -// delete_table(get_self(), 1); -// delete_table(get_self(), 2); -// delete_table(get_self(), 3); -// delete_table(get_self(), get_self().value); -// delete_table(get_self(), get_self().value); -// delete_table(get_self(), get_self().value); -// delete_table(get_self(), get_self().value); -// delete_table(get_self(), get_self().value); -// delete_table(get_self(), get_self().value); + delete_table(get_self(), 0); + delete_table(get_self(), 1); + delete_table(get_self(), 2); + delete_table(get_self(), 3); + delete_table(get_self(), get_self().value); + delete_table(get_self(), get_self().value); + delete_table(get_self(), get_self().value); + delete_table(get_self(), get_self().value); + delete_table(get_self(), get_self().value); + delete_table(get_self(), get_self().value); -// } +} } // namespace hypha diff --git a/src/upvote_election/actions.cpp b/src/upvote_election/actions.cpp index 80da9c6..1e02be7 100644 --- a/src/upvote_election/actions.cpp +++ b/src/upvote_election/actions.cpp @@ -347,6 +347,12 @@ namespace hypha { } + static void initLastRound(UpvoteElection& election, ElectionRound& round, std::vector delegateIds, int64_t winner) { + auto groups = createGroupsEden(delegateIds); + eosio::check(groups.size() == 1, "last group is 1"); + round.addElectionGroup(groups[0], winner); + } + static void scheduleElectionUpdate(dao& dao, UpvoteElection& election, time_point date) { if (date < eosio::current_time_point()) return; @@ -386,9 +392,6 @@ namespace hypha { bool deleteExistingBadges, eosio::transaction* trx = nullptr) { - // TODO This removes and adds badges. They're all pretty expensive operations but at most there's 11 badges - // So it should work in all cases. If not we could pack it into deferred transactions. - // Delete existing if required if (deleteExistingBadges) { // Note: This must happen before we assign new badges, since new and old badge holders @@ -477,7 +480,6 @@ namespace hypha { auto memAccount = mem.getAccount(); - // TODO: I wonder if we could call this directly auto action = eosio::action( eosio::permission_level(dao.get_self(), eosio::name("active")), dao.get_self(), @@ -505,15 +507,15 @@ namespace hypha { else { action.send(); } - }; + }; auto chiefBadgeEdge = Edge::get(dao.get_self(), dao.getRootID(), badges::common::links::CHIEF_DELEGATE); auto chiefBadgeId = chiefBadgeEdge.getToNode(); - // auto chiefBadge = TypedDocument::withType(dao, chiefBadgeId, common::BADGE_NAME); + //auto chiefBadge = TypedDocument::withType(dao, chiefBadgeId, common::BADGE_NAME); auto headBadgeEdge = Edge::get(dao.get_self(), dao.getRootID(), badges::common::links::HEAD_DELEGATE); auto headBadgeId = headBadgeEdge.getToNode(); - // auto headBadge = TypedDocument::withType(dao, headBadgeId, common::BADGE_NAME); + //auto headBadge = TypedDocument::withType(dao, headBadgeId, common::BADGE_NAME); for (auto& chief : chiefDelegates) { if (chief != headDelegate) { @@ -813,6 +815,7 @@ namespace hypha { // force flag: force update of status - for debugging, not used in normal use - true requires self authorization void dao::updateupvelc(uint64_t election_id, bool reschedule, bool force) { + if (force) { require_auth(get_self()); } @@ -899,7 +902,6 @@ namespace hypha { Edge::get(get_self(), election_id, upvote_common::links::CURRENT_ROUND).erase(); election.setCurrentRound(&round); election.update(); - initRound(election, round, seed, winners); @@ -908,8 +910,12 @@ namespace hypha { else { eosio::print(" election ended. "); - // The election has ended. + auto round = election.addRound(); + + // Set up the last round to store winners Edge::get(get_self(), election_id, upvote_common::links::CURRENT_ROUND).erase(); + election.setCurrentRound(&round); + election.update(); // delete ongoing election link Edge::get(get_self(), daoId, election.getId(), upvote_common::links::ONGOING_ELECTION).erase(); @@ -924,6 +930,8 @@ namespace hypha { eosio::print(" head del ix: ", headIndex, " winners size: ", winners.size(), " head del: ", headDelegate, " "); + initLastRound(election, round, winners, headDelegate); + assignDelegateBadges(*this, daoId, election.getId(), winners, headDelegate, true); election.setStatus(upvote_common::upvote_status::FINISHED); diff --git a/src/upvote_election/election_round.cpp b/src/upvote_election/election_round.cpp index 564cd38..a131624 100644 --- a/src/upvote_election/election_round.cpp +++ b/src/upvote_election/election_round.cpp @@ -158,7 +158,7 @@ namespace hypha::upvote_election { return {}; } - void ElectionRound::addElectionGroup(std::vector accound_ids) + void ElectionRound::addElectionGroup(std::vector accound_ids, int64_t winner) { ElectionGroup electionGroup( getDao(), @@ -166,10 +166,13 @@ namespace hypha::upvote_election { accound_ids, ElectionGroupData{ .member_count = accound_ids.size(), - .winner = -1 + .winner = winner } ); + if (winner != -1) { + Edge(getDao().get_self(), getDao().get_self(), electionGroup.getId(), winner, links::GROUP_WINNER); + } } } \ No newline at end of file From 244bfac4238a157344bc128169c798c7ddaf8137 Mon Sep 17 00:00:00 2001 From: NIK Date: Fri, 13 Oct 2023 03:02:26 +0800 Subject: [PATCH 44/51] handle case where there's no delegates - cancel --- src/upvote_election/actions.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/upvote_election/actions.cpp b/src/upvote_election/actions.cpp index 1e02be7..c647db6 100644 --- a/src/upvote_election/actions.cpp +++ b/src/upvote_election/actions.cpp @@ -831,11 +831,6 @@ namespace hypha { if (status == upvote_common::upvote_status::UPCOMING) { auto start = election.getStartDate(); - std::string timeStrNow = eosio::time_point_sec(now).to_string(); - std::string timeStrStart = eosio::time_point_sec(start).to_string(); - - eosio::print(" upcoming ", timeStrNow, " ", timeStrStart); - //Let's update as we already started if (start <= now || force) { // START.... @@ -843,6 +838,15 @@ namespace hypha { Edge::get(get_self(), daoId, election.getId(), upvote_common::links::UPCOMING_ELECTION).erase(); + //Setup all candidates + auto delegates = getGraph().getEdgesFrom(daoId, badges::common::links::DELEGATE); + if (delegates.size() < 1) { + eosio::print(" no delegates, cancel election! "); + election.setStatus(upvote_common::upvote_status::CANCELED); + election.update(); + return; + } + Edge(get_self(), get_self(), daoId, election.getId(), upvote_common::links::ONGOING_ELECTION); election.setStatus(upvote_common::upvote_status::ONGOING); @@ -851,10 +855,8 @@ namespace hypha { election.setCurrentRound(&startRound); election.update(); - //Setup all candidates - auto delegates = getGraph().getEdgesFrom(daoId, badges::common::links::DELEGATE); - std::vector delegateIds; + delegateIds.reserve(delegates.size()); for (auto& delegate : delegates) { From 9069bf2d728aa28d95f30bccd7f22d39f61aed49 Mon Sep 17 00:00:00 2001 From: NIK Date: Mon, 16 Oct 2023 10:35:51 +0800 Subject: [PATCH 45/51] remove delete existing badges that code did not work and is not needed in a real scenario where badges expire according to their expiry date. --- src/upvote_election/actions.cpp | 94 +++------------------------------ 1 file changed, 7 insertions(+), 87 deletions(-) diff --git a/src/upvote_election/actions.cpp b/src/upvote_election/actions.cpp index c647db6..cfb0574 100644 --- a/src/upvote_election/actions.cpp +++ b/src/upvote_election/actions.cpp @@ -389,88 +389,16 @@ namespace hypha { uint64_t electionId, const std::vector& chiefDelegates, uint64_t headDelegate, - bool deleteExistingBadges, eosio::transaction* trx = nullptr) { - // Delete existing if required - if (deleteExistingBadges) { - // Note: This must happen before we assign new badges, since new and old badge holders - // might be the same - // Note: This might not be needed since badges actually expire when their period is over. - - //Remove existing Head Delegate/Chief Delegate badges if any - auto cleanBadgesOf = [&](const name& badgeEdge) { - auto badgeId = Edge::get(dao.get_self(), dao.getRootID(), badgeEdge).getToNode(); - - auto badgeAssignmentEdges = dao.getGraph().getEdgesFrom(badgeId, common::ASSIGNMENT); - - if (badgeAssignmentEdges.size() == 0) { - eosio::print(" no existing badge holders ", badgeEdge); - return; - } - - //Filter out those that are not from the specified DAO - auto badgeAssignments = std::vector{}; - badgeAssignments.reserve(badgeAssignmentEdges.size()); - - std::transform( - badgeAssignmentEdges.begin(), - badgeAssignmentEdges.end(), - std::back_inserter(badgeAssignments), - [](const Edge& edge) { - return edge.to_node; - } - ); - - badgeAssignments.erase( - std::remove_if( - badgeAssignments.begin(), - badgeAssignments.end(), - [&](uint64_t id) { - return !Edge::exists(dao.get_self(), id, daoId, common::DAO); - } - ), - badgeAssignments.end() - ); - - for (auto& id : badgeAssignments) { - auto doc = TypedDocument::withType(dao, id, common::ASSIGN_BADGE); - - auto cw = doc.getContentWrapper(); - - cw.insertOrReplace(*cw.getGroupOrFail(SYSTEM), Content{ - "force_archive", - 1 - }); - - cw.insertOrReplace(*cw.getGroupOrFail(DETAILS), Content{ - END_TIME, - eosio::current_time_point() - }); - - doc.update(); - - auto action = eosio::action( - eosio::permission_level(dao.get_self(), eosio::name("active")), - dao.get_self(), - eosio::name("archiverecur"), - std::make_tuple(id) - ); - - if (trx) { - trx->actions.emplace_back(std::move(action)); - } - else { - action.send(); - } - } - }; - - cleanBadgesOf(badges::common::links::HEAD_DELEGATE); - cleanBadgesOf(badges::common::links::CHIEF_DELEGATE); + auto chiefBadgeEdge = Edge::get(dao.get_self(), dao.getRootID(), badges::common::links::CHIEF_DELEGATE); + auto chiefBadgeId = chiefBadgeEdge.getToNode(); + //auto chiefBadge = TypedDocument::withType(dao, chiefBadgeId, common::BADGE_NAME); - } + auto headBadgeEdge = Edge::get(dao.get_self(), dao.getRootID(), badges::common::links::HEAD_DELEGATE); + auto headBadgeId = headBadgeEdge.getToNode(); + //auto headBadge = TypedDocument::withType(dao, headBadgeId, common::BADGE_NAME); // Generate proposals for each one of the delegates // Note: These are auto-approved and instantly on. @@ -509,14 +437,6 @@ namespace hypha { } }; - auto chiefBadgeEdge = Edge::get(dao.get_self(), dao.getRootID(), badges::common::links::CHIEF_DELEGATE); - auto chiefBadgeId = chiefBadgeEdge.getToNode(); - //auto chiefBadge = TypedDocument::withType(dao, chiefBadgeId, common::BADGE_NAME); - - auto headBadgeEdge = Edge::get(dao.get_self(), dao.getRootID(), badges::common::links::HEAD_DELEGATE); - auto headBadgeId = headBadgeEdge.getToNode(); - //auto headBadge = TypedDocument::withType(dao, headBadgeId, common::BADGE_NAME); - for (auto& chief : chiefDelegates) { if (chief != headDelegate) { createAssignment("Chief Delegate", chief, chiefBadgeId); @@ -934,7 +854,7 @@ namespace hypha { initLastRound(election, round, winners, headDelegate); - assignDelegateBadges(*this, daoId, election.getId(), winners, headDelegate, true); + assignDelegateBadges(*this, daoId, election.getId(), winners, headDelegate); election.setStatus(upvote_common::upvote_status::FINISHED); } From 0daab06ba2dbe4c1697e93ad9a3556724b302183 Mon Sep 17 00:00:00 2001 From: NIK Date: Mon, 16 Oct 2023 20:24:10 +0800 Subject: [PATCH 46/51] added allowance for unit testing --- src/proposals/badge_assignment_proposal.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/proposals/badge_assignment_proposal.cpp b/src/proposals/badge_assignment_proposal.cpp index c9c281c..45e4463 100644 --- a/src/proposals/badge_assignment_proposal.cpp +++ b/src/proposals/badge_assignment_proposal.cpp @@ -122,8 +122,13 @@ namespace hypha upvote_election::UpvoteElection upvoteElection(m_dao, election); //Set start as when the election started, // this way duration syncs with the duration of the election - - start = upvoteElection.getStartDate(); + auto electionStartDate = upvoteElection.getStartDate(); + if (start < electionStartDate) { + // this doesn't happen, except while debugging and forcing elections to start + // debug code. In debug mode, keep it at now time point to get the badge assignment. + } else { + start = electionStartDate; + } duration = upvoteElection.getDuration(); } else { From 4f65d05c691973b1b9237e1b2a3f3b03f74ce18a Mon Sep 17 00:00:00 2001 From: NIK Date: Mon, 16 Oct 2023 21:56:47 +0800 Subject: [PATCH 47/51] upload video link --- include/dao.hpp | 2 ++ include/upvote_election/election_group.hpp | 3 ++- src/upvote_election/actions.cpp | 20 ++++++++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/include/dao.hpp b/include/dao.hpp index 177763b..fa8f228 100644 --- a/include/dao.hpp +++ b/include/dao.hpp @@ -338,6 +338,8 @@ namespace pricing { ACTION castupvote(uint64_t round_id, uint64_t group_id, name voter, uint64_t voted_id); ACTION uesubmitseed(uint64_t dao_id, eosio::checksum256 seed, name account); + ACTION upvotevideo(uint64_t group_id, name account, std::string link); + ACTION createupvelc(uint64_t dao_id, ContentGroups& election_config); ACTION editupvelc(uint64_t election_id, ContentGroups& election_config); diff --git a/include/upvote_election/election_group.hpp b/include/upvote_election/election_group.hpp index c24bcb1..9e0e4c7 100644 --- a/include/upvote_election/election_group.hpp +++ b/include/upvote_election/election_group.hpp @@ -21,7 +21,8 @@ class ElectionGroup : public TypedDocument DECLARE_DOCUMENT( Data, PROPERTY(member_count, int64_t, MemberCount, USE_GETSET), - PROPERTY(winner, int64_t, Winner, USE_GETSET) + PROPERTY(winner, int64_t, Winner, USE_GETSET), + PROPERTY(videolink, string, VideoLink, USE_GETSET) ) public: diff --git a/src/upvote_election/actions.cpp b/src/upvote_election/actions.cpp index cfb0574..d70f9f2 100644 --- a/src/upvote_election/actions.cpp +++ b/src/upvote_election/actions.cpp @@ -955,6 +955,26 @@ namespace hypha { } + void dao::upvotevideo(uint64_t group_id, name account, std::string link) + { + // Check auth + eosio::require_auth(account); + auto memberId = getMemberID(account); + + // Check the group membership of voter and voted + ElectionGroup group(*this, group_id); + + EOS_CHECK( + group.isElectionRoundMember(memberId), + "Only members of the group can vote." + ); + + group.setVideoLink(link); + group.update(); + + } + + /* election_config: [ From 141d5894bbf705ce2581429ed12987b4f0b2c68d Mon Sep 17 00:00:00 2001 From: NIK Date: Tue, 24 Oct 2023 00:14:04 +0800 Subject: [PATCH 48/51] delete previous election edge if it exists --- src/upvote_election/actions.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/upvote_election/actions.cpp b/src/upvote_election/actions.cpp index d70f9f2..15600ff 100644 --- a/src/upvote_election/actions.cpp +++ b/src/upvote_election/actions.cpp @@ -842,6 +842,11 @@ namespace hypha { // delete ongoing election link Edge::get(get_self(), daoId, election.getId(), upvote_common::links::ONGOING_ELECTION).erase(); + // delete previous previous election link + if (auto [exists, edge] = Edge::getIfExists(get_self(), daoId, upvote_common::links::PREVIOUS_ELECTION); exists) { + edge.erase(); + } + // add previous election link Edge(get_self(), get_self(), daoId, election.getId(), upvote_common::links::PREVIOUS_ELECTION); From 20d275a2111b25e792a5745c7a5939ef955dfa57 Mon Sep 17 00:00:00 2001 From: Gerard097 Date: Wed, 25 Oct 2023 18:00:10 -0600 Subject: [PATCH 49/51] Add helper function to cleanup DAO from axuliar tables --- include/dao.hpp | 2 ++ src/dao.cpp | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/include/dao.hpp b/include/dao.hpp index ffa4346..3c5b348 100644 --- a/include/dao.hpp +++ b/include/dao.hpp @@ -192,6 +192,8 @@ namespace pricing { ACTION remdoc(uint64_t doc_id); + ACTION cleandao(uint64_t dao_id); + ACTION createcalen(bool is_default); ACTION initcalendar(uint64_t calendar_id, uint64_t next_period); diff --git a/src/dao.cpp b/src/dao.cpp index a51445f..26ad08a 100644 --- a/src/dao.cpp +++ b/src/dao.cpp @@ -67,6 +67,39 @@ void dao::remedge(uint64_t from_node, uint64_t to_node, name edge_name) Edge::get(get_self(), from_node, to_node, edge_name).erase(); } +void dao::cleandao(uint64_t dao_id) +{ + //Remove DAO entries from 'daos' and 'tokentodao' tables + eosio::require_auth(get_self()); + + auto daoDoc = TypedDocument::withType(*this, dao_id, common::DAO); + + auto cw = daoDoc.getContentWrapper(); + + auto name = cw.getOrFail(DETAILS, DAO_NAME)->getAs(); + + remNameID(name); + + token_to_dao_table tok_t(get_self(), get_self().value); + + auto by_id = tok_t.get_index<"bydocid"_n>(); + auto idIt = by_id.find(dao_id); + + if (idIt != by_id.end()){ + by_id.erase(idIt); + } + + auto voiceContract = getSettingOrFail(GOVERNANCE_TOKEN_CONTRACT); + + //delete voice token, reward and peg tokens are not deletable ATM + eosio::action( + eosio::permission_level(get_self(), eosio::name("active")), + voiceContract, + eosio::name("del"), + std::make_tuple(name, asset{0, symbol{"VOICE", 2}}) + ).send(); +} + void dao::remdoc(uint64_t doc_id) { eosio::require_auth(get_self()); From 7056dafdf238aeb2f8a9529cea0f7911867d39dd Mon Sep 17 00:00:00 2001 From: NIK Date: Thu, 26 Oct 2023 10:28:10 +0800 Subject: [PATCH 50/51] deploy scropts --- deploy_eos_testnet.sh | 1 + deploy_testnet.sh | 1 + 2 files changed, 2 insertions(+) create mode 100755 deploy_eos_testnet.sh create mode 100755 deploy_testnet.sh diff --git a/deploy_eos_testnet.sh b/deploy_eos_testnet.sh new file mode 100755 index 0000000..f0ef1db --- /dev/null +++ b/deploy_eos_testnet.sh @@ -0,0 +1 @@ +cleosjungle4 set contract daoxhypha111 build/dao dao_O3.wasm dao.abi \ No newline at end of file diff --git a/deploy_testnet.sh b/deploy_testnet.sh new file mode 100755 index 0000000..94f934e --- /dev/null +++ b/deploy_testnet.sh @@ -0,0 +1 @@ +cleost set contract mtdhoxhyphaa build/dao dao_O3.wasm dao.abi \ No newline at end of file From 006e776249d42657700116d977b0afe7fcc738a9 Mon Sep 17 00:00:00 2001 From: NIK Date: Thu, 26 Oct 2023 10:42:51 +0800 Subject: [PATCH 51/51] deploy scripts --- deploy_eos_mainnet.sh | 1 + deploy_eos_testnet.sh | 2 +- deploy_telos_mainnet.sh | 1 + deploy_testnet.sh | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) create mode 100755 deploy_eos_mainnet.sh create mode 100755 deploy_telos_mainnet.sh diff --git a/deploy_eos_mainnet.sh b/deploy_eos_mainnet.sh new file mode 100755 index 0000000..4d78a71 --- /dev/null +++ b/deploy_eos_mainnet.sh @@ -0,0 +1 @@ +cleos -u http://eos.greymass.com set contract dao.hypha build/dao dao_O3.wasm dao.abi \ No newline at end of file diff --git a/deploy_eos_testnet.sh b/deploy_eos_testnet.sh index f0ef1db..f6f79d6 100755 --- a/deploy_eos_testnet.sh +++ b/deploy_eos_testnet.sh @@ -1 +1 @@ -cleosjungle4 set contract daoxhypha111 build/dao dao_O3.wasm dao.abi \ No newline at end of file +cleos -u https://jungle4.dfuse.eosnation.io set contract daoxhypha111 build/dao dao_O3.wasm dao.abi \ No newline at end of file diff --git a/deploy_telos_mainnet.sh b/deploy_telos_mainnet.sh new file mode 100755 index 0000000..7100f2f --- /dev/null +++ b/deploy_telos_mainnet.sh @@ -0,0 +1 @@ +cleos -u http://mainnet.telos.net set contract dao.hypha build/dao dao_O3.wasm dao.abi \ No newline at end of file diff --git a/deploy_testnet.sh b/deploy_testnet.sh index 94f934e..fbb78f2 100755 --- a/deploy_testnet.sh +++ b/deploy_testnet.sh @@ -1 +1 @@ -cleost set contract mtdhoxhyphaa build/dao dao_O3.wasm dao.abi \ No newline at end of file +cleos -u https://testnet.telos.net set contract mtdhoxhyphaa build/dao dao_O3.wasm dao.abi \ No newline at end of file