From 15edd8b19ccdf2ff5b11308229e6ed662477d480 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Wed, 3 Nov 2021 20:05:21 -0400 Subject: [PATCH 01/54] contract auth --- contracts/eden/include/contract_auth.hpp | 11 +++++ contracts/eden/include/eden.hpp | 42 ++++++++++++++----- contracts/eden/src/actions/elect.cpp | 25 +++++++---- contracts/eden/src/actions/induct.cpp | 36 ++++++++++------ .../contracts/include/eosio/abi_generator.hpp | 6 ++- .../contracts/include/eosio/action.hpp | 32 ++++++++++++++ 6 files changed, 120 insertions(+), 32 deletions(-) create mode 100644 contracts/eden/include/contract_auth.hpp diff --git a/contracts/eden/include/contract_auth.hpp b/contracts/eden/include/contract_auth.hpp new file mode 100644 index 000000000..7ab09b7a7 --- /dev/null +++ b/contracts/eden/include/contract_auth.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include + +namespace eden +{ + struct auth_info + { + void require_auth(eosio::name account) const { eosio::require_auth(account); } + }; +} // namespace eden diff --git a/contracts/eden/include/eden.hpp b/contracts/eden/include/eden.hpp index d8a427f45..929721b58 100644 --- a/contracts/eden/include/eden.hpp +++ b/contracts/eden/include/eden.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -97,21 +98,33 @@ namespace eden void clearall(); - void inductinit(uint64_t id, + void inductinit(const eosio::excluded_arg& auth, + uint64_t id, eosio::name inviter, eosio::name invitee, std::vector witnesses); - void inductprofil(uint64_t id, new_member_profile new_member_profile); + void inductprofil(const eosio::excluded_arg& auth, + uint64_t id, + new_member_profile new_member_profile); - void inductvideo(eosio::name account, uint64_t id, std::string video); + void inductvideo(const eosio::excluded_arg& auth, + eosio::name account, + uint64_t id, + std::string video); - void inductendors(eosio::name account, uint64_t id, eosio::checksum256 induction_data_hash); + void inductendors(const eosio::excluded_arg& auth, + eosio::name account, + uint64_t id, + eosio::checksum256 induction_data_hash); void inductdonate(eosio::name payer, uint64_t id, const eosio::asset& quantity); - void inductcancel(eosio::name account, uint64_t id); - void inductmeetin(eosio::name account, + void inductcancel(const eosio::excluded_arg& auth, + eosio::name account, + uint64_t id); + void inductmeetin(const eosio::excluded_arg& auth, + eosio::name account, uint64_t id, const std::vector& keys, const eosio::bytes& data, @@ -129,16 +142,25 @@ namespace eden const std::string& election_time, uint32_t round_duration_sec); - void electopt(eosio::name member, bool participating); + void electopt(const eosio::excluded_arg& auth, + eosio::name member, + bool participating); void electseed(const eosio::bytes& btc_header); - void electmeeting(eosio::name account, + void electmeeting(const eosio::excluded_arg& auth, + eosio::name account, uint8_t round, const std::vector& keys, const eosio::bytes& data, const std::optional& old_data); - void electvote(uint8_t round, eosio::name voter, eosio::name candidate); - void electvideo(uint8_t round, eosio::name voter, const std::string& video); + void electvote(const eosio::excluded_arg& auth, + uint8_t round, + eosio::name voter, + eosio::name candidate); + void electvideo(const eosio::excluded_arg& auth, + uint8_t round, + eosio::name voter, + const std::string& video); void electprocess(uint32_t max_steps); void distribute(uint32_t max_steps); diff --git a/contracts/eden/src/actions/elect.cpp b/contracts/eden/src/actions/elect.cpp index e73192224..69085a0ee 100644 --- a/contracts/eden/src/actions/elect.cpp +++ b/contracts/eden/src/actions/elect.cpp @@ -26,9 +26,11 @@ namespace eden globals.set_election_round_duration(round_duration); } - void eden::electopt(eosio::name voter, bool participating) + void eden::electopt(const eosio::excluded_arg& auth, + eosio::name voter, + bool participating) { - eosio::require_auth(voter); + auth.value.require_auth(voter); members members{get_self()}; const auto& member = members.get_member(voter); @@ -51,13 +53,14 @@ namespace eden elections.seed(btc_header); } - void eden::electmeeting(eosio::name account, + void eden::electmeeting(const eosio::excluded_arg& auth, + eosio::name account, uint8_t round, const std::vector& keys, const eosio::bytes& data, const std::optional& old_data) { - eosio::require_auth(account); + auth.value.require_auth(account); members members{get_self()}; elections elections{get_self()}; auto group_id = elections.get_group_id(account, round); @@ -66,16 +69,22 @@ namespace eden encrypt.set(group_id, keys, data, old_data); } - void eden::electvote(uint8_t round, eosio::name voter, eosio::name candidate) + void eden::electvote(const eosio::excluded_arg& auth, + uint8_t round, + eosio::name voter, + eosio::name candidate) { - eosio::require_auth(voter); + auth.value.require_auth(voter); elections elections(get_self()); elections.vote(round, voter, candidate); } - void eden::electvideo(uint8_t round, eosio::name voter, const std::string& video) + void eden::electvideo(const eosio::excluded_arg& auth, + uint8_t round, + eosio::name voter, + const std::string& video) { - eosio::require_auth(voter); + auth.value.require_auth(voter); elections elections{get_self()}; members members{get_self()}; if (auto check = elections.can_upload_video(round, voter); boost::logic::indeterminate(check)) diff --git a/contracts/eden/src/actions/induct.cpp b/contracts/eden/src/actions/induct.cpp index 93b1d04c2..4801ab10e 100644 --- a/contracts/eden/src/actions/induct.cpp +++ b/contracts/eden/src/actions/induct.cpp @@ -10,12 +10,13 @@ namespace eden { - void eden::inductinit(uint64_t id, + void eden::inductinit(const eosio::excluded_arg& auth, + uint64_t id, eosio::name inviter, eosio::name invitee, std::vector witnesses) { - require_auth(inviter); + auth.value.require_auth(inviter); globals{get_self()}.check_active(); @@ -33,13 +34,14 @@ namespace eden inductions{get_self()}.initialize_induction(id, inviter, invitee, witnesses); } - void eden::inductmeetin(eosio::name account, + void eden::inductmeetin(const eosio::excluded_arg& auth, + eosio::name account, uint64_t id, const std::vector& keys, const eosio::bytes& data, const std::optional& old_data) { - require_auth(account); + auth.value.require_auth(account); globals{get_self()}.check_active(); members members{get_self()}; @@ -52,11 +54,13 @@ namespace eden encrypt.set(id, keys, data, old_data); } - void eden::inductprofil(uint64_t id, new_member_profile new_member_profile) + void eden::inductprofil(const eosio::excluded_arg& auth, + uint64_t id, + new_member_profile new_member_profile) { inductions inductions{get_self()}; const auto& induction = inductions.get_induction(id); - require_auth(induction.invitee()); + auth.value.require_auth(induction.invitee()); members{get_self()}.check_pending_member(induction.invitee()); @@ -69,9 +73,12 @@ namespace eden } } - void eden::inductvideo(eosio::name account, uint64_t id, std::string video) + void eden::inductvideo(const eosio::excluded_arg& auth, + eosio::name account, + uint64_t id, + std::string video) { - require_auth(account); + auth.value.require_auth(account); inductions inductions{get_self()}; const auto& induction = inductions.get_induction(id); @@ -83,9 +90,12 @@ namespace eden inductions.update_video(induction, video); } - void eden::inductendors(eosio::name account, uint64_t id, eosio::checksum256 induction_data_hash) + void eden::inductendors(const eosio::excluded_arg& auth, + eosio::name account, + uint64_t id, + eosio::checksum256 induction_data_hash) { - require_auth(account); + auth.value.require_auth(account); inductions inductions{get_self()}; const auto& induction = inductions.get_induction(id); @@ -178,9 +188,11 @@ namespace eden } } - void eden::inductcancel(eosio::name account, uint64_t id) + void eden::inductcancel(const eosio::excluded_arg& auth, + eosio::name account, + uint64_t id) { - eosio::require_auth(account); + auth.value.require_auth(account); inductions inductions{get_self()}; bool is_genesis = globals{get_self()}.get().stage == contract_stage::genesis; diff --git a/libraries/eosiolib/contracts/include/eosio/abi_generator.hpp b/libraries/eosiolib/contracts/include/eosio/abi_generator.hpp index 9accdf507..61b0e4186 100644 --- a/libraries/eosiolib/contracts/include/eosio/abi_generator.hpp +++ b/libraries/eosiolib/contracts/include/eosio/abi_generator.hpp @@ -184,14 +184,16 @@ namespace eosio template void add_action_args(struct_def& def, std::tuple*, N name, Ns... names) { - def.fields.push_back({name, get_type()}); + if constexpr (!is_excluded_arg((remove_cvref_t*)nullptr)) + def.fields.push_back({name, get_type()}); add_action_args(def, (std::tuple*)nullptr, names...); } template void add_action_args(struct_def& def, std::tuple*) { - def.fields.push_back({"arg" + std::to_string(i), get_type()}); + if constexpr (!is_excluded_arg((remove_cvref_t*)nullptr)) + def.fields.push_back({"arg" + std::to_string(i), get_type()}); add_action_args(def, (std::tuple*)nullptr); } diff --git a/libraries/eosiolib/contracts/include/eosio/action.hpp b/libraries/eosiolib/contracts/include/eosio/action.hpp index 0ec0db883..dfeb3689c 100644 --- a/libraries/eosiolib/contracts/include/eosio/action.hpp +++ b/libraries/eosiolib/contracts/include/eosio/action.hpp @@ -54,6 +54,28 @@ namespace eosio } }; // namespace internal_use_do_not_use + template + struct excluded_arg + { + T value; + }; + template + constexpr void eosio_for_each_field(excluded_arg*, F f) + { + } + + template + constexpr bool is_excluded_arg(T*) + { + return false; + } + + template + constexpr bool is_excluded_arg(excluded_arg*) + { + return true; + } + /** * @defgroup action Action * @ingroup contracts @@ -391,6 +413,16 @@ namespace eosio { return std::tuple::type>...>{}; } + template + auto get_args(R (Act::*p)(const excluded_arg&, Args...)) + { + return std::tuple::type>...>{}; + } + template + auto get_args(R (Act::*p)(const excluded_arg&, Args...) const) + { + return std::tuple::type>...>{}; + } template auto get_args_nounwrap(R (Act::*p)(Args...)) From b7257c2ad22e26f0c765f46314591c9515736fb0 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Thu, 4 Nov 2021 12:41:28 -0400 Subject: [PATCH 02/54] contract auth --- contracts/eden/include/eden.hpp | 50 ++++++++++++++-------- contracts/eden/include/eden_dispatcher.hpp | 7 +++ 2 files changed, 38 insertions(+), 19 deletions(-) create mode 100644 contracts/eden/include/eden_dispatcher.hpp diff --git a/contracts/eden/include/eden.hpp b/contracts/eden/include/eden.hpp index 929721b58..e821cf7b7 100644 --- a/contracts/eden/include/eden.hpp +++ b/contracts/eden/include/eden.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -236,35 +237,46 @@ namespace eden action(addtogenesis, account, expiration), action(gensetexpire, id, new_expiration), action(clearall, ricardian_contract(clearall_ricardian)), - action(inductinit, - id, - inviter, - invitee, - witnesses, - ricardian_contract(inductinit_ricardian)), - action(inductmeetin, account, id, keys, data, old_data), - action(inductprofil, id, new_member_profile, ricardian_contract(inductprofil_ricardian)), - action(inductvideo, account, id, video, ricardian_contract(inductvideo_ricardian)), - action(inductendors, - account, - id, - induction_data_hash, - ricardian_contract(inductendors_ricardian)), + eden_auth_action(inductinit, + 0, + id, + inviter, + invitee, + witnesses, + ricardian_contract(inductinit_ricardian)), + eden_auth_action(inductmeetin, 1, account, id, keys, data, old_data), + eden_auth_action(inductprofil, + 2, + id, + new_member_profile, + ricardian_contract(inductprofil_ricardian)), + eden_auth_action(inductvideo, + 3, + account, + id, + video, + ricardian_contract(inductvideo_ricardian)), + eden_auth_action(inductendors, + 4, + account, + id, + induction_data_hash, + ricardian_contract(inductendors_ricardian)), action(setencpubkey, account, key), action(electsettime, election_time), action(electconfig, day, time, round_duration), - action(electopt, member, participating), + eden_auth_action(electopt, 5, member, participating), action(electseed, btc_header), - action(electmeeting, account, round, keys, data, old_data), - action(electvote, round, voter, candidate), - action(electvideo, round, voter, video), + eden_auth_action(electmeeting, 6, account, round, keys, data, old_data), + eden_auth_action(electvote, 7, round, voter, candidate), + eden_auth_action(electvideo, 8, round, voter, video), action(electprocess, max_steps), action(bylawspropose, proposer, bylaws), action(bylawsapprove, approver, bylaws_hash), action(bylawsratify, approver, bylaws_hash), action(distribute, max_steps), action(inductdonate, payer, id, quantity, ricardian_contract(inductdonate_ricardian)), - action(inductcancel, account, id, ricardian_contract(inductcancel_ricardian)), + eden_auth_action(inductcancel, 9, account, id, ricardian_contract(inductcancel_ricardian)), action(inducted, inductee, ricardian_contract(inducted_ricardian)), action(resign, account), action(gc, limit, ricardian_contract(gc_ricardian)), diff --git a/contracts/eden/include/eden_dispatcher.hpp b/contracts/eden/include/eden_dispatcher.hpp new file mode 100644 index 000000000..a70324d58 --- /dev/null +++ b/contracts/eden/include/eden_dispatcher.hpp @@ -0,0 +1,7 @@ +#pragma once + +#include + +#define EOSIO_MATCH_ACTIONeden_auth_action EOSIO_MATCH_YES +#define EOSIO_EXTRACT_ACTION_NAMEeden_auth_action(name, index, ...) name +#define EOSIO_EXTRACT_ACTION_ARGSeden_auth_action(name, index, ...) __VA_ARGS__ From 4716d5a5d0dd79a702142af673f38ebab0dd67db Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Thu, 4 Nov 2021 14:21:07 -0400 Subject: [PATCH 03/54] contract auth --- contracts/eden/include/eden.hpp | 2 +- contracts/eden/include/eden_dispatcher.hpp | 49 ++++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/contracts/eden/include/eden.hpp b/contracts/eden/include/eden.hpp index e821cf7b7..f8016063b 100644 --- a/contracts/eden/include/eden.hpp +++ b/contracts/eden/include/eden.hpp @@ -213,7 +213,7 @@ namespace eden eosio::ignore>); }; - EOSIO_ACTIONS( + EDEN_ACTIONS( eden, "eden.gm"_n, action(withdraw, owner, quantity, ricardian_contract(withdraw_ricardian)), diff --git a/contracts/eden/include/eden_dispatcher.hpp b/contracts/eden/include/eden_dispatcher.hpp index a70324d58..791ba36ca 100644 --- a/contracts/eden/include/eden_dispatcher.hpp +++ b/contracts/eden/include/eden_dispatcher.hpp @@ -1,7 +1,56 @@ #pragma once +#include #include +namespace eden +{ + template + void execute_auth_action(eosio::name contract, + R (T::*func)(const eosio::excluded_arg& auth, Args...), + const eosio::excluded_arg& auth, + eosio::datastream& ds) + { + std::tuple...> t; + ds >> t; + T inst(contract, contract, ds); + std::apply([&](auto&... args) { (inst.*func)(auth, std::move(args)...); }, t); + } +} // namespace eden + #define EOSIO_MATCH_ACTIONeden_auth_action EOSIO_MATCH_YES #define EOSIO_EXTRACT_ACTION_NAMEeden_auth_action(name, index, ...) name #define EOSIO_EXTRACT_ACTION_ARGSeden_auth_action(name, index, ...) __VA_ARGS__ + +#define EDEN_MATCH_AUTH_ACTION(x) EOSIO_MATCH(EDEN_MATCH_AUTH_ACTION, x) +#define EDEN_MATCH_AUTH_ACTIONeden_auth_action EOSIO_MATCH_YES + +#define EDEN_EXTRACT_AUTH_ACTION_INDEX(x) BOOST_PP_CAT(EDEN_EXTRACT_AUTH_ACTION_INDEX, x) +#define EDEN_EXTRACT_AUTH_ACTION_INDEXeden_auth_action(name, index, ...) index + +#define EDEN_DISPATCH_AUTH_ACTION_INTERNAL_1(r, type, member) \ + case EDEN_EXTRACT_AUTH_ACTION_INDEX(member): \ + ::eden::execute_auth_action(contract, &type::EOSIO_EXTRACT_ACTION_NAME(member), auth, ds); \ + return true; +#define EDEN_DISPATCH_AUTH_ACTION_INTERNAL(r, type, member) \ + BOOST_PP_IIF(EDEN_MATCH_AUTH_ACTION(member), EDEN_DISPATCH_AUTH_ACTION_INTERNAL_1, EOSIO_EMPTY) \ + (r, type, member) +#define EDEN_DISPATCH_AUTH_ACTION(type, MEMBERS) \ + BOOST_PP_SEQ_FOR_EACH(EDEN_DISPATCH_AUTH_ACTION_INTERNAL, type, MEMBERS) + +#define EDEN_ACTIONS(CONTRACT_CLASS, CONTRACT_ACCOUNT, ...) \ + EOSIO_ACTIONS(CONTRACT_CLASS, CONTRACT_ACCOUNT, __VA_ARGS__) \ + namespace actions \ + { \ + inline bool dispatch_auth(eosio::name contract, \ + uint32_t index, \ + const eosio::excluded_arg& auth, \ + eosio::datastream& ds) \ + { \ + switch (index) \ + { \ + EDEN_DISPATCH_AUTH_ACTION(CONTRACT_CLASS, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \ + } \ + return false; \ + } \ + } From 83dcd2ce40d60f9a4aa815d64918311b72276e9f Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Thu, 4 Nov 2021 19:04:48 -0400 Subject: [PATCH 04/54] contract auth --- contracts/eden/include/eden_abi_generator.hpp | 14 ++++++++++++++ contracts/eden/include/eden_dispatcher.hpp | 15 +++++++++++++++ contracts/eden/src/eden.cpp | 2 ++ 3 files changed, 31 insertions(+) create mode 100644 contracts/eden/include/eden_abi_generator.hpp diff --git a/contracts/eden/include/eden_abi_generator.hpp b/contracts/eden/include/eden_abi_generator.hpp new file mode 100644 index 000000000..195ca5c6a --- /dev/null +++ b/contracts/eden/include/eden_abi_generator.hpp @@ -0,0 +1,14 @@ +#pragma once + +#define EOSIO_ABIGEN_ITEMauth_actions(ns, variant_name, missing_struct_name) \ + ([&] { \ + gen.def.structs.push_back(eosio::struct_def{missing_struct_name}); \ + eosio::variant_def vdef{variant_name}; \ + ns::for_each_auth_action([&](uint32_t index, const char* name, const auto&) { \ + if (index >= vdef.types.size()) \ + vdef.types.resize(index + 1, missing_struct_name); \ + vdef.types[index] = name; \ + }); \ + gen.def.variants.value.push_back(std::move(vdef)); \ + })(); \ + , 1 diff --git a/contracts/eden/include/eden_dispatcher.hpp b/contracts/eden/include/eden_dispatcher.hpp index 791ba36ca..01f779a4f 100644 --- a/contracts/eden/include/eden_dispatcher.hpp +++ b/contracts/eden/include/eden_dispatcher.hpp @@ -38,6 +38,16 @@ namespace eden #define EDEN_DISPATCH_AUTH_ACTION(type, MEMBERS) \ BOOST_PP_SEQ_FOR_EACH(EDEN_DISPATCH_AUTH_ACTION_INTERNAL, type, MEMBERS) +#define EDEN_GET_AUTH_ACTION_INTERNAL_1(r, type, member) \ + f(EDEN_EXTRACT_AUTH_ACTION_INDEX(member), \ + BOOST_PP_STRINGIZE(EOSIO_EXTRACT_ACTION_NAME(member)), \ + &type::EOSIO_EXTRACT_ACTION_NAME(member)); +#define EDEN_GET_AUTH_ACTION_INTERNAL(r, type, member) \ + BOOST_PP_IIF(EDEN_MATCH_AUTH_ACTION(member), EDEN_GET_AUTH_ACTION_INTERNAL_1, EOSIO_EMPTY) \ + (r, type, member) +#define EDEN_GET_AUTH_ACTION(type, MEMBERS) \ + BOOST_PP_SEQ_FOR_EACH(EDEN_GET_AUTH_ACTION_INTERNAL, type, MEMBERS) + #define EDEN_ACTIONS(CONTRACT_CLASS, CONTRACT_ACCOUNT, ...) \ EOSIO_ACTIONS(CONTRACT_CLASS, CONTRACT_ACCOUNT, __VA_ARGS__) \ namespace actions \ @@ -53,4 +63,9 @@ namespace eden } \ return false; \ } \ + template \ + void for_each_auth_action(F f) \ + { \ + EDEN_GET_AUTH_ACTION(CONTRACT_CLASS, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \ + } \ } diff --git a/contracts/eden/src/eden.cpp b/contracts/eden/src/eden.cpp index 5b1ae2172..4e7af0a96 100644 --- a/contracts/eden/src/eden.cpp +++ b/contracts/eden/src/eden.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -49,6 +50,7 @@ EOSIO_ABIGEN( "DOUBLE_VEC", "STRING_VEC"), actions(eden::actions), + auth_actions(eden::actions, "actions", "unsupported_action"), table("account"_n, eden::account_variant), table("auction"_n, eden::auction_variant), table("bylaws"_n, eden::bylaws_variant), From 2cfac49f7890bd0fca0e457499ba3f71059390d4 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Fri, 5 Nov 2021 11:31:19 -0400 Subject: [PATCH 05/54] contract auth --- contracts/eden/CMakeLists.txt | 1 + contracts/eden/include/eden.hpp | 9 +++++++++ contracts/eden/include/eden_abi_generator.hpp | 8 +++++++- contracts/eden/src/actions/contract_auth.cpp | 13 +++++++++++++ contracts/eden/src/eden.cpp | 3 ++- 5 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 contracts/eden/src/actions/contract_auth.cpp diff --git a/contracts/eden/CMakeLists.txt b/contracts/eden/CMakeLists.txt index ec019e00d..9693c0d2c 100644 --- a/contracts/eden/CMakeLists.txt +++ b/contracts/eden/CMakeLists.txt @@ -17,6 +17,7 @@ add_executable(eden src/actions/migrate.cpp src/actions/encrypt.cpp src/actions/tables.cpp + src/actions/contract_auth.cpp src/eden.cpp src/events.cpp src/accounts.cpp diff --git a/contracts/eden/include/eden.hpp b/contracts/eden/include/eden.hpp index f8016063b..ef61de4d5 100644 --- a/contracts/eden/include/eden.hpp +++ b/contracts/eden/include/eden.hpp @@ -42,6 +42,9 @@ namespace eden extern const char* peacetreaty_clause; extern const char* bylaws_clause; + // Placeholder; the ABI generator redefines this + using action = std::variant; + #ifdef ENABLE_SET_TABLE_ROWS using table_variant = boost::mp11::mp_append> actions); + void withdraw(eosio::name owner, const eosio::asset& quantity); void donate(eosio::name payer, const eosio::asset& quantity); @@ -216,6 +224,7 @@ namespace eden EDEN_ACTIONS( eden, "eden.gm"_n, + action(runactions, sig, account, sequence, actions), action(withdraw, owner, quantity, ricardian_contract(withdraw_ricardian)), action(donate, owner, quantity), action(transfer, to, quantity, memo), diff --git a/contracts/eden/include/eden_abi_generator.hpp b/contracts/eden/include/eden_abi_generator.hpp index 195ca5c6a..1a41973e3 100644 --- a/contracts/eden/include/eden_abi_generator.hpp +++ b/contracts/eden/include/eden_abi_generator.hpp @@ -9,6 +9,12 @@ vdef.types.resize(index + 1, missing_struct_name); \ vdef.types[index] = name; \ }); \ - gen.def.variants.value.push_back(std::move(vdef)); \ + auto& variants = gen.def.variants.value; \ + auto it = std::find_if(variants.begin(), variants.end(), \ + [&](auto& d) { return d.name == variant_name; }); \ + if (it != variants.end()) \ + *it = std::move(vdef); \ + else \ + variants.push_back(std::move(vdef)); \ })(); \ , 1 diff --git a/contracts/eden/src/actions/contract_auth.cpp b/contracts/eden/src/actions/contract_auth.cpp new file mode 100644 index 000000000..c37a004ad --- /dev/null +++ b/contracts/eden/src/actions/contract_auth.cpp @@ -0,0 +1,13 @@ +#include + +namespace eden +{ + void eden::runactions(const eosio::signature& sig, + eosio::name account, + eosio::varuint32 sequence, + eosio::ignore> actions) + { + // + } + +} // namespace eden diff --git a/contracts/eden/src/eden.cpp b/contracts/eden/src/eden.cpp index 4e7af0a96..39831af02 100644 --- a/contracts/eden/src/eden.cpp +++ b/contracts/eden/src/eden.cpp @@ -49,8 +49,9 @@ EOSIO_ABIGEN( "FLOAT_VEC", "DOUBLE_VEC", "STRING_VEC"), + variant("action", eden::action), actions(eden::actions), - auth_actions(eden::actions, "actions", "unsupported_action"), + auth_actions(eden::actions, "action", "unsupported_action"), table("account"_n, eden::account_variant), table("auction"_n, eden::auction_variant), table("bylaws"_n, eden::bylaws_variant), From 5459062735c099297e92d6e51152bff4037adaea Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Fri, 5 Nov 2021 12:53:06 -0400 Subject: [PATCH 06/54] contract auth --- contracts/eden/include/contract_auth.hpp | 46 +++++++++++++++++++- contracts/eden/include/eden.hpp | 4 +- contracts/eden/src/actions/contract_auth.cpp | 17 ++++++-- contracts/eden/src/eden.cpp | 1 + 4 files changed, 61 insertions(+), 7 deletions(-) diff --git a/contracts/eden/include/contract_auth.hpp b/contracts/eden/include/contract_auth.hpp index 7ab09b7a7..cc3e575d8 100644 --- a/contracts/eden/include/contract_auth.hpp +++ b/contracts/eden/include/contract_auth.hpp @@ -1,11 +1,53 @@ #pragma once -#include +#include +#include namespace eden { + struct session + { + eosio::public_key key; + eosio::block_timestamp expiration; + std::vector sequences; + }; + EOSIO_REFLECT(session, key, expiration, sequences) + + struct session_container_v0 + { + eosio::name owner; + eosio::block_timestamp earliest_expiration; + std::vector sessions; + + uint64_t primary_key() const { return owner.value; } + uint64_t by_expiration() const { return earliest_expiration.slot; } + }; + EOSIO_REFLECT(session_container_v0, owner, earliest_expiration, sessions) + + using session_container_variant = std::variant; + + struct session_container + { + session_container_variant value; + EDEN_FORWARD_MEMBERS(value, owner, earliest_expiration, sessions); + EDEN_FORWARD_FUNCTIONS(value, primary_key, by_expiration) + }; + EOSIO_REFLECT(session_container, value) + + using sessions_table_type = eosio::multi_index< + "sessions"_n, + session_container, + eosio::indexed_by< + "byexpiration"_n, + eosio::const_mem_fun>>; + struct auth_info { - void require_auth(eosio::name account) const { eosio::require_auth(account); } + eosio::name authorized_eden_account; + void require_auth(eosio::name eden_account) const + { + if (eden_account != authorized_eden_account) + eosio::require_auth(eden_account); + } }; } // namespace eden diff --git a/contracts/eden/include/eden.hpp b/contracts/eden/include/eden.hpp index ef61de4d5..407a04e7f 100644 --- a/contracts/eden/include/eden.hpp +++ b/contracts/eden/include/eden.hpp @@ -80,7 +80,7 @@ namespace eden std::string memo); void runactions(const eosio::signature& sig, - eosio::name account, + eosio::name eden_account, eosio::varuint32 sequence, eosio::ignore> actions); @@ -224,7 +224,7 @@ namespace eden EDEN_ACTIONS( eden, "eden.gm"_n, - action(runactions, sig, account, sequence, actions), + action(runactions, sig, eden_account, sequence, actions), action(withdraw, owner, quantity, ricardian_contract(withdraw_ricardian)), action(donate, owner, quantity), action(transfer, to, quantity, memo), diff --git a/contracts/eden/src/actions/contract_auth.cpp b/contracts/eden/src/actions/contract_auth.cpp index c37a004ad..5c4dc1d57 100644 --- a/contracts/eden/src/actions/contract_auth.cpp +++ b/contracts/eden/src/actions/contract_auth.cpp @@ -3,11 +3,22 @@ namespace eden { void eden::runactions(const eosio::signature& sig, - eosio::name account, + eosio::name eden_account, eosio::varuint32 sequence, eosio::ignore> actions) { - // - } + eosio::excluded_arg auth; + auth.value.authorized_eden_account = eden_account; + eosio::varuint32 num_actions; + get_datastream() >> num_actions; + eosio::check(num_actions.value > 0, "actions is empty"); + for (uint32_t i = 0; i < num_actions.value; ++i) + { + eosio::varuint32 index; + get_datastream() >> index; + eosio::check(actions::dispatch_auth(get_self(), index.value, auth, get_datastream()), + "unsupported action index"); + } + } } // namespace eden diff --git a/contracts/eden/src/eden.cpp b/contracts/eden/src/eden.cpp index 39831af02..d30a6c885 100644 --- a/contracts/eden/src/eden.cpp +++ b/contracts/eden/src/eden.cpp @@ -67,6 +67,7 @@ EOSIO_ABIGEN( table("memberstats"_n, eden::member_stats_variant), table("migration"_n, eden::migration_variant), table("pools"_n, eden::pool_variant), + table("sessions"_n, eden::session_container_variant), table("votes"_n, eden::vote), ricardian_clause("peacetreaty", eden::peacetreaty_clause), ricardian_clause("bylaws", eden::bylaws_clause)) From 03ecc684149892926b73ec678c9941aec2a8b391 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Fri, 5 Nov 2021 19:09:20 -0400 Subject: [PATCH 07/54] contract auth --- contracts/eden/include/eden.hpp | 6 +- contracts/eden/src/actions/contract_auth.cpp | 71 +++++++++++++++++--- 2 files changed, 66 insertions(+), 11 deletions(-) diff --git a/contracts/eden/include/eden.hpp b/contracts/eden/include/eden.hpp index 407a04e7f..3eb04f256 100644 --- a/contracts/eden/include/eden.hpp +++ b/contracts/eden/include/eden.hpp @@ -79,9 +79,9 @@ namespace eden const eosio::asset& quantity, std::string memo); - void runactions(const eosio::signature& sig, - eosio::name eden_account, - eosio::varuint32 sequence, + void runactions(const eosio::signature& signature, + eosio::ignore eden_account, + eosio::ignore sequence, eosio::ignore> actions); void withdraw(eosio::name owner, const eosio::asset& quantity); diff --git a/contracts/eden/src/actions/contract_auth.cpp b/contracts/eden/src/actions/contract_auth.cpp index 5c4dc1d57..fd9bdd522 100644 --- a/contracts/eden/src/actions/contract_auth.cpp +++ b/contracts/eden/src/actions/contract_auth.cpp @@ -2,23 +2,78 @@ namespace eden { - void eden::runactions(const eosio::signature& sig, - eosio::name eden_account, - eosio::varuint32 sequence, - eosio::ignore> actions) + void expire(session_container& sc) { + auto now = eosio::current_block_time(); + auto& sessions = sc.sessions(); + auto new_end = std::remove_if(sessions.begin(), sessions.end(), + [&](auto& session) { return session.expiration <= now; }); + sessions.erase(new_end, sessions.end()); + + auto expiration = eosio::block_timestamp::max(); + for (auto& session : sessions) + expiration = std::min(expiration, session.expiration); + sc.earliest_expiration() = expiration; + } + + void eden::runactions(const eosio::signature& signature, + eosio::ignore, + eosio::ignore, + eosio::ignore>) + { + auto& ds = get_datastream(); + auto digest = eosio::sha256(ds.pos(), ds.remaining()); + auto recovered = eosio::recover_key(digest, signature); + + eosio::name eden_account; + eosio::varuint32 sequence; + ds >> eden_account; + ds >> sequence; + + sessions_table_type table(get_self(), default_scope); + auto sc = table.find(eden_account.value); + eosio::check(sc != table.end(), "User has no session keys"); + table.modify(sc, get_self(), [&](auto& sc) { + expire(sc); + auto& sessions = sc.sessions(); + auto session = std::find_if(sessions.begin(), sessions.end(), + [&](auto& session) { return session.key == recovered; }); + if (session == sessions.end()) + eosio::check(false, "Recovered session key " + public_key_to_string(recovered) + + " is either expired or not found"); + + auto& sequences = session->sequences; + if (sequences.begin() != sequences.end()) + { + if (sequence.value < *sequences.begin()) + eosio::check(false, "received duplicate sequence " + std::to_string(sequence.value)); + else if (sequence.value > sequences.end()[-1].value + 10) + eosio::check(false, + "sequence " + std::to_string(sequence.value) + " skips too many"); + } + auto it = std::lower_bound(sequences.begin(), sequences.end(), sequence); + if (it != sequences.end() && *it == sequence) + eosio::check(false, "received duplicate sequence " + std::to_string(sequence.value)); + sequences.insert(it, sequence); + if (sequences.size() > 20) + sequences.erase(sequences.begin()); + }); + eosio::excluded_arg auth; auth.value.authorized_eden_account = eden_account; eosio::varuint32 num_actions; - get_datastream() >> num_actions; + ds >> num_actions; eosio::check(num_actions.value > 0, "actions is empty"); for (uint32_t i = 0; i < num_actions.value; ++i) { eosio::varuint32 index; - get_datastream() >> index; - eosio::check(actions::dispatch_auth(get_self(), index.value, auth, get_datastream()), + ds >> index; + eosio::check(actions::dispatch_auth(get_self(), index.value, auth, ds), "unsupported action index"); } - } + + eosio::check(!ds.remaining(), "detected extra action data after post"); + + } // eden::runactions } // namespace eden From 301177fa0454ae7a74158b9fc64a1a447056c924 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Sat, 6 Nov 2021 13:04:42 -0400 Subject: [PATCH 08/54] contract auth --- contracts/eden/include/contract_auth.hpp | 15 ++-- contracts/eden/include/eden.hpp | 13 +++- contracts/eden/src/actions/contract_auth.cpp | 72 +++++++++++++++++++- 3 files changed, 90 insertions(+), 10 deletions(-) diff --git a/contracts/eden/include/contract_auth.hpp b/contracts/eden/include/contract_auth.hpp index cc3e575d8..19508935c 100644 --- a/contracts/eden/include/contract_auth.hpp +++ b/contracts/eden/include/contract_auth.hpp @@ -5,31 +5,32 @@ namespace eden { - struct session + struct session_v0 { eosio::public_key key; eosio::block_timestamp expiration; + std::string description; std::vector sequences; }; - EOSIO_REFLECT(session, key, expiration, sequences) + EOSIO_REFLECT(session_v0, key, expiration, description, sequences) struct session_container_v0 { - eosio::name owner; + eosio::name eden_account; eosio::block_timestamp earliest_expiration; - std::vector sessions; + std::vector sessions; - uint64_t primary_key() const { return owner.value; } + uint64_t primary_key() const { return eden_account.value; } uint64_t by_expiration() const { return earliest_expiration.slot; } }; - EOSIO_REFLECT(session_container_v0, owner, earliest_expiration, sessions) + EOSIO_REFLECT(session_container_v0, eden_account, earliest_expiration, sessions) using session_container_variant = std::variant; struct session_container { session_container_variant value; - EDEN_FORWARD_MEMBERS(value, owner, earliest_expiration, sessions); + EDEN_FORWARD_MEMBERS(value, eden_account, earliest_expiration, sessions); EDEN_FORWARD_FUNCTIONS(value, primary_key, by_expiration) }; EOSIO_REFLECT(session_container, value) diff --git a/contracts/eden/include/eden.hpp b/contracts/eden/include/eden.hpp index 3eb04f256..03916f864 100644 --- a/contracts/eden/include/eden.hpp +++ b/contracts/eden/include/eden.hpp @@ -79,6 +79,15 @@ namespace eden const eosio::asset& quantity, std::string memo); + void newsession(eosio::name eden_account, + const eosio::public_key& key, + eosio::block_timestamp expiration, + const std::string& description); + + void delsession(const eosio::excluded_arg& auth, + eosio::name eden_account, + const eosio::public_key& key); + void runactions(const eosio::signature& signature, eosio::ignore eden_account, eosio::ignore sequence, @@ -224,6 +233,8 @@ namespace eden EDEN_ACTIONS( eden, "eden.gm"_n, + action(newsession, eden_account, key, expiration, description), + eden_auth_action(delsession, 0, eden_account, key), action(runactions, sig, eden_account, sequence, actions), action(withdraw, owner, quantity, ricardian_contract(withdraw_ricardian)), action(donate, owner, quantity), @@ -247,7 +258,7 @@ namespace eden action(gensetexpire, id, new_expiration), action(clearall, ricardian_contract(clearall_ricardian)), eden_auth_action(inductinit, - 0, + 10, id, inviter, invitee, diff --git a/contracts/eden/src/actions/contract_auth.cpp b/contracts/eden/src/actions/contract_auth.cpp index fd9bdd522..d7a18189f 100644 --- a/contracts/eden/src/actions/contract_auth.cpp +++ b/contracts/eden/src/actions/contract_auth.cpp @@ -16,6 +16,73 @@ namespace eden sc.earliest_expiration() = expiration; } + void eden::newsession(eosio::name eden_account, + const eosio::public_key& key, + eosio::block_timestamp expiration, + const std::string& description) + { + eosio::require_auth(eden_account); + eosio::check(key.index() < 2, "unsupported key type"); + eosio::check(expiration > eosio::current_block_time(), "session is expired"); + eosio::check(expiration <= eosio::current_block_time().to_time_point() + eosio::days(90), + "expiration is too far in the future"); + eosio::check(description.size() <= 20, "description is too long"); + + sessions_table_type table(get_self(), default_scope); + auto sc = table.find(eden_account.value); + if (sc == table.end()) + { + table.emplace(get_self(), [&](auto& sc) { + sc.eden_account() = eden_account; + sc.earliest_expiration() = expiration; + sc.sessions().push_back(session_v0{ + .key = key, + .expiration = expiration, + .description = description, + }); + }); + } + else + { + table.modify(sc, get_self(), [&](auto& sc) { + auto& sessions = sc.sessions(); + auto session = std::find_if(sessions.begin(), sessions.end(), + [&](auto& session) { return session.key == key; }); + eosio::check(session == sessions.end(), "session key already exists"); + sessions.push_back(session_v0{ + .key = key, + .expiration = expiration, + .description = description, + }); + if (sessions.size() > 4) + sessions.erase(sessions.begin()); + expire(sc); // also sets earliest_expiration + }); + } + } // eden::newsession + + void eden::delsession(const eosio::excluded_arg& auth, + eosio::name eden_account, + const eosio::public_key& key) + { + auth.value.require_auth(eden_account); + sessions_table_type table(get_self(), default_scope); + auto sc = table.find(eden_account.value); + eosio::check(sc != table.end(), "User has no session keys"); + bool empty = false; + table.modify(sc, get_self(), [&](auto& sc) { + auto& sessions = sc.sessions(); + auto session = std::find_if(sessions.begin(), sessions.end(), + [&](auto& session) { return session.key == key; }); + eosio::check(session != sessions.end(), "Session key is either expired or not found"); + sessions.erase(session); + expire(sc); // also sets earliest_expiration + empty = sessions.empty(); + }); + if (empty) + table.erase(sc); + } // eden::delsession + void eden::runactions(const eosio::signature& signature, eosio::ignore, eosio::ignore, @@ -51,6 +118,8 @@ namespace eden eosio::check(false, "sequence " + std::to_string(sequence.value) + " skips too many"); } + else if (sequence.value > 10) + eosio::check(false, "sequence " + std::to_string(sequence.value) + " skips too many"); auto it = std::lower_bound(sequences.begin(), sequences.end(), sequence); if (it != sequences.end() && *it == sequence) eosio::check(false, "received duplicate sequence " + std::to_string(sequence.value)); @@ -73,7 +142,6 @@ namespace eden "unsupported action index"); } - eosio::check(!ds.remaining(), "detected extra action data after post"); - + eosio::check(!ds.remaining(), "detected extra action data"); } // eden::runactions } // namespace eden From c390977ed20834f78f208eb3eaaad580c4b1246f Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Sat, 6 Nov 2021 14:39:04 -0400 Subject: [PATCH 09/54] contract auth --- contracts/eden/include/eden_dispatcher.hpp | 17 +++ contracts/eden/src/eden-micro-chain.cpp | 119 ++++++++++++++------- 2 files changed, 95 insertions(+), 41 deletions(-) diff --git a/contracts/eden/include/eden_dispatcher.hpp b/contracts/eden/include/eden_dispatcher.hpp index 01f779a4f..02c2011c1 100644 --- a/contracts/eden/include/eden_dispatcher.hpp +++ b/contracts/eden/include/eden_dispatcher.hpp @@ -48,6 +48,15 @@ namespace eden #define EDEN_GET_AUTH_ACTION(type, MEMBERS) \ BOOST_PP_SEQ_FOR_EACH(EDEN_GET_AUTH_ACTION_INTERNAL, type, MEMBERS) +#define EDEN_NAME_FOR_AUTH_ACTION_INTERNAL_1(r, type, member) \ + case EDEN_EXTRACT_AUTH_ACTION_INDEX(member): \ + return BOOST_PP_CAT(BOOST_PP_STRINGIZE(EOSIO_EXTRACT_ACTION_NAME(member)), _n); +#define EDEN_NAME_FOR_AUTH_ACTION_INTERNAL(r, type, member) \ + BOOST_PP_IIF(EDEN_MATCH_AUTH_ACTION(member), EDEN_NAME_FOR_AUTH_ACTION_INTERNAL_1, EOSIO_EMPTY) \ + (r, type, member) +#define EDEN_NAME_FOR_AUTH_ACTION(type, MEMBERS) \ + BOOST_PP_SEQ_FOR_EACH(EDEN_NAME_FOR_AUTH_ACTION_INTERNAL, type, MEMBERS) + #define EDEN_ACTIONS(CONTRACT_CLASS, CONTRACT_ACCOUNT, ...) \ EOSIO_ACTIONS(CONTRACT_CLASS, CONTRACT_ACCOUNT, __VA_ARGS__) \ namespace actions \ @@ -68,4 +77,12 @@ namespace eden { \ EDEN_GET_AUTH_ACTION(CONTRACT_CLASS, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \ } \ + inline eosio::name get_name_for_auth_action(uint32_t index) \ + { \ + switch (index) \ + { \ + EDEN_NAME_FOR_AUTH_ACTION(CONTRACT_CLASS, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \ + } \ + return {}; \ + } \ } diff --git a/contracts/eden/src/eden-micro-chain.cpp b/contracts/eden/src/eden-micro-chain.cpp index bdd8e2750..7b24c9e94 100644 --- a/contracts/eden/src/eden-micro-chain.cpp +++ b/contracts/eden/src/eden-micro-chain.cpp @@ -1723,10 +1723,9 @@ void handle_event(const action_context& context, const eden::event& event) } template -void call(void (*f)(Args...), const action_context& context, const std::vector& data) +void call(void (*f)(Args...), const action_context& context, eosio::input_stream& s) { std::tuple...> t; - eosio::input_stream s(data); // TODO: prevent abort, indicate what failed eosio::from_bin(t, s); std::apply([f](auto&&... args) { f(std::move(args)...); }, t); @@ -1735,15 +1734,81 @@ void call(void (*f)(Args...), const action_context& context, const std::vector void call(void (*f)(const action_context&, Args...), const action_context& context, - const std::vector& data) + eosio::input_stream& s) { std::tuple...> t; - eosio::input_stream s(data); // TODO: prevent abort, indicate what failed eosio::from_bin(t, s); std::apply([&](auto&&... args) { f(context, std::move(args)...); }, t); } +bool dispatch(eosio::name action_name, const action_context& context, eosio::input_stream& s); + +void runactions(const action_context& context, eosio::input_stream& s) +{ + eosio::signature signature; + eosio::name eden_account; + eosio::varuint32 sequence; + eosio::varuint32 num_actions; + from_bin(signature, s); + from_bin(eden_account, s); + from_bin(sequence, s); + from_bin(num_actions, s); + for (uint32_t i = 0; i < num_actions.value; ++i) + { + auto index = eosio::varuint32_from_bin(s); + auto name = eden::actions::get_name_for_auth_action(index); + if (!dispatch(name, context, s)) + // fatal because this throws off the rest of the stream + eosio::check(false, "runactions dispatch failed for " + std::to_string(index) + " " + + name.to_string()); + } + eosio::check(!s.remaining(), "unpack error (extra data) within runactions"); +} + +bool dispatch(eosio::name action_name, const action_context& context, eosio::input_stream& s) +{ + if (action_name == "runactions"_n) + runactions(context, s); + else if (action_name == "clearall"_n) + call(clearall, context, s); + else if (action_name == "withdraw"_n) + call(withdraw, context, s); + else if (action_name == "donate"_n) + call(donate, context, s); + else if (action_name == "transfer"_n) + call(transfer, context, s); + else if (action_name == "fundtransfer"_n) + call(fundtransfer, context, s); + else if (action_name == "usertransfer"_n) + call(usertransfer, context, s); + else if (action_name == "genesis"_n) + call(genesis, context, s); + else if (action_name == "addtogenesis"_n) + call(addtogenesis, context, s); + else if (action_name == "inductinit"_n) + call(inductinit, context, s); + else if (action_name == "inductprofil"_n) + call(inductprofil, context, s); + else if (action_name == "inductvideo"_n) + call(inductvideo, context, s); + else if (action_name == "inductcancel"_n) + call(inductcancel, context, s); + else if (action_name == "inductdonate"_n) + call(inductdonate, context, s); + else if (action_name == "resign"_n) + call(resign, context, s); + else if (action_name == "electopt"_n) + call(electopt, context, s); + else if (action_name == "electvote"_n) + call(electvote, context, s); + else if (action_name == "electvideo"_n) + call(electvideo, context, s); + else + return false; + return true; +} + void filter_block(const subchain::eosio_block& block) { block_state block_state{}; @@ -1754,44 +1819,15 @@ void filter_block(const subchain::eosio_block& block) action_context context{block, block_state, trx, action}; if (action.firstReceiver == eden_account) { - if (action.name == "clearall"_n) - call(clearall, context, action.hexData.data); - else if (action.name == "withdraw"_n) - call(withdraw, context, action.hexData.data); - else if (action.name == "donate"_n) - call(donate, context, action.hexData.data); - else if (action.name == "transfer"_n) - call(transfer, context, action.hexData.data); - else if (action.name == "fundtransfer"_n) - call(fundtransfer, context, action.hexData.data); - else if (action.name == "usertransfer"_n) - call(usertransfer, context, action.hexData.data); - else if (action.name == "genesis"_n) - call(genesis, context, action.hexData.data); - else if (action.name == "addtogenesis"_n) - call(addtogenesis, context, action.hexData.data); - else if (action.name == "inductinit"_n) - call(inductinit, context, action.hexData.data); - else if (action.name == "inductprofil"_n) - call(inductprofil, context, action.hexData.data); - else if (action.name == "inductvideo"_n) - call(inductvideo, context, action.hexData.data); - else if (action.name == "inductcancel"_n) - call(inductcancel, context, action.hexData.data); - else if (action.name == "inductdonate"_n) - call(inductdonate, context, action.hexData.data); - else if (action.name == "resign"_n) - call(resign, context, action.hexData.data); - else if (action.name == "electopt"_n) - call(electopt, context, action.hexData.data); - else if (action.name == "electvote"_n) - call(electvote, context, action.hexData.data); - else if (action.name == "electvideo"_n) - call(electvideo, context, action.hexData.data); + eosio::input_stream s(action.hexData.data); + dispatch(action.name, context, s); } else if (action.firstReceiver == token_account && action.receiver == eden_account && action.name == "transfer"_n) - call(notify_transfer, context, action.hexData.data); + { + eosio::input_stream s(action.hexData.data); + call(notify_transfer, context, s); + } else if (action.firstReceiver == "eosio.null"_n && action.name == "eden.events"_n && action.creatorAction && action.creatorAction->receiver == eden_account) { @@ -1802,10 +1838,11 @@ void filter_block(const subchain::eosio_block& block) } else if (action.firstReceiver == atomic_account && action.receiver == eden_account) { + eosio::input_stream s(action.hexData.data); if (action.name == "logmint"_n) - call(logmint, context, action.hexData.data); + call(logmint, context, s); else if (action.name == "logtransfer"_n) - call(logtransfer, context, action.hexData.data); + call(logtransfer, context, s); } } // for(action) eosio::check(!block_state.in_withdraw && !block_state.in_manual_transfer, From 108cd32af05f8582d6a5ceed7a065c8253216073 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Tue, 9 Nov 2021 12:49:43 -0500 Subject: [PATCH 10/54] contract auth --- contracts/eden/include/eden_dispatcher.hpp | 15 ++++ contracts/eden/src/actions/contract_auth.cpp | 2 +- contracts/eden/tests/include/tester-base.hpp | 86 ++++++++++++++++++++ contracts/eden/tests/test-eden.cpp | 42 ++++++++++ 4 files changed, 144 insertions(+), 1 deletion(-) diff --git a/contracts/eden/include/eden_dispatcher.hpp b/contracts/eden/include/eden_dispatcher.hpp index 02c2011c1..46dfcfa00 100644 --- a/contracts/eden/include/eden_dispatcher.hpp +++ b/contracts/eden/include/eden_dispatcher.hpp @@ -57,6 +57,16 @@ namespace eden #define EDEN_NAME_FOR_AUTH_ACTION(type, MEMBERS) \ BOOST_PP_SEQ_FOR_EACH(EDEN_NAME_FOR_AUTH_ACTION_INTERNAL, type, MEMBERS) +#define EDEN_INDEX_FOR_AUTH_ACTION_INTERNAL_1(r, type, member) \ + if (name == BOOST_PP_CAT(BOOST_PP_STRINGIZE(EOSIO_EXTRACT_ACTION_NAME(member)), _n)) \ + return EDEN_EXTRACT_AUTH_ACTION_INDEX(member); +#define EDEN_INDEX_FOR_AUTH_ACTION_INTERNAL(r, type, member) \ + BOOST_PP_IIF(EDEN_MATCH_AUTH_ACTION(member), EDEN_INDEX_FOR_AUTH_ACTION_INTERNAL_1, \ + EOSIO_EMPTY) \ + (r, type, member) +#define EDEN_INDEX_FOR_AUTH_ACTION(type, MEMBERS) \ + BOOST_PP_SEQ_FOR_EACH(EDEN_INDEX_FOR_AUTH_ACTION_INTERNAL, type, MEMBERS) + #define EDEN_ACTIONS(CONTRACT_CLASS, CONTRACT_ACCOUNT, ...) \ EOSIO_ACTIONS(CONTRACT_CLASS, CONTRACT_ACCOUNT, __VA_ARGS__) \ namespace actions \ @@ -85,4 +95,9 @@ namespace eden } \ return {}; \ } \ + inline std::optional get_index_for_auth_action(eosio::name name) \ + { \ + EDEN_INDEX_FOR_AUTH_ACTION(CONTRACT_CLASS, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \ + return {}; \ + } \ } diff --git a/contracts/eden/src/actions/contract_auth.cpp b/contracts/eden/src/actions/contract_auth.cpp index d7a18189f..eab4e8e1a 100644 --- a/contracts/eden/src/actions/contract_auth.cpp +++ b/contracts/eden/src/actions/contract_auth.cpp @@ -68,7 +68,7 @@ namespace eden auth.value.require_auth(eden_account); sessions_table_type table(get_self(), default_scope); auto sc = table.find(eden_account.value); - eosio::check(sc != table.end(), "User has no session keys"); + eosio::check(sc != table.end(), "Session key is either expired or not found"); bool empty = false; table.modify(sc, get_self(), [&](auto& sc) { auto& sessions = sc.sessions(); diff --git a/contracts/eden/tests/include/tester-base.hpp b/contracts/eden/tests/include/tester-base.hpp index 95a4f85ed..dad959c37 100644 --- a/contracts/eden/tests/include/tester-base.hpp +++ b/contracts/eden/tests/include/tester-base.hpp @@ -28,6 +28,26 @@ using user_context = test_chain::user_context; using eden::accounts; using eden::members; +inline const eosio::private_key alice_session_1_priv_key = + private_key_from_string("5KdMjZ6vrbQWromznw5v7WLt4q92abv8sKgRKzagpj8SHacnozX"); +inline const eosio::public_key alice_session_1_pub_key = + public_key_from_string("EOS665ajq1JUMwWH3bHcRxxTqiZBZBc6CakwUfLkZJxRqp4vyzqsQ"); + +inline const eosio::private_key alice_session_2_priv_key = + private_key_from_string("5KKnoRi3WfLL82sS4WdP8YXmezVR24Y8jxy5JXzwC2SouqoHgu2"); +inline const eosio::public_key alice_session_2_pub_key = + public_key_from_string("EOS8VWTR1mogYHEd9HJxgG2Tj3GbPghrnJqMfWfdHbTE11BJmyLRR"); + +inline const eosio::private_key pip_session_1_priv_key = + private_key_from_string("5KZLNGfDrqPM1yVL5zPXMhbAHBSi6ZtU2seqeUdEfudPgv9n93h"); +inline const eosio::public_key pip_session_1_pub_key = + public_key_from_string("EOS8YQhKe3x1xTA1KHmkBPznWqa3UGQsaHTUMkJJtcds9giK4Erft"); + +inline const eosio::private_key pip_session_2_priv_key = + private_key_from_string("5Jr4bSzJWhtr3bxY83xRDhUTgir9Mhn6YwVt4Y9SRgu1GopZ5vA"); +inline const eosio::public_key pip_session_2_pub_key = + public_key_from_string("EOS5iALbhfqEZvqkUifUGbfMQSFnd1ui8ZsXVHT23XWh1HLyyPrJE"); + namespace eosio { std::ostream& operator<<(std::ostream& os, @@ -202,6 +222,7 @@ struct eden_tester test_chain chain; user_context eosio_token = chain.as("eosio.token"_n); user_context eden_gm = chain.as("eden.gm"_n); + user_context payer = chain.as("payer"_n); user_context alice = chain.as("alice"_n); user_context pip = chain.as("pip"_n); user_context egeon = chain.as("egeon"_n); @@ -215,6 +236,7 @@ struct eden_tester chain.create_code_account("eden.gm"_n); f(); eden_setup(chain); + chain.create_account("payer"_n); for (auto account : {"alice"_n, "pip"_n, "egeon"_n, "bertie"_n, "ahab"_n}) { chain.create_account(account); @@ -497,6 +519,58 @@ struct eden_tester return result; }; + void newsession(eosio::name authorizer, + eosio::name eden_account, + const eosio::public_key& key, + eosio::block_timestamp expiration, + const std::string& description, + const char* expected = nullptr) + { + expect(chain.as(authorizer) + .trace(eden_account, key, expiration, description), + expected); + } + + void delsession(eosio::name authorizer, + eosio::name eden_account, + const eosio::public_key& key, + const char* expected = nullptr) + { + expect(chain.as(authorizer).trace(eden_account, key), expected); + } + + template + void runactions(const private_key& key, + eosio::name eden_account, + eosio::varuint32 sequence, + const char* expected, + const Ts&... actions) + { + std::vector data; + vector_stream s{data}; + to_bin(eden_account, s); + to_bin(sequence, s); + to_bin(eosio::varuint32(sizeof...(actions)), s); + for (const auto& a : {actions...}) + data.insert(data.end(), a.begin(), a.end()); + + auto digest = eosio::sha256(data.data(), data.size()); + auto signature = eosio::sign(key, digest); + auto sig_bin = eosio::convert_to_bin(signature); + data.insert(data.begin(), sig_bin.begin(), sig_bin.end()); + + eosio::action act; + act.account = "eden.gm"_n; + act.name = "runactions"_n; + act.authorization.push_back({"payer"_n, "active"_n}); + act.data = std::move(data); + + // printf("created runactions with data: %s\n", + // eosio::hex(act.data.begin(), act.data.end()).c_str()); + + expect(chain.push_transaction(chain.make_transaction({act})), expected); + } + void write_dfuse_history(const char* filename) { chain.start_block(); @@ -504,3 +578,15 @@ struct eden_tester dfuse_subchain::write_history(filename, chain); } }; + +template +std::vector auth_act(Ts&&... args) +{ + auto index = actions::get_index_for_auth_action(ActionWrapper::action_name); + eosio::check(index.has_value(), "action index not found"); + auto data = eosio::convert_to_bin(eosio::varuint32(*index)); + auto act = ActionWrapper{""_n}.to_action(std::forward(args)...); + data.insert(data.end(), act.data.begin(), act.data.end()); + // eosio::print("auth_act: ", eosio::hex(data.begin(), data.end()), "\n"); + return data; +} diff --git a/contracts/eden/tests/test-eden.cpp b/contracts/eden/tests/test-eden.cpp index c51648cd4..8cf1a7002 100644 --- a/contracts/eden/tests/test-eden.cpp +++ b/contracts/eden/tests/test-eden.cpp @@ -1235,3 +1235,45 @@ TEST_CASE("election-events") t.write_dfuse_history("dfuse-test-election.json"); CompareFile{"test-election"}.write_events(t.chain).compare(); } + +TEST_CASE("contract-auth") +{ + eden_tester t; + t.genesis(); + + t.newsession("pip"_n, "alice"_n, alice_session_1_pub_key, + t.chain.get_head_block_info().timestamp.to_time_point() + eosio::days(90), + "no, pip, no", "missing authority of alice"); + t.newsession("alice"_n, "alice"_n, alice_session_1_pub_key, + t.chain.get_head_block_info().timestamp.to_time_point(), "my first session", + "session is expired"); + t.newsession("alice"_n, "alice"_n, alice_session_1_pub_key, + t.chain.get_head_block_info().timestamp.to_time_point() + eosio::days(91), + "my first session", "expiration is too far in the future"); + t.newsession("alice"_n, "alice"_n, alice_session_1_pub_key, + t.chain.get_head_block_info().timestamp.to_time_point() + eosio::days(90), + "four score and twenty", "description is too long"); + + t.newsession("alice"_n, "alice"_n, alice_session_1_pub_key, + t.chain.get_head_block_info().timestamp.to_time_point() + eosio::days(90), + "four score and seven"); + t.newsession("alice"_n, "alice"_n, alice_session_2_pub_key, + t.chain.get_head_block_info().timestamp.to_time_point() + eosio::days(90), + "another session"); + + t.delsession("pip"_n, "alice"_n, alice_session_1_pub_key, "missing authority of alice"); + t.delsession("alice"_n, "alice"_n, alice_session_1_pub_key); + t.chain.start_block(); + t.delsession("alice"_n, "alice"_n, alice_session_1_pub_key, + "Session key is either expired or not found"); + + t.runactions(alice_session_1_priv_key, "alice"_n, 1, + "Recovered session key PUB_K1_665ajq1JUMwWH3bHcRxxTqiZBZBc6CakwUfLkZJxRqp4vAtJaV " + "is either expired or not found", + auth_act("alice"_n, pip_session_1_pub_key)); + t.runactions(alice_session_2_priv_key, "alice"_n, 1, + "Session key is either expired or not found", + auth_act("alice"_n, alice_session_1_pub_key)); + t.runactions(alice_session_2_priv_key, "alice"_n, 1, nullptr, + auth_act("alice"_n, alice_session_2_pub_key)); +} From 37041ba9ef53d4ea40fa2ae1791abf55da10246e Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Tue, 9 Nov 2021 17:06:11 -0500 Subject: [PATCH 11/54] contract auth --- contracts/eden/include/contract_auth.hpp | 6 +- contracts/eden/src/actions/contract_auth.cpp | 6 +- contracts/eden/tests/include/tester-base.hpp | 26 +--- contracts/eden/tests/test-eden.cpp | 121 +++++++++++++++++-- 4 files changed, 125 insertions(+), 34 deletions(-) diff --git a/contracts/eden/include/contract_auth.hpp b/contracts/eden/include/contract_auth.hpp index 19508935c..283b2d8bb 100644 --- a/contracts/eden/include/contract_auth.hpp +++ b/contracts/eden/include/contract_auth.hpp @@ -47,8 +47,12 @@ namespace eden eosio::name authorized_eden_account; void require_auth(eosio::name eden_account) const { - if (eden_account != authorized_eden_account) + if (!authorized_eden_account.value) eosio::require_auth(eden_account); + else if (eden_account != authorized_eden_account) + eosio::check(false, "need authority of " + eden_account.to_string() + + " but have authority of " + + authorized_eden_account.to_string()); } }; } // namespace eden diff --git a/contracts/eden/src/actions/contract_auth.cpp b/contracts/eden/src/actions/contract_auth.cpp index eab4e8e1a..e03332a5e 100644 --- a/contracts/eden/src/actions/contract_auth.cpp +++ b/contracts/eden/src/actions/contract_auth.cpp @@ -1,4 +1,5 @@ #include +#include namespace eden { @@ -27,6 +28,7 @@ namespace eden eosio::check(expiration <= eosio::current_block_time().to_time_point() + eosio::days(90), "expiration is too far in the future"); eosio::check(description.size() <= 20, "description is too long"); + members(get_self()).get_member(eden_account); sessions_table_type table(get_self(), default_scope); auto sc = table.find(eden_account.value); @@ -99,7 +101,9 @@ namespace eden sessions_table_type table(get_self(), default_scope); auto sc = table.find(eden_account.value); - eosio::check(sc != table.end(), "User has no session keys"); + if (sc == table.end()) + eosio::check(false, "Recovered session key " + public_key_to_string(recovered) + + " is either expired or not found"); table.modify(sc, get_self(), [&](auto& sc) { expire(sc); auto& sessions = sc.sessions(); diff --git a/contracts/eden/tests/include/tester-base.hpp b/contracts/eden/tests/include/tester-base.hpp index dad959c37..028ae5662 100644 --- a/contracts/eden/tests/include/tester-base.hpp +++ b/contracts/eden/tests/include/tester-base.hpp @@ -28,26 +28,6 @@ using user_context = test_chain::user_context; using eden::accounts; using eden::members; -inline const eosio::private_key alice_session_1_priv_key = - private_key_from_string("5KdMjZ6vrbQWromznw5v7WLt4q92abv8sKgRKzagpj8SHacnozX"); -inline const eosio::public_key alice_session_1_pub_key = - public_key_from_string("EOS665ajq1JUMwWH3bHcRxxTqiZBZBc6CakwUfLkZJxRqp4vyzqsQ"); - -inline const eosio::private_key alice_session_2_priv_key = - private_key_from_string("5KKnoRi3WfLL82sS4WdP8YXmezVR24Y8jxy5JXzwC2SouqoHgu2"); -inline const eosio::public_key alice_session_2_pub_key = - public_key_from_string("EOS8VWTR1mogYHEd9HJxgG2Tj3GbPghrnJqMfWfdHbTE11BJmyLRR"); - -inline const eosio::private_key pip_session_1_priv_key = - private_key_from_string("5KZLNGfDrqPM1yVL5zPXMhbAHBSi6ZtU2seqeUdEfudPgv9n93h"); -inline const eosio::public_key pip_session_1_pub_key = - public_key_from_string("EOS8YQhKe3x1xTA1KHmkBPznWqa3UGQsaHTUMkJJtcds9giK4Erft"); - -inline const eosio::private_key pip_session_2_priv_key = - private_key_from_string("5Jr4bSzJWhtr3bxY83xRDhUTgir9Mhn6YwVt4Y9SRgu1GopZ5vA"); -inline const eosio::public_key pip_session_2_pub_key = - public_key_from_string("EOS5iALbhfqEZvqkUifUGbfMQSFnd1ui8ZsXVHT23XWh1HLyyPrJE"); - namespace eosio { std::ostream& operator<<(std::ostream& os, @@ -279,6 +259,12 @@ struct eden_tester } } + auto hash_induction(const std::string& video, const eden::new_member_profile& profile) + { + auto hash_data = eosio::convert_to_bin(std::tuple(video, profile)); + return eosio::sha256(hash_data.data(), hash_data.size()); + } + void finish_induction(uint64_t induction_id, eosio::name inviter, eosio::name invitee, diff --git a/contracts/eden/tests/test-eden.cpp b/contracts/eden/tests/test-eden.cpp index 8cf1a7002..7bf028057 100644 --- a/contracts/eden/tests/test-eden.cpp +++ b/contracts/eden/tests/test-eden.cpp @@ -4,6 +4,31 @@ bool write_expected = false; +const eosio::private_key alice_session_priv_key = + private_key_from_string("5KdMjZ6vrbQWromznw5v7WLt4q92abv8sKgRKzagpj8SHacnozX"); +const eosio::public_key alice_session_pub_key = + public_key_from_string("EOS665ajq1JUMwWH3bHcRxxTqiZBZBc6CakwUfLkZJxRqp4vyzqsQ"); + +const eosio::private_key alice_session_2_priv_key = + private_key_from_string("5KKnoRi3WfLL82sS4WdP8YXmezVR24Y8jxy5JXzwC2SouqoHgu2"); +const eosio::public_key alice_session_2_pub_key = + public_key_from_string("EOS8VWTR1mogYHEd9HJxgG2Tj3GbPghrnJqMfWfdHbTE11BJmyLRR"); + +const eosio::private_key pip_session_priv_key = + private_key_from_string("5KZLNGfDrqPM1yVL5zPXMhbAHBSi6ZtU2seqeUdEfudPgv9n93h"); +const eosio::public_key pip_session_pub_key = + public_key_from_string("EOS8YQhKe3x1xTA1KHmkBPznWqa3UGQsaHTUMkJJtcds9giK4Erft"); + +const eosio::private_key egeon_session_priv_key = + private_key_from_string("5Jk9RLHvhSgN8h7VjRGdY91GpeoXs5qP7JnizReg4DXBqtbGM8y"); +const eosio::public_key egeon_session_pub_key = + public_key_from_string("EOS8kBx4XYj3zZ3Z1Sb8vdq43ursVTebfcShKMDUymiA2ctcznX71"); + +const eosio::private_key bertie_session_priv_key = + private_key_from_string("5Jr4bSzJWhtr3bxY83xRDhUTgir9Mhn6YwVt4Y9SRgu1GopZ5vA"); +const eosio::public_key bertie_session_pub_key = + public_key_from_string("EOS5iALbhfqEZvqkUifUGbfMQSFnd1ui8ZsXVHT23XWh1HLyyPrJE"); + int main(int argc, char* argv[]) { Catch::Session session; @@ -1241,39 +1266,111 @@ TEST_CASE("contract-auth") eden_tester t; t.genesis(); - t.newsession("pip"_n, "alice"_n, alice_session_1_pub_key, + t.newsession("pip"_n, "alice"_n, alice_session_pub_key, t.chain.get_head_block_info().timestamp.to_time_point() + eosio::days(90), "no, pip, no", "missing authority of alice"); - t.newsession("alice"_n, "alice"_n, alice_session_1_pub_key, + t.newsession("alice"_n, "alice"_n, alice_session_pub_key, t.chain.get_head_block_info().timestamp.to_time_point(), "my first session", "session is expired"); - t.newsession("alice"_n, "alice"_n, alice_session_1_pub_key, + t.newsession("alice"_n, "alice"_n, alice_session_pub_key, t.chain.get_head_block_info().timestamp.to_time_point() + eosio::days(91), "my first session", "expiration is too far in the future"); - t.newsession("alice"_n, "alice"_n, alice_session_1_pub_key, + t.newsession("alice"_n, "alice"_n, alice_session_pub_key, t.chain.get_head_block_info().timestamp.to_time_point() + eosio::days(90), "four score and twenty", "description is too long"); - t.newsession("alice"_n, "alice"_n, alice_session_1_pub_key, + t.newsession("alice"_n, "alice"_n, alice_session_pub_key, t.chain.get_head_block_info().timestamp.to_time_point() + eosio::days(90), "four score and seven"); t.newsession("alice"_n, "alice"_n, alice_session_2_pub_key, t.chain.get_head_block_info().timestamp.to_time_point() + eosio::days(90), "another session"); + t.newsession("alice"_n, "alice"_n, alice_session_2_pub_key, + t.chain.get_head_block_info().timestamp.to_time_point() + eosio::days(60), + "another session", "session key already exists"); - t.delsession("pip"_n, "alice"_n, alice_session_1_pub_key, "missing authority of alice"); - t.delsession("alice"_n, "alice"_n, alice_session_1_pub_key); + t.delsession("pip"_n, "alice"_n, alice_session_pub_key, "missing authority of alice"); + t.delsession("alice"_n, "alice"_n, alice_session_pub_key); t.chain.start_block(); - t.delsession("alice"_n, "alice"_n, alice_session_1_pub_key, + t.delsession("alice"_n, "alice"_n, alice_session_pub_key, "Session key is either expired or not found"); - t.runactions(alice_session_1_priv_key, "alice"_n, 1, + t.runactions(alice_session_priv_key, "alice"_n, 1, "Recovered session key PUB_K1_665ajq1JUMwWH3bHcRxxTqiZBZBc6CakwUfLkZJxRqp4vAtJaV " "is either expired or not found", - auth_act("alice"_n, pip_session_1_pub_key)); + auth_act("alice"_n, pip_session_pub_key)); t.runactions(alice_session_2_priv_key, "alice"_n, 1, "Session key is either expired or not found", - auth_act("alice"_n, alice_session_1_pub_key)); + auth_act("alice"_n, alice_session_pub_key)); t.runactions(alice_session_2_priv_key, "alice"_n, 1, nullptr, auth_act("alice"_n, alice_session_2_pub_key)); -} + t.chain.start_block(); + t.runactions(alice_session_2_priv_key, "alice"_n, 1, + "Recovered session key PUB_K1_8VWTR1mogYHEd9HJxgG2Tj3GbPghrnJqMfWfdHbTE11BLxqvo3 " + "is either expired or not found", + auth_act("alice"_n, alice_session_2_pub_key)); +} // TEST_CASE("contract-auth") + +TEST_CASE("contract-auth-induct") +{ + eden_tester t; + t.genesis(); + + t.newsession("alice"_n, "alice"_n, alice_session_pub_key, + t.chain.get_head_block_info().timestamp.to_time_point() + eosio::days(90), ""); + t.newsession("pip"_n, "pip"_n, pip_session_pub_key, + t.chain.get_head_block_info().timestamp.to_time_point() + eosio::days(90), ""); + t.newsession("egeon"_n, "egeon"_n, egeon_session_pub_key, + t.chain.get_head_block_info().timestamp.to_time_point() + eosio::days(90), ""); + + t.newsession("bertie"_n, "bertie"_n, bertie_session_pub_key, + t.chain.get_head_block_info().timestamp.to_time_point() + eosio::days(90), "", + "member bertie not found"); + + t.runactions( + pip_session_priv_key, "pip"_n, 1, "need authority of alice but have authority of pip", + auth_act(1234, "alice"_n, "bertie"_n, std::vector{"pip"_n, "egeon"_n})); + t.runactions( + alice_session_priv_key, "alice"_n, 1, nullptr, + auth_act(1234, "alice"_n, "bertie"_n, std::vector{"pip"_n, "egeon"_n})); + t.newsession("bertie"_n, "bertie"_n, bertie_session_pub_key, + t.chain.get_head_block_info().timestamp.to_time_point() + eosio::days(90), ""); + t.runactions(alice_session_priv_key, "alice"_n, 2, + "need authority of bertie but have authority of alice", + auth_act(1234, bertie_profile)); + t.runactions(bertie_session_priv_key, "bertie"_n, 1, + "Video can only be set by inviter or a witness", + auth_act(1234, bertie_profile), + auth_act("bertie"_n, 1234, "vid"s)); + t.runactions(bertie_session_priv_key, "bertie"_n, 1, + "need authority of pip but have authority of bertie", + auth_act(1234, bertie_profile), + auth_act("pip"_n, 1234, "vid"s)); + t.runactions( + bertie_session_priv_key, "bertie"_n, 1, + "Induction can only be endorsed by inviter or a witness", + auth_act(1234, bertie_profile), + auth_act("bertie"_n, 1234, t.hash_induction("vid"s, bertie_profile))); + t.runactions( + bertie_session_priv_key, "bertie"_n, 1, "need authority of pip but have authority of bertie", + auth_act(1234, bertie_profile), + auth_act("pip"_n, 1234, t.hash_induction("vid"s, bertie_profile))); + t.runactions(bertie_session_priv_key, "bertie"_n, 1, nullptr, + auth_act(1234, bertie_profile)); + + t.runactions( + pip_session_priv_key, "pip"_n, 2, "need authority of alice but have authority of pip", + auth_act("alice"_n, 1234, std::vector(4), + eosio::bytes{}, std::nullopt)); + t.runactions(pip_session_priv_key, "pip"_n, 2, nullptr, + auth_act("pip"_n, 1234, std::vector(0), + eosio::bytes{}, std::nullopt)); + + t.runactions( + pip_session_priv_key, "pip"_n, 3, nullptr, + auth_act("pip"_n, 1234, "vid"s), + auth_act("pip"_n, 1234, t.hash_induction("vid"s, bertie_profile))); + t.runactions( + alice_session_priv_key, "alice"_n, 3, nullptr, + auth_act("alice"_n, 1234, t.hash_induction("vid"s, bertie_profile))); +} // TEST_CASE("contract-auth-induct") From 520a764d2bf14a7a3c6666b5896fd55233b0703d Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Tue, 9 Nov 2021 18:36:31 -0500 Subject: [PATCH 12/54] contract auth --- contracts/eden/include/contract_auth.hpp | 2 + contracts/eden/src/actions/contract_auth.cpp | 16 +++++ contracts/eden/src/actions/induct.cpp | 1 + contracts/eden/src/elections.cpp | 1 - contracts/eden/tests/test-eden.cpp | 61 ++++++++++++++++++++ 5 files changed, 80 insertions(+), 1 deletion(-) diff --git a/contracts/eden/include/contract_auth.hpp b/contracts/eden/include/contract_auth.hpp index 283b2d8bb..206a9b0cc 100644 --- a/contracts/eden/include/contract_auth.hpp +++ b/contracts/eden/include/contract_auth.hpp @@ -42,6 +42,8 @@ namespace eden "byexpiration"_n, eosio::const_mem_fun>>; + uint32_t gc_sessions(eosio::name contract, uint32_t remaining); + struct auth_info { eosio::name authorized_eden_account; diff --git a/contracts/eden/src/actions/contract_auth.cpp b/contracts/eden/src/actions/contract_auth.cpp index e03332a5e..8e7d98901 100644 --- a/contracts/eden/src/actions/contract_auth.cpp +++ b/contracts/eden/src/actions/contract_auth.cpp @@ -17,6 +17,22 @@ namespace eden sc.earliest_expiration() = expiration; } + uint32_t gc_sessions(eosio::name contract, uint32_t remaining) + { + auto now = eosio::current_block_time(); + sessions_table_type table(contract, default_scope); + auto idx = table.get_index<"byexpiration"_n>(); + while (remaining && idx.begin() != idx.end() && idx.begin()->earliest_expiration() <= now) + { + auto& sc = *idx.begin(); + table.modify(sc, contract, [&](auto& sc) { expire(sc); }); + if (sc.sessions().empty()) + table.erase(sc); + --remaining; + } + return remaining; + } + void eden::newsession(eosio::name eden_account, const eosio::public_key& key, eosio::block_timestamp expiration, diff --git a/contracts/eden/src/actions/induct.cpp b/contracts/eden/src/actions/induct.cpp index 4801ab10e..822727408 100644 --- a/contracts/eden/src/actions/induct.cpp +++ b/contracts/eden/src/actions/induct.cpp @@ -176,6 +176,7 @@ namespace eden auctions auctions{get_self()}; remaining = auctions.finish_auctions(remaining); } + remaining = gc_sessions(get_self(), remaining); eosio::check(remaining != limit, "Nothing to do."); if (!removed_members.empty()) { diff --git a/contracts/eden/src/elections.cpp b/contracts/eden/src/elections.cpp index 0fac12fd5..abf00c433 100644 --- a/contracts/eden/src/elections.cpp +++ b/contracts/eden/src/elections.cpp @@ -919,7 +919,6 @@ namespace eden void elections::vote(uint8_t round, eosio::name voter, eosio::name candidate) { - eosio::require_auth(voter); const auto& state = check_active(); eosio::check(state.round == round, "Round " + std::to_string(static_cast(round)) + " is not running (in round " + diff --git a/contracts/eden/tests/test-eden.cpp b/contracts/eden/tests/test-eden.cpp index 7bf028057..730d38f23 100644 --- a/contracts/eden/tests/test-eden.cpp +++ b/contracts/eden/tests/test-eden.cpp @@ -1374,3 +1374,64 @@ TEST_CASE("contract-auth-induct") alice_session_priv_key, "alice"_n, 3, nullptr, auth_act("alice"_n, 1234, t.hash_induction("vid"s, bertie_profile))); } // TEST_CASE("contract-auth-induct") + +TEST_CASE("contract-auth-elect") +{ + eden_tester t; + t.genesis(); + t.induct_n(100); + + auto create_sessions = [&] { + t.alice.trace(1000); + t.newsession("alice"_n, "alice"_n, alice_session_pub_key, + t.chain.get_head_block_info().timestamp.to_time_point() + eosio::days(90), ""); + t.newsession("pip"_n, "pip"_n, pip_session_pub_key, + t.chain.get_head_block_info().timestamp.to_time_point() + eosio::days(90), ""); + t.newsession("egeon"_n, "egeon"_n, egeon_session_pub_key, + t.chain.get_head_block_info().timestamp.to_time_point() + eosio::days(90), ""); + }; + + create_sessions(); + t.runactions(pip_session_priv_key, "pip"_n, 1, + "need authority of alice but have authority of pip", + auth_act("alice"_n, true)); + t.runactions(alice_session_priv_key, "alice"_n, 1, nullptr, + auth_act("alice"_n, true)); + t.chain.finish_block(); + t.runactions(alice_session_priv_key, "alice"_n, 2, "Not currently opted out", + auth_act("alice"_n, true)); + + t.electdonate_all(); + t.skip_to(t.next_election_time().to_time_point() - eosio::days(1)); + t.electseed(t.next_election_time().to_time_point() - eosio::days(1)); + t.skip_to(t.next_election_time().to_time_point() + eosio::minutes(10)); + t.setup_election(); + + t.runactions(pip_session_priv_key, "pip"_n, 2, + "Recovered session key PUB_K1_8YQhKe3x1xTA1KHmkBPznWqa3UGQsaHTUMkJJtcds9giKNsHGv " + "is either expired or not found", + auth_act("pip"_n, 0, std::vector(0), + eosio::bytes{}, std::nullopt)); + create_sessions(); + t.runactions(pip_session_priv_key, "pip"_n, 2, + "need authority of alice but have authority of pip", + auth_act("alice"_n, 0, std::vector(0), + eosio::bytes{}, std::nullopt)); + t.runactions(pip_session_priv_key, "pip"_n, 2, nullptr, + auth_act("pip"_n, 0, std::vector(0), + eosio::bytes{}, std::nullopt)); + + t.runactions(pip_session_priv_key, "pip"_n, 3, + "need authority of alice but have authority of pip", + auth_act(0, "alice"_n, "pip"_n)); + t.runactions(alice_session_priv_key, "alice"_n, 0, "alice and pip are not in the same group", + auth_act(0, "alice"_n, "pip"_n)); + + t.runactions(pip_session_priv_key, "pip"_n, 3, + "need authority of alice but have authority of pip", + auth_act(0, "alice"_n, + "Qmb7WmZiSDXss5HfuKfoSf6jxTDrHzr8AoAUDeDMLNDuws")); + t.runactions(alice_session_priv_key, "alice"_n, 1, nullptr, + auth_act(0, "alice"_n, + "Qmb7WmZiSDXss5HfuKfoSf6jxTDrHzr8AoAUDeDMLNDuws")); +} // TEST_CASE("contract-auth-elect") From 4813981caa61851103b7bd99a131decd1db75703 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Wed, 10 Nov 2021 15:15:36 -0500 Subject: [PATCH 13/54] contract auth --- contracts/eden/include/events.hpp | 23 +++++++++++++++++++- contracts/eden/src/actions/contract_auth.cpp | 16 +++++++++----- contracts/eden/tests/test-eden.cpp | 6 +++++ 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/contracts/eden/include/events.hpp b/contracts/eden/include/events.hpp index b317acb1c..89674dfc4 100644 --- a/contracts/eden/include/events.hpp +++ b/contracts/eden/include/events.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -206,6 +207,24 @@ namespace eden }; EOSIO_REFLECT(distribution_event_return, owner, distribution_time, rank, amount, pool) + // Session events + + struct session_new_event + { + eosio::name eden_account; + eosio::public_key key; + eosio::block_timestamp expiration; + std::string description; + }; + EOSIO_REFLECT(session_new_event, eden_account, key, expiration, description) + + struct session_del_event + { + eosio::name eden_account; + eosio::public_key key; + }; + EOSIO_REFLECT(session_del_event, eden_account, key) + using event = std::variant; + distribution_event_return, + session_new_event, + session_del_event>; void push_event(const event& e, eosio::name self); void send_events(eosio::name self); diff --git a/contracts/eden/src/actions/contract_auth.cpp b/contracts/eden/src/actions/contract_auth.cpp index 8e7d98901..6c0caf2fa 100644 --- a/contracts/eden/src/actions/contract_auth.cpp +++ b/contracts/eden/src/actions/contract_auth.cpp @@ -1,12 +1,16 @@ #include +#include #include namespace eden { - void expire(session_container& sc) + void expire(eosio::name contract, session_container& sc) { auto now = eosio::current_block_time(); auto& sessions = sc.sessions(); + for (auto& session : sessions) + if (session.expiration <= now) + push_event(session_del_event{sc.eden_account(), session.key}, contract); auto new_end = std::remove_if(sessions.begin(), sessions.end(), [&](auto& session) { return session.expiration <= now; }); sessions.erase(new_end, sessions.end()); @@ -25,7 +29,7 @@ namespace eden while (remaining && idx.begin() != idx.end() && idx.begin()->earliest_expiration() <= now) { auto& sc = *idx.begin(); - table.modify(sc, contract, [&](auto& sc) { expire(sc); }); + table.modify(sc, contract, [&](auto& sc) { expire(contract, sc); }); if (sc.sessions().empty()) table.erase(sc); --remaining; @@ -74,9 +78,10 @@ namespace eden }); if (sessions.size() > 4) sessions.erase(sessions.begin()); - expire(sc); // also sets earliest_expiration + expire(get_self(), sc); // also sets earliest_expiration }); } + push_event(session_new_event{eden_account, key, expiration, description}, get_self()); } // eden::newsession void eden::delsession(const eosio::excluded_arg& auth, @@ -93,8 +98,9 @@ namespace eden auto session = std::find_if(sessions.begin(), sessions.end(), [&](auto& session) { return session.key == key; }); eosio::check(session != sessions.end(), "Session key is either expired or not found"); + push_event(session_del_event{sc.eden_account(), session->key}, get_self()); sessions.erase(session); - expire(sc); // also sets earliest_expiration + expire(get_self(), sc); // also sets earliest_expiration empty = sessions.empty(); }); if (empty) @@ -121,7 +127,7 @@ namespace eden eosio::check(false, "Recovered session key " + public_key_to_string(recovered) + " is either expired or not found"); table.modify(sc, get_self(), [&](auto& sc) { - expire(sc); + expire(get_self(), sc); auto& sessions = sc.sessions(); auto session = std::find_if(sessions.begin(), sessions.end(), [&](auto& session) { return session.key == recovered; }); diff --git a/contracts/eden/tests/test-eden.cpp b/contracts/eden/tests/test-eden.cpp index 730d38f23..d25b4e275 100644 --- a/contracts/eden/tests/test-eden.cpp +++ b/contracts/eden/tests/test-eden.cpp @@ -1373,6 +1373,12 @@ TEST_CASE("contract-auth-induct") t.runactions( alice_session_priv_key, "alice"_n, 3, nullptr, auth_act("alice"_n, 1234, t.hash_induction("vid"s, bertie_profile))); + + t.runactions(pip_session_priv_key, "pip"_n, 4, + "need authority of alice but have authority of pip", + auth_act("alice"_n, 1234)); + t.runactions(pip_session_priv_key, "pip"_n, 4, nullptr, + auth_act("pip"_n, 1234)); } // TEST_CASE("contract-auth-induct") TEST_CASE("contract-auth-elect") From 97a1508694d8313f43fe030d3bf85e2d25bc39ca Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Wed, 10 Nov 2021 15:38:33 -0500 Subject: [PATCH 14/54] cleanup --- contracts/eden/CMakeLists.txt | 2 +- contracts/eden/include/eden.hpp | 24 +++++++++---------- contracts/eden/include/eden_dispatcher.hpp | 18 +++++++------- .../{contract_auth.hpp => sessions.hpp} | 2 +- contracts/eden/src/actions/elect.cpp | 16 ++++++------- contracts/eden/src/actions/induct.cpp | 24 +++++++++---------- .../{contract_auth.cpp => sessions.cpp} | 6 ++--- 7 files changed, 47 insertions(+), 45 deletions(-) rename contracts/eden/include/{contract_auth.hpp => sessions.hpp} (98%) rename contracts/eden/src/actions/{contract_auth.cpp => sessions.cpp} (97%) diff --git a/contracts/eden/CMakeLists.txt b/contracts/eden/CMakeLists.txt index 9693c0d2c..624d5f371 100644 --- a/contracts/eden/CMakeLists.txt +++ b/contracts/eden/CMakeLists.txt @@ -17,7 +17,7 @@ add_executable(eden src/actions/migrate.cpp src/actions/encrypt.cpp src/actions/tables.cpp - src/actions/contract_auth.cpp + src/actions/sessions.cpp src/eden.cpp src/events.cpp src/accounts.cpp diff --git a/contracts/eden/include/eden.hpp b/contracts/eden/include/eden.hpp index 03916f864..a70b0bb03 100644 --- a/contracts/eden/include/eden.hpp +++ b/contracts/eden/include/eden.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include #include @@ -9,6 +8,7 @@ #include #include #include +#include #include #include @@ -84,7 +84,7 @@ namespace eden eosio::block_timestamp expiration, const std::string& description); - void delsession(const eosio::excluded_arg& auth, + void delsession(const eosio::excluded_arg& current_session, eosio::name eden_account, const eosio::public_key& key); @@ -116,32 +116,32 @@ namespace eden void clearall(); - void inductinit(const eosio::excluded_arg& auth, + void inductinit(const eosio::excluded_arg& current_session, uint64_t id, eosio::name inviter, eosio::name invitee, std::vector witnesses); - void inductprofil(const eosio::excluded_arg& auth, + void inductprofil(const eosio::excluded_arg& current_session, uint64_t id, new_member_profile new_member_profile); - void inductvideo(const eosio::excluded_arg& auth, + void inductvideo(const eosio::excluded_arg& current_session, eosio::name account, uint64_t id, std::string video); - void inductendors(const eosio::excluded_arg& auth, + void inductendors(const eosio::excluded_arg& current_session, eosio::name account, uint64_t id, eosio::checksum256 induction_data_hash); void inductdonate(eosio::name payer, uint64_t id, const eosio::asset& quantity); - void inductcancel(const eosio::excluded_arg& auth, + void inductcancel(const eosio::excluded_arg& current_session, eosio::name account, uint64_t id); - void inductmeetin(const eosio::excluded_arg& auth, + void inductmeetin(const eosio::excluded_arg& current_session, eosio::name account, uint64_t id, const std::vector& keys, @@ -160,22 +160,22 @@ namespace eden const std::string& election_time, uint32_t round_duration_sec); - void electopt(const eosio::excluded_arg& auth, + void electopt(const eosio::excluded_arg& current_session, eosio::name member, bool participating); void electseed(const eosio::bytes& btc_header); - void electmeeting(const eosio::excluded_arg& auth, + void electmeeting(const eosio::excluded_arg& current_session, eosio::name account, uint8_t round, const std::vector& keys, const eosio::bytes& data, const std::optional& old_data); - void electvote(const eosio::excluded_arg& auth, + void electvote(const eosio::excluded_arg& current_session, uint8_t round, eosio::name voter, eosio::name candidate); - void electvideo(const eosio::excluded_arg& auth, + void electvideo(const eosio::excluded_arg& current_session, uint8_t round, eosio::name voter, const std::string& video); diff --git a/contracts/eden/include/eden_dispatcher.hpp b/contracts/eden/include/eden_dispatcher.hpp index 46dfcfa00..c4e3a57a4 100644 --- a/contracts/eden/include/eden_dispatcher.hpp +++ b/contracts/eden/include/eden_dispatcher.hpp @@ -1,20 +1,21 @@ #pragma once -#include #include +#include namespace eden { template void execute_auth_action(eosio::name contract, - R (T::*func)(const eosio::excluded_arg& auth, Args...), - const eosio::excluded_arg& auth, + R (T::*func)(const eosio::excluded_arg& current_session, + Args...), + const eosio::excluded_arg& current_session, eosio::datastream& ds) { std::tuple...> t; ds >> t; T inst(contract, contract, ds); - std::apply([&](auto&... args) { (inst.*func)(auth, std::move(args)...); }, t); + std::apply([&](auto&... args) { (inst.*func)(current_session, std::move(args)...); }, t); } } // namespace eden @@ -28,9 +29,10 @@ namespace eden #define EDEN_EXTRACT_AUTH_ACTION_INDEX(x) BOOST_PP_CAT(EDEN_EXTRACT_AUTH_ACTION_INDEX, x) #define EDEN_EXTRACT_AUTH_ACTION_INDEXeden_auth_action(name, index, ...) index -#define EDEN_DISPATCH_AUTH_ACTION_INTERNAL_1(r, type, member) \ - case EDEN_EXTRACT_AUTH_ACTION_INDEX(member): \ - ::eden::execute_auth_action(contract, &type::EOSIO_EXTRACT_ACTION_NAME(member), auth, ds); \ +#define EDEN_DISPATCH_AUTH_ACTION_INTERNAL_1(r, type, member) \ + case EDEN_EXTRACT_AUTH_ACTION_INDEX(member): \ + ::eden::execute_auth_action(contract, &type::EOSIO_EXTRACT_ACTION_NAME(member), \ + current_session, ds); \ return true; #define EDEN_DISPATCH_AUTH_ACTION_INTERNAL(r, type, member) \ BOOST_PP_IIF(EDEN_MATCH_AUTH_ACTION(member), EDEN_DISPATCH_AUTH_ACTION_INTERNAL_1, EOSIO_EMPTY) \ @@ -73,7 +75,7 @@ namespace eden { \ inline bool dispatch_auth(eosio::name contract, \ uint32_t index, \ - const eosio::excluded_arg& auth, \ + const eosio::excluded_arg& current_session, \ eosio::datastream& ds) \ { \ switch (index) \ diff --git a/contracts/eden/include/contract_auth.hpp b/contracts/eden/include/sessions.hpp similarity index 98% rename from contracts/eden/include/contract_auth.hpp rename to contracts/eden/include/sessions.hpp index 206a9b0cc..340eff3cd 100644 --- a/contracts/eden/include/contract_auth.hpp +++ b/contracts/eden/include/sessions.hpp @@ -44,7 +44,7 @@ namespace eden uint32_t gc_sessions(eosio::name contract, uint32_t remaining); - struct auth_info + struct session_info { eosio::name authorized_eden_account; void require_auth(eosio::name eden_account) const diff --git a/contracts/eden/src/actions/elect.cpp b/contracts/eden/src/actions/elect.cpp index 69085a0ee..b571f11df 100644 --- a/contracts/eden/src/actions/elect.cpp +++ b/contracts/eden/src/actions/elect.cpp @@ -26,11 +26,11 @@ namespace eden globals.set_election_round_duration(round_duration); } - void eden::electopt(const eosio::excluded_arg& auth, + void eden::electopt(const eosio::excluded_arg& current_session, eosio::name voter, bool participating) { - auth.value.require_auth(voter); + current_session.value.require_auth(voter); members members{get_self()}; const auto& member = members.get_member(voter); @@ -53,14 +53,14 @@ namespace eden elections.seed(btc_header); } - void eden::electmeeting(const eosio::excluded_arg& auth, + void eden::electmeeting(const eosio::excluded_arg& current_session, eosio::name account, uint8_t round, const std::vector& keys, const eosio::bytes& data, const std::optional& old_data) { - auth.value.require_auth(account); + current_session.value.require_auth(account); members members{get_self()}; elections elections{get_self()}; auto group_id = elections.get_group_id(account, round); @@ -69,22 +69,22 @@ namespace eden encrypt.set(group_id, keys, data, old_data); } - void eden::electvote(const eosio::excluded_arg& auth, + void eden::electvote(const eosio::excluded_arg& current_session, uint8_t round, eosio::name voter, eosio::name candidate) { - auth.value.require_auth(voter); + current_session.value.require_auth(voter); elections elections(get_self()); elections.vote(round, voter, candidate); } - void eden::electvideo(const eosio::excluded_arg& auth, + void eden::electvideo(const eosio::excluded_arg& current_session, uint8_t round, eosio::name voter, const std::string& video) { - auth.value.require_auth(voter); + current_session.value.require_auth(voter); elections elections{get_self()}; members members{get_self()}; if (auto check = elections.can_upload_video(round, voter); boost::logic::indeterminate(check)) diff --git a/contracts/eden/src/actions/induct.cpp b/contracts/eden/src/actions/induct.cpp index 822727408..aa008ffd5 100644 --- a/contracts/eden/src/actions/induct.cpp +++ b/contracts/eden/src/actions/induct.cpp @@ -10,13 +10,13 @@ namespace eden { - void eden::inductinit(const eosio::excluded_arg& auth, + void eden::inductinit(const eosio::excluded_arg& current_session, uint64_t id, eosio::name inviter, eosio::name invitee, std::vector witnesses) { - auth.value.require_auth(inviter); + current_session.value.require_auth(inviter); globals{get_self()}.check_active(); @@ -34,14 +34,14 @@ namespace eden inductions{get_self()}.initialize_induction(id, inviter, invitee, witnesses); } - void eden::inductmeetin(const eosio::excluded_arg& auth, + void eden::inductmeetin(const eosio::excluded_arg& current_session, eosio::name account, uint64_t id, const std::vector& keys, const eosio::bytes& data, const std::optional& old_data) { - auth.value.require_auth(account); + current_session.value.require_auth(account); globals{get_self()}.check_active(); members members{get_self()}; @@ -54,13 +54,13 @@ namespace eden encrypt.set(id, keys, data, old_data); } - void eden::inductprofil(const eosio::excluded_arg& auth, + void eden::inductprofil(const eosio::excluded_arg& current_session, uint64_t id, new_member_profile new_member_profile) { inductions inductions{get_self()}; const auto& induction = inductions.get_induction(id); - auth.value.require_auth(induction.invitee()); + current_session.value.require_auth(induction.invitee()); members{get_self()}.check_pending_member(induction.invitee()); @@ -73,12 +73,12 @@ namespace eden } } - void eden::inductvideo(const eosio::excluded_arg& auth, + void eden::inductvideo(const eosio::excluded_arg& current_session, eosio::name account, uint64_t id, std::string video) { - auth.value.require_auth(account); + current_session.value.require_auth(account); inductions inductions{get_self()}; const auto& induction = inductions.get_induction(id); @@ -90,12 +90,12 @@ namespace eden inductions.update_video(induction, video); } - void eden::inductendors(const eosio::excluded_arg& auth, + void eden::inductendors(const eosio::excluded_arg& current_session, eosio::name account, uint64_t id, eosio::checksum256 induction_data_hash) { - auth.value.require_auth(account); + current_session.value.require_auth(account); inductions inductions{get_self()}; const auto& induction = inductions.get_induction(id); @@ -189,11 +189,11 @@ namespace eden } } - void eden::inductcancel(const eosio::excluded_arg& auth, + void eden::inductcancel(const eosio::excluded_arg& current_session, eosio::name account, uint64_t id) { - auth.value.require_auth(account); + current_session.value.require_auth(account); inductions inductions{get_self()}; bool is_genesis = globals{get_self()}.get().stage == contract_stage::genesis; diff --git a/contracts/eden/src/actions/contract_auth.cpp b/contracts/eden/src/actions/sessions.cpp similarity index 97% rename from contracts/eden/src/actions/contract_auth.cpp rename to contracts/eden/src/actions/sessions.cpp index 6c0caf2fa..23c1e06a2 100644 --- a/contracts/eden/src/actions/contract_auth.cpp +++ b/contracts/eden/src/actions/sessions.cpp @@ -84,11 +84,11 @@ namespace eden push_event(session_new_event{eden_account, key, expiration, description}, get_self()); } // eden::newsession - void eden::delsession(const eosio::excluded_arg& auth, + void eden::delsession(const eosio::excluded_arg& current_session, eosio::name eden_account, const eosio::public_key& key) { - auth.value.require_auth(eden_account); + current_session.value.require_auth(eden_account); sessions_table_type table(get_self(), default_scope); auto sc = table.find(eden_account.value); eosio::check(sc != table.end(), "Session key is either expired or not found"); @@ -154,7 +154,7 @@ namespace eden sequences.erase(sequences.begin()); }); - eosio::excluded_arg auth; + eosio::excluded_arg auth; auth.value.authorized_eden_account = eden_account; eosio::varuint32 num_actions; From 5677922bc734dd3e20b80392f117055699b81388 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Wed, 10 Nov 2021 16:00:16 -0500 Subject: [PATCH 15/54] cleanup --- contracts/eden/include/eden.hpp | 10 +- contracts/eden/include/sessions.hpp | 4 +- contracts/eden/src/actions/sessions.cpp | 10 +- contracts/eden/src/eden-micro-chain.cpp | 10 +- contracts/eden/tests/include/tester-base.hpp | 19 +- contracts/eden/tests/test-eden.cpp | 181 +++++++++---------- 6 files changed, 117 insertions(+), 117 deletions(-) diff --git a/contracts/eden/include/eden.hpp b/contracts/eden/include/eden.hpp index a70b0bb03..202bc9bfd 100644 --- a/contracts/eden/include/eden.hpp +++ b/contracts/eden/include/eden.hpp @@ -88,10 +88,10 @@ namespace eden eosio::name eden_account, const eosio::public_key& key); - void runactions(const eosio::signature& signature, - eosio::ignore eden_account, - eosio::ignore sequence, - eosio::ignore> actions); + void execsession(const eosio::signature& signature, + eosio::ignore eden_account, + eosio::ignore sequence, + eosio::ignore> actions); void withdraw(eosio::name owner, const eosio::asset& quantity); @@ -235,7 +235,7 @@ namespace eden "eden.gm"_n, action(newsession, eden_account, key, expiration, description), eden_auth_action(delsession, 0, eden_account, key), - action(runactions, sig, eden_account, sequence, actions), + action(execsession, sig, eden_account, sequence, actions), action(withdraw, owner, quantity, ricardian_contract(withdraw_ricardian)), action(donate, owner, quantity), action(transfer, to, quantity, memo), diff --git a/contracts/eden/include/sessions.hpp b/contracts/eden/include/sessions.hpp index 340eff3cd..c709f379e 100644 --- a/contracts/eden/include/sessions.hpp +++ b/contracts/eden/include/sessions.hpp @@ -52,8 +52,8 @@ namespace eden if (!authorized_eden_account.value) eosio::require_auth(eden_account); else if (eden_account != authorized_eden_account) - eosio::check(false, "need authority of " + eden_account.to_string() + - " but have authority of " + + eosio::check(false, "need session key of " + eden_account.to_string() + + " but have session key of " + authorized_eden_account.to_string()); } }; diff --git a/contracts/eden/src/actions/sessions.cpp b/contracts/eden/src/actions/sessions.cpp index 23c1e06a2..37d73e3e5 100644 --- a/contracts/eden/src/actions/sessions.cpp +++ b/contracts/eden/src/actions/sessions.cpp @@ -107,10 +107,10 @@ namespace eden table.erase(sc); } // eden::delsession - void eden::runactions(const eosio::signature& signature, - eosio::ignore, - eosio::ignore, - eosio::ignore>) + void eden::execsession(const eosio::signature& signature, + eosio::ignore, + eosio::ignore, + eosio::ignore>) { auto& ds = get_datastream(); auto digest = eosio::sha256(ds.pos(), ds.remaining()); @@ -169,5 +169,5 @@ namespace eden } eosio::check(!ds.remaining(), "detected extra action data"); - } // eden::runactions + } // eden::execsession } // namespace eden diff --git a/contracts/eden/src/eden-micro-chain.cpp b/contracts/eden/src/eden-micro-chain.cpp index 7b24c9e94..9500248f6 100644 --- a/contracts/eden/src/eden-micro-chain.cpp +++ b/contracts/eden/src/eden-micro-chain.cpp @@ -1744,7 +1744,7 @@ void call(void (*f)(const action_context&, Args...), bool dispatch(eosio::name action_name, const action_context& context, eosio::input_stream& s); -void runactions(const action_context& context, eosio::input_stream& s) +void execsession(const action_context& context, eosio::input_stream& s) { eosio::signature signature; eosio::name eden_account; @@ -1760,16 +1760,16 @@ void runactions(const action_context& context, eosio::input_stream& s) auto name = eden::actions::get_name_for_auth_action(index); if (!dispatch(name, context, s)) // fatal because this throws off the rest of the stream - eosio::check(false, "runactions dispatch failed for " + std::to_string(index) + " " + + eosio::check(false, "execsession dispatch failed for " + std::to_string(index) + " " + name.to_string()); } - eosio::check(!s.remaining(), "unpack error (extra data) within runactions"); + eosio::check(!s.remaining(), "unpack error (extra data) within execsession"); } bool dispatch(eosio::name action_name, const action_context& context, eosio::input_stream& s) { - if (action_name == "runactions"_n) - runactions(context, s); + if (action_name == "execsession"_n) + execsession(context, s); else if (action_name == "clearall"_n) call(clearall, context, s); else if (action_name == "withdraw"_n) diff --git a/contracts/eden/tests/include/tester-base.hpp b/contracts/eden/tests/include/tester-base.hpp index 028ae5662..e7852e1b9 100644 --- a/contracts/eden/tests/include/tester-base.hpp +++ b/contracts/eden/tests/include/tester-base.hpp @@ -526,11 +526,11 @@ struct eden_tester } template - void runactions(const private_key& key, - eosio::name eden_account, - eosio::varuint32 sequence, - const char* expected, - const Ts&... actions) + void execsession(const private_key& key, + eosio::name eden_account, + eosio::varuint32 sequence, + const char* expected, + const Ts&... actions) { std::vector data; vector_stream s{data}; @@ -547,11 +547,11 @@ struct eden_tester eosio::action act; act.account = "eden.gm"_n; - act.name = "runactions"_n; + act.name = "execsession"_n; act.authorization.push_back({"payer"_n, "active"_n}); act.data = std::move(data); - // printf("created runactions with data: %s\n", + // printf("created execsession with data: %s\n", // eosio::hex(act.data.begin(), act.data.end()).c_str()); expect(chain.push_transaction(chain.make_transaction({act})), expected); @@ -565,14 +565,15 @@ struct eden_tester } }; +// session action template -std::vector auth_act(Ts&&... args) +std::vector sact(Ts&&... args) { auto index = actions::get_index_for_auth_action(ActionWrapper::action_name); eosio::check(index.has_value(), "action index not found"); auto data = eosio::convert_to_bin(eosio::varuint32(*index)); auto act = ActionWrapper{""_n}.to_action(std::forward(args)...); data.insert(data.end(), act.data.begin(), act.data.end()); - // eosio::print("auth_act: ", eosio::hex(data.begin(), data.end()), "\n"); + // eosio::print("sact: ", eosio::hex(data.begin(), data.end()), "\n"); return data; } diff --git a/contracts/eden/tests/test-eden.cpp b/contracts/eden/tests/test-eden.cpp index d25b4e275..cd9a30fd9 100644 --- a/contracts/eden/tests/test-eden.cpp +++ b/contracts/eden/tests/test-eden.cpp @@ -1295,20 +1295,20 @@ TEST_CASE("contract-auth") t.delsession("alice"_n, "alice"_n, alice_session_pub_key, "Session key is either expired or not found"); - t.runactions(alice_session_priv_key, "alice"_n, 1, - "Recovered session key PUB_K1_665ajq1JUMwWH3bHcRxxTqiZBZBc6CakwUfLkZJxRqp4vAtJaV " - "is either expired or not found", - auth_act("alice"_n, pip_session_pub_key)); - t.runactions(alice_session_2_priv_key, "alice"_n, 1, - "Session key is either expired or not found", - auth_act("alice"_n, alice_session_pub_key)); - t.runactions(alice_session_2_priv_key, "alice"_n, 1, nullptr, - auth_act("alice"_n, alice_session_2_pub_key)); + t.execsession(alice_session_priv_key, "alice"_n, 1, + "Recovered session key PUB_K1_665ajq1JUMwWH3bHcRxxTqiZBZBc6CakwUfLkZJxRqp4vAtJaV " + "is either expired or not found", + sact("alice"_n, pip_session_pub_key)); + t.execsession(alice_session_2_priv_key, "alice"_n, 1, + "Session key is either expired or not found", + sact("alice"_n, alice_session_pub_key)); + t.execsession(alice_session_2_priv_key, "alice"_n, 1, nullptr, + sact("alice"_n, alice_session_2_pub_key)); t.chain.start_block(); - t.runactions(alice_session_2_priv_key, "alice"_n, 1, - "Recovered session key PUB_K1_8VWTR1mogYHEd9HJxgG2Tj3GbPghrnJqMfWfdHbTE11BLxqvo3 " - "is either expired or not found", - auth_act("alice"_n, alice_session_2_pub_key)); + t.execsession(alice_session_2_priv_key, "alice"_n, 1, + "Recovered session key PUB_K1_8VWTR1mogYHEd9HJxgG2Tj3GbPghrnJqMfWfdHbTE11BLxqvo3 " + "is either expired or not found", + sact("alice"_n, alice_session_2_pub_key)); } // TEST_CASE("contract-auth") TEST_CASE("contract-auth-induct") @@ -1327,58 +1327,58 @@ TEST_CASE("contract-auth-induct") t.chain.get_head_block_info().timestamp.to_time_point() + eosio::days(90), "", "member bertie not found"); - t.runactions( - pip_session_priv_key, "pip"_n, 1, "need authority of alice but have authority of pip", - auth_act(1234, "alice"_n, "bertie"_n, std::vector{"pip"_n, "egeon"_n})); - t.runactions( + t.execsession( + pip_session_priv_key, "pip"_n, 1, "need session key of alice but have session key of pip", + sact(1234, "alice"_n, "bertie"_n, std::vector{"pip"_n, "egeon"_n})); + t.execsession( alice_session_priv_key, "alice"_n, 1, nullptr, - auth_act(1234, "alice"_n, "bertie"_n, std::vector{"pip"_n, "egeon"_n})); + sact(1234, "alice"_n, "bertie"_n, std::vector{"pip"_n, "egeon"_n})); t.newsession("bertie"_n, "bertie"_n, bertie_session_pub_key, t.chain.get_head_block_info().timestamp.to_time_point() + eosio::days(90), ""); - t.runactions(alice_session_priv_key, "alice"_n, 2, - "need authority of bertie but have authority of alice", - auth_act(1234, bertie_profile)); - t.runactions(bertie_session_priv_key, "bertie"_n, 1, - "Video can only be set by inviter or a witness", - auth_act(1234, bertie_profile), - auth_act("bertie"_n, 1234, "vid"s)); - t.runactions(bertie_session_priv_key, "bertie"_n, 1, - "need authority of pip but have authority of bertie", - auth_act(1234, bertie_profile), - auth_act("pip"_n, 1234, "vid"s)); - t.runactions( + t.execsession(alice_session_priv_key, "alice"_n, 2, + "need session key of bertie but have session key of alice", + sact(1234, bertie_profile)); + t.execsession(bertie_session_priv_key, "bertie"_n, 1, + "Video can only be set by inviter or a witness", + sact(1234, bertie_profile), + sact("bertie"_n, 1234, "vid"s)); + t.execsession(bertie_session_priv_key, "bertie"_n, 1, + "need session key of pip but have session key of bertie", + sact(1234, bertie_profile), + sact("pip"_n, 1234, "vid"s)); + t.execsession( bertie_session_priv_key, "bertie"_n, 1, "Induction can only be endorsed by inviter or a witness", - auth_act(1234, bertie_profile), - auth_act("bertie"_n, 1234, t.hash_induction("vid"s, bertie_profile))); - t.runactions( - bertie_session_priv_key, "bertie"_n, 1, "need authority of pip but have authority of bertie", - auth_act(1234, bertie_profile), - auth_act("pip"_n, 1234, t.hash_induction("vid"s, bertie_profile))); - t.runactions(bertie_session_priv_key, "bertie"_n, 1, nullptr, - auth_act(1234, bertie_profile)); - - t.runactions( - pip_session_priv_key, "pip"_n, 2, "need authority of alice but have authority of pip", - auth_act("alice"_n, 1234, std::vector(4), - eosio::bytes{}, std::nullopt)); - t.runactions(pip_session_priv_key, "pip"_n, 2, nullptr, - auth_act("pip"_n, 1234, std::vector(0), - eosio::bytes{}, std::nullopt)); - - t.runactions( - pip_session_priv_key, "pip"_n, 3, nullptr, - auth_act("pip"_n, 1234, "vid"s), - auth_act("pip"_n, 1234, t.hash_induction("vid"s, bertie_profile))); - t.runactions( + sact(1234, bertie_profile), + sact("bertie"_n, 1234, t.hash_induction("vid"s, bertie_profile))); + t.execsession( + bertie_session_priv_key, "bertie"_n, 1, + "need session key of pip but have session key of bertie", + sact(1234, bertie_profile), + sact("pip"_n, 1234, t.hash_induction("vid"s, bertie_profile))); + t.execsession(bertie_session_priv_key, "bertie"_n, 1, nullptr, + sact(1234, bertie_profile)); + + t.execsession(pip_session_priv_key, "pip"_n, 2, + "need session key of alice but have session key of pip", + sact("alice"_n, 1234, std::vector(4), + eosio::bytes{}, std::nullopt)); + t.execsession(pip_session_priv_key, "pip"_n, 2, nullptr, + sact("pip"_n, 1234, std::vector(0), + eosio::bytes{}, std::nullopt)); + + t.execsession( + pip_session_priv_key, "pip"_n, 3, nullptr, sact("pip"_n, 1234, "vid"s), + sact("pip"_n, 1234, t.hash_induction("vid"s, bertie_profile))); + t.execsession( alice_session_priv_key, "alice"_n, 3, nullptr, - auth_act("alice"_n, 1234, t.hash_induction("vid"s, bertie_profile))); + sact("alice"_n, 1234, t.hash_induction("vid"s, bertie_profile))); - t.runactions(pip_session_priv_key, "pip"_n, 4, - "need authority of alice but have authority of pip", - auth_act("alice"_n, 1234)); - t.runactions(pip_session_priv_key, "pip"_n, 4, nullptr, - auth_act("pip"_n, 1234)); + t.execsession(pip_session_priv_key, "pip"_n, 4, + "need session key of alice but have session key of pip", + sact("alice"_n, 1234)); + t.execsession(pip_session_priv_key, "pip"_n, 4, nullptr, + sact("pip"_n, 1234)); } // TEST_CASE("contract-auth-induct") TEST_CASE("contract-auth-elect") @@ -1398,14 +1398,14 @@ TEST_CASE("contract-auth-elect") }; create_sessions(); - t.runactions(pip_session_priv_key, "pip"_n, 1, - "need authority of alice but have authority of pip", - auth_act("alice"_n, true)); - t.runactions(alice_session_priv_key, "alice"_n, 1, nullptr, - auth_act("alice"_n, true)); + t.execsession(pip_session_priv_key, "pip"_n, 1, + "need session key of alice but have session key of pip", + sact("alice"_n, true)); + t.execsession(alice_session_priv_key, "alice"_n, 1, nullptr, + sact("alice"_n, true)); t.chain.finish_block(); - t.runactions(alice_session_priv_key, "alice"_n, 2, "Not currently opted out", - auth_act("alice"_n, true)); + t.execsession(alice_session_priv_key, "alice"_n, 2, "Not currently opted out", + sact("alice"_n, true)); t.electdonate_all(); t.skip_to(t.next_election_time().to_time_point() - eosio::days(1)); @@ -1413,31 +1413,30 @@ TEST_CASE("contract-auth-elect") t.skip_to(t.next_election_time().to_time_point() + eosio::minutes(10)); t.setup_election(); - t.runactions(pip_session_priv_key, "pip"_n, 2, - "Recovered session key PUB_K1_8YQhKe3x1xTA1KHmkBPznWqa3UGQsaHTUMkJJtcds9giKNsHGv " - "is either expired or not found", - auth_act("pip"_n, 0, std::vector(0), - eosio::bytes{}, std::nullopt)); + t.execsession(pip_session_priv_key, "pip"_n, 2, + "Recovered session key PUB_K1_8YQhKe3x1xTA1KHmkBPznWqa3UGQsaHTUMkJJtcds9giKNsHGv " + "is either expired or not found", + sact("pip"_n, 0, std::vector(0), + eosio::bytes{}, std::nullopt)); create_sessions(); - t.runactions(pip_session_priv_key, "pip"_n, 2, - "need authority of alice but have authority of pip", - auth_act("alice"_n, 0, std::vector(0), - eosio::bytes{}, std::nullopt)); - t.runactions(pip_session_priv_key, "pip"_n, 2, nullptr, - auth_act("pip"_n, 0, std::vector(0), - eosio::bytes{}, std::nullopt)); - - t.runactions(pip_session_priv_key, "pip"_n, 3, - "need authority of alice but have authority of pip", - auth_act(0, "alice"_n, "pip"_n)); - t.runactions(alice_session_priv_key, "alice"_n, 0, "alice and pip are not in the same group", - auth_act(0, "alice"_n, "pip"_n)); - - t.runactions(pip_session_priv_key, "pip"_n, 3, - "need authority of alice but have authority of pip", - auth_act(0, "alice"_n, - "Qmb7WmZiSDXss5HfuKfoSf6jxTDrHzr8AoAUDeDMLNDuws")); - t.runactions(alice_session_priv_key, "alice"_n, 1, nullptr, - auth_act(0, "alice"_n, - "Qmb7WmZiSDXss5HfuKfoSf6jxTDrHzr8AoAUDeDMLNDuws")); + t.execsession(pip_session_priv_key, "pip"_n, 2, + "need session key of alice but have session key of pip", + sact("alice"_n, 0, std::vector(0), + eosio::bytes{}, std::nullopt)); + t.execsession(pip_session_priv_key, "pip"_n, 2, nullptr, + sact("pip"_n, 0, std::vector(0), + eosio::bytes{}, std::nullopt)); + + t.execsession(pip_session_priv_key, "pip"_n, 3, + "need session key of alice but have session key of pip", + sact(0, "alice"_n, "pip"_n)); + t.execsession(alice_session_priv_key, "alice"_n, 0, "alice and pip are not in the same group", + sact(0, "alice"_n, "pip"_n)); + + t.execsession( + pip_session_priv_key, "pip"_n, 3, "need session key of alice but have session key of pip", + sact(0, "alice"_n, "Qmb7WmZiSDXss5HfuKfoSf6jxTDrHzr8AoAUDeDMLNDuws")); + t.execsession( + alice_session_priv_key, "alice"_n, 1, nullptr, + sact(0, "alice"_n, "Qmb7WmZiSDXss5HfuKfoSf6jxTDrHzr8AoAUDeDMLNDuws")); } // TEST_CASE("contract-auth-elect") From 8e4b8598521a0e31a871e7580acd3d2fb8632cc1 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Wed, 10 Nov 2021 16:15:01 -0500 Subject: [PATCH 16/54] cleanup --- contracts/eden/include/eden_abi_generator.hpp | 34 +++++++++---------- contracts/eden/include/eden_dispatcher.hpp | 14 ++++---- contracts/eden/src/actions/sessions.cpp | 6 ++-- contracts/eden/src/eden-micro-chain.cpp | 2 +- contracts/eden/tests/include/tester-base.hpp | 2 +- 5 files changed, 29 insertions(+), 29 deletions(-) diff --git a/contracts/eden/include/eden_abi_generator.hpp b/contracts/eden/include/eden_abi_generator.hpp index 1a41973e3..5df56e3c4 100644 --- a/contracts/eden/include/eden_abi_generator.hpp +++ b/contracts/eden/include/eden_abi_generator.hpp @@ -1,20 +1,20 @@ #pragma once -#define EOSIO_ABIGEN_ITEMauth_actions(ns, variant_name, missing_struct_name) \ - ([&] { \ - gen.def.structs.push_back(eosio::struct_def{missing_struct_name}); \ - eosio::variant_def vdef{variant_name}; \ - ns::for_each_auth_action([&](uint32_t index, const char* name, const auto&) { \ - if (index >= vdef.types.size()) \ - vdef.types.resize(index + 1, missing_struct_name); \ - vdef.types[index] = name; \ - }); \ - auto& variants = gen.def.variants.value; \ - auto it = std::find_if(variants.begin(), variants.end(), \ - [&](auto& d) { return d.name == variant_name; }); \ - if (it != variants.end()) \ - *it = std::move(vdef); \ - else \ - variants.push_back(std::move(vdef)); \ - })(); \ +#define EOSIO_ABIGEN_ITEMauth_actions(ns, variant_name, missing_struct_name) \ + ([&] { \ + gen.def.structs.push_back(eosio::struct_def{missing_struct_name}); \ + eosio::variant_def vdef{variant_name}; \ + ns::for_each_session_action([&](uint32_t index, const char* name, const auto&) { \ + if (index >= vdef.types.size()) \ + vdef.types.resize(index + 1, missing_struct_name); \ + vdef.types[index] = name; \ + }); \ + auto& variants = gen.def.variants.value; \ + auto it = std::find_if(variants.begin(), variants.end(), \ + [&](auto& d) { return d.name == variant_name; }); \ + if (it != variants.end()) \ + *it = std::move(vdef); \ + else \ + variants.push_back(std::move(vdef)); \ + })(); \ , 1 diff --git a/contracts/eden/include/eden_dispatcher.hpp b/contracts/eden/include/eden_dispatcher.hpp index c4e3a57a4..d4857d249 100644 --- a/contracts/eden/include/eden_dispatcher.hpp +++ b/contracts/eden/include/eden_dispatcher.hpp @@ -73,10 +73,10 @@ namespace eden EOSIO_ACTIONS(CONTRACT_CLASS, CONTRACT_ACCOUNT, __VA_ARGS__) \ namespace actions \ { \ - inline bool dispatch_auth(eosio::name contract, \ - uint32_t index, \ - const eosio::excluded_arg& current_session, \ - eosio::datastream& ds) \ + inline bool session_dispatch(eosio::name contract, \ + uint32_t index, \ + const eosio::excluded_arg& current_session, \ + eosio::datastream& ds) \ { \ switch (index) \ { \ @@ -85,11 +85,11 @@ namespace eden return false; \ } \ template \ - void for_each_auth_action(F f) \ + void for_each_session_action(F f) \ { \ EDEN_GET_AUTH_ACTION(CONTRACT_CLASS, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \ } \ - inline eosio::name get_name_for_auth_action(uint32_t index) \ + inline eosio::name get_name_for_session_action(uint32_t index) \ { \ switch (index) \ { \ @@ -97,7 +97,7 @@ namespace eden } \ return {}; \ } \ - inline std::optional get_index_for_auth_action(eosio::name name) \ + inline std::optional get_index_for_session_action(eosio::name name) \ { \ EDEN_INDEX_FOR_AUTH_ACTION(CONTRACT_CLASS, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \ return {}; \ diff --git a/contracts/eden/src/actions/sessions.cpp b/contracts/eden/src/actions/sessions.cpp index 37d73e3e5..4d401b3c9 100644 --- a/contracts/eden/src/actions/sessions.cpp +++ b/contracts/eden/src/actions/sessions.cpp @@ -154,8 +154,8 @@ namespace eden sequences.erase(sequences.begin()); }); - eosio::excluded_arg auth; - auth.value.authorized_eden_account = eden_account; + eosio::excluded_arg current_session; + current_session.value.authorized_eden_account = eden_account; eosio::varuint32 num_actions; ds >> num_actions; @@ -164,7 +164,7 @@ namespace eden { eosio::varuint32 index; ds >> index; - eosio::check(actions::dispatch_auth(get_self(), index.value, auth, ds), + eosio::check(actions::session_dispatch(get_self(), index.value, current_session, ds), "unsupported action index"); } diff --git a/contracts/eden/src/eden-micro-chain.cpp b/contracts/eden/src/eden-micro-chain.cpp index 9500248f6..8f41fe2b2 100644 --- a/contracts/eden/src/eden-micro-chain.cpp +++ b/contracts/eden/src/eden-micro-chain.cpp @@ -1757,7 +1757,7 @@ void execsession(const action_context& context, eosio::input_stream& s) for (uint32_t i = 0; i < num_actions.value; ++i) { auto index = eosio::varuint32_from_bin(s); - auto name = eden::actions::get_name_for_auth_action(index); + auto name = eden::actions::get_name_for_session_action(index); if (!dispatch(name, context, s)) // fatal because this throws off the rest of the stream eosio::check(false, "execsession dispatch failed for " + std::to_string(index) + " " + diff --git a/contracts/eden/tests/include/tester-base.hpp b/contracts/eden/tests/include/tester-base.hpp index e7852e1b9..12cbfb81b 100644 --- a/contracts/eden/tests/include/tester-base.hpp +++ b/contracts/eden/tests/include/tester-base.hpp @@ -569,7 +569,7 @@ struct eden_tester template std::vector sact(Ts&&... args) { - auto index = actions::get_index_for_auth_action(ActionWrapper::action_name); + auto index = actions::get_index_for_session_action(ActionWrapper::action_name); eosio::check(index.has_value(), "action index not found"); auto data = eosio::convert_to_bin(eosio::varuint32(*index)); auto act = ActionWrapper{""_n}.to_action(std::forward(args)...); From 684d629679f8db8efc0ec0a949f76b511e46edf1 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Wed, 10 Nov 2021 16:36:55 -0500 Subject: [PATCH 17/54] cleanup --- contracts/eden/include/eden.hpp | 66 ++++---- contracts/eden/include/eden_abi_generator.hpp | 2 +- contracts/eden/include/eden_dispatcher.hpp | 147 +++++++++--------- contracts/eden/src/eden.cpp | 2 +- 4 files changed, 112 insertions(+), 105 deletions(-) diff --git a/contracts/eden/include/eden.hpp b/contracts/eden/include/eden.hpp index 202bc9bfd..a35bedbb6 100644 --- a/contracts/eden/include/eden.hpp +++ b/contracts/eden/include/eden.hpp @@ -234,7 +234,7 @@ namespace eden eden, "eden.gm"_n, action(newsession, eden_account, key, expiration, description), - eden_auth_action(delsession, 0, eden_account, key), + eden_session_action(delsession, 0, eden_account, key), action(execsession, sig, eden_account, sequence, actions), action(withdraw, owner, quantity, ricardian_contract(withdraw_ricardian)), action(donate, owner, quantity), @@ -257,46 +257,50 @@ namespace eden action(addtogenesis, account, expiration), action(gensetexpire, id, new_expiration), action(clearall, ricardian_contract(clearall_ricardian)), - eden_auth_action(inductinit, - 10, - id, - inviter, - invitee, - witnesses, - ricardian_contract(inductinit_ricardian)), - eden_auth_action(inductmeetin, 1, account, id, keys, data, old_data), - eden_auth_action(inductprofil, - 2, - id, - new_member_profile, - ricardian_contract(inductprofil_ricardian)), - eden_auth_action(inductvideo, - 3, - account, - id, - video, - ricardian_contract(inductvideo_ricardian)), - eden_auth_action(inductendors, - 4, - account, - id, - induction_data_hash, - ricardian_contract(inductendors_ricardian)), + eden_session_action(inductinit, + 10, + id, + inviter, + invitee, + witnesses, + ricardian_contract(inductinit_ricardian)), + eden_session_action(inductmeetin, 1, account, id, keys, data, old_data), + eden_session_action(inductprofil, + 2, + id, + new_member_profile, + ricardian_contract(inductprofil_ricardian)), + eden_session_action(inductvideo, + 3, + account, + id, + video, + ricardian_contract(inductvideo_ricardian)), + eden_session_action(inductendors, + 4, + account, + id, + induction_data_hash, + ricardian_contract(inductendors_ricardian)), action(setencpubkey, account, key), action(electsettime, election_time), action(electconfig, day, time, round_duration), - eden_auth_action(electopt, 5, member, participating), + eden_session_action(electopt, 5, member, participating), action(electseed, btc_header), - eden_auth_action(electmeeting, 6, account, round, keys, data, old_data), - eden_auth_action(electvote, 7, round, voter, candidate), - eden_auth_action(electvideo, 8, round, voter, video), + eden_session_action(electmeeting, 6, account, round, keys, data, old_data), + eden_session_action(electvote, 7, round, voter, candidate), + eden_session_action(electvideo, 8, round, voter, video), action(electprocess, max_steps), action(bylawspropose, proposer, bylaws), action(bylawsapprove, approver, bylaws_hash), action(bylawsratify, approver, bylaws_hash), action(distribute, max_steps), action(inductdonate, payer, id, quantity, ricardian_contract(inductdonate_ricardian)), - eden_auth_action(inductcancel, 9, account, id, ricardian_contract(inductcancel_ricardian)), + eden_session_action(inductcancel, + 9, + account, + id, + ricardian_contract(inductcancel_ricardian)), action(inducted, inductee, ricardian_contract(inducted_ricardian)), action(resign, account), action(gc, limit, ricardian_contract(gc_ricardian)), diff --git a/contracts/eden/include/eden_abi_generator.hpp b/contracts/eden/include/eden_abi_generator.hpp index 5df56e3c4..3eeac288b 100644 --- a/contracts/eden/include/eden_abi_generator.hpp +++ b/contracts/eden/include/eden_abi_generator.hpp @@ -1,6 +1,6 @@ #pragma once -#define EOSIO_ABIGEN_ITEMauth_actions(ns, variant_name, missing_struct_name) \ +#define EOSIO_ABIGEN_ITEMeden_session_actions(ns, variant_name, missing_struct_name) \ ([&] { \ gen.def.structs.push_back(eosio::struct_def{missing_struct_name}); \ eosio::variant_def vdef{variant_name}; \ diff --git a/contracts/eden/include/eden_dispatcher.hpp b/contracts/eden/include/eden_dispatcher.hpp index d4857d249..50fea63f8 100644 --- a/contracts/eden/include/eden_dispatcher.hpp +++ b/contracts/eden/include/eden_dispatcher.hpp @@ -6,11 +6,11 @@ namespace eden { template - void execute_auth_action(eosio::name contract, - R (T::*func)(const eosio::excluded_arg& current_session, - Args...), - const eosio::excluded_arg& current_session, - eosio::datastream& ds) + void execute_session_action( + eosio::name contract, + R (T::*func)(const eosio::excluded_arg& current_session, Args...), + const eosio::excluded_arg& current_session, + eosio::datastream& ds) { std::tuple...> t; ds >> t; @@ -19,87 +19,90 @@ namespace eden } } // namespace eden -#define EOSIO_MATCH_ACTIONeden_auth_action EOSIO_MATCH_YES -#define EOSIO_EXTRACT_ACTION_NAMEeden_auth_action(name, index, ...) name -#define EOSIO_EXTRACT_ACTION_ARGSeden_auth_action(name, index, ...) __VA_ARGS__ +#define EOSIO_MATCH_ACTIONeden_session_action EOSIO_MATCH_YES +#define EOSIO_EXTRACT_ACTION_NAMEeden_session_action(name, index, ...) name +#define EOSIO_EXTRACT_ACTION_ARGSeden_session_action(name, index, ...) __VA_ARGS__ -#define EDEN_MATCH_AUTH_ACTION(x) EOSIO_MATCH(EDEN_MATCH_AUTH_ACTION, x) -#define EDEN_MATCH_AUTH_ACTIONeden_auth_action EOSIO_MATCH_YES +#define EDEN_MATCH_SESSION_ACTION(x) EOSIO_MATCH(EDEN_MATCH_SESSION_ACTION, x) +#define EDEN_MATCH_SESSION_ACTIONeden_session_action EOSIO_MATCH_YES -#define EDEN_EXTRACT_AUTH_ACTION_INDEX(x) BOOST_PP_CAT(EDEN_EXTRACT_AUTH_ACTION_INDEX, x) -#define EDEN_EXTRACT_AUTH_ACTION_INDEXeden_auth_action(name, index, ...) index +#define EDEN_EXTRACT_SESSION_ACTION_INDEX(x) BOOST_PP_CAT(EDEN_EXTRACT_SESSION_ACTION_INDEX, x) +#define EDEN_EXTRACT_SESSION_ACTION_INDEXeden_session_action(name, index, ...) index -#define EDEN_DISPATCH_AUTH_ACTION_INTERNAL_1(r, type, member) \ - case EDEN_EXTRACT_AUTH_ACTION_INDEX(member): \ - ::eden::execute_auth_action(contract, &type::EOSIO_EXTRACT_ACTION_NAME(member), \ - current_session, ds); \ +#define EDEN_DISPATCH_SESSION_ACTION_INTERNAL_1(r, type, member) \ + case EDEN_EXTRACT_SESSION_ACTION_INDEX(member): \ + ::eden::execute_session_action(contract, &type::EOSIO_EXTRACT_ACTION_NAME(member), \ + current_session, ds); \ return true; -#define EDEN_DISPATCH_AUTH_ACTION_INTERNAL(r, type, member) \ - BOOST_PP_IIF(EDEN_MATCH_AUTH_ACTION(member), EDEN_DISPATCH_AUTH_ACTION_INTERNAL_1, EOSIO_EMPTY) \ +#define EDEN_DISPATCH_SESSION_ACTION_INTERNAL(r, type, member) \ + BOOST_PP_IIF(EDEN_MATCH_SESSION_ACTION(member), EDEN_DISPATCH_SESSION_ACTION_INTERNAL_1, \ + EOSIO_EMPTY) \ (r, type, member) -#define EDEN_DISPATCH_AUTH_ACTION(type, MEMBERS) \ - BOOST_PP_SEQ_FOR_EACH(EDEN_DISPATCH_AUTH_ACTION_INTERNAL, type, MEMBERS) +#define EDEN_DISPATCH_SESSION_ACTION(type, MEMBERS) \ + BOOST_PP_SEQ_FOR_EACH(EDEN_DISPATCH_SESSION_ACTION_INTERNAL, type, MEMBERS) -#define EDEN_GET_AUTH_ACTION_INTERNAL_1(r, type, member) \ - f(EDEN_EXTRACT_AUTH_ACTION_INDEX(member), \ +#define EDEN_GET_SESSION_ACTION_INTERNAL_1(r, type, member) \ + f(EDEN_EXTRACT_SESSION_ACTION_INDEX(member), \ BOOST_PP_STRINGIZE(EOSIO_EXTRACT_ACTION_NAME(member)), \ &type::EOSIO_EXTRACT_ACTION_NAME(member)); -#define EDEN_GET_AUTH_ACTION_INTERNAL(r, type, member) \ - BOOST_PP_IIF(EDEN_MATCH_AUTH_ACTION(member), EDEN_GET_AUTH_ACTION_INTERNAL_1, EOSIO_EMPTY) \ +#define EDEN_GET_SESSION_ACTION_INTERNAL(r, type, member) \ + BOOST_PP_IIF(EDEN_MATCH_SESSION_ACTION(member), EDEN_GET_SESSION_ACTION_INTERNAL_1, \ + EOSIO_EMPTY) \ (r, type, member) -#define EDEN_GET_AUTH_ACTION(type, MEMBERS) \ - BOOST_PP_SEQ_FOR_EACH(EDEN_GET_AUTH_ACTION_INTERNAL, type, MEMBERS) +#define EDEN_GET_SESSION_ACTION(type, MEMBERS) \ + BOOST_PP_SEQ_FOR_EACH(EDEN_GET_SESSION_ACTION_INTERNAL, type, MEMBERS) -#define EDEN_NAME_FOR_AUTH_ACTION_INTERNAL_1(r, type, member) \ - case EDEN_EXTRACT_AUTH_ACTION_INDEX(member): \ +#define EDEN_NAME_FOR_SESSION_ACTION_INTERNAL_1(r, type, member) \ + case EDEN_EXTRACT_SESSION_ACTION_INDEX(member): \ return BOOST_PP_CAT(BOOST_PP_STRINGIZE(EOSIO_EXTRACT_ACTION_NAME(member)), _n); -#define EDEN_NAME_FOR_AUTH_ACTION_INTERNAL(r, type, member) \ - BOOST_PP_IIF(EDEN_MATCH_AUTH_ACTION(member), EDEN_NAME_FOR_AUTH_ACTION_INTERNAL_1, EOSIO_EMPTY) \ +#define EDEN_NAME_FOR_SESSION_ACTION_INTERNAL(r, type, member) \ + BOOST_PP_IIF(EDEN_MATCH_SESSION_ACTION(member), EDEN_NAME_FOR_SESSION_ACTION_INTERNAL_1, \ + EOSIO_EMPTY) \ (r, type, member) -#define EDEN_NAME_FOR_AUTH_ACTION(type, MEMBERS) \ - BOOST_PP_SEQ_FOR_EACH(EDEN_NAME_FOR_AUTH_ACTION_INTERNAL, type, MEMBERS) +#define EDEN_NAME_FOR_SESSION_ACTION(type, MEMBERS) \ + BOOST_PP_SEQ_FOR_EACH(EDEN_NAME_FOR_SESSION_ACTION_INTERNAL, type, MEMBERS) -#define EDEN_INDEX_FOR_AUTH_ACTION_INTERNAL_1(r, type, member) \ +#define EDEN_INDEX_FOR_SESSION_ACTION_INTERNAL_1(r, type, member) \ if (name == BOOST_PP_CAT(BOOST_PP_STRINGIZE(EOSIO_EXTRACT_ACTION_NAME(member)), _n)) \ - return EDEN_EXTRACT_AUTH_ACTION_INDEX(member); -#define EDEN_INDEX_FOR_AUTH_ACTION_INTERNAL(r, type, member) \ - BOOST_PP_IIF(EDEN_MATCH_AUTH_ACTION(member), EDEN_INDEX_FOR_AUTH_ACTION_INTERNAL_1, \ - EOSIO_EMPTY) \ + return EDEN_EXTRACT_SESSION_ACTION_INDEX(member); +#define EDEN_INDEX_FOR_SESSION_ACTION_INTERNAL(r, type, member) \ + BOOST_PP_IIF(EDEN_MATCH_SESSION_ACTION(member), EDEN_INDEX_FOR_SESSION_ACTION_INTERNAL_1, \ + EOSIO_EMPTY) \ (r, type, member) -#define EDEN_INDEX_FOR_AUTH_ACTION(type, MEMBERS) \ - BOOST_PP_SEQ_FOR_EACH(EDEN_INDEX_FOR_AUTH_ACTION_INTERNAL, type, MEMBERS) +#define EDEN_INDEX_FOR_SESSION_ACTION(type, MEMBERS) \ + BOOST_PP_SEQ_FOR_EACH(EDEN_INDEX_FOR_SESSION_ACTION_INTERNAL, type, MEMBERS) -#define EDEN_ACTIONS(CONTRACT_CLASS, CONTRACT_ACCOUNT, ...) \ - EOSIO_ACTIONS(CONTRACT_CLASS, CONTRACT_ACCOUNT, __VA_ARGS__) \ - namespace actions \ - { \ - inline bool session_dispatch(eosio::name contract, \ - uint32_t index, \ - const eosio::excluded_arg& current_session, \ - eosio::datastream& ds) \ - { \ - switch (index) \ - { \ - EDEN_DISPATCH_AUTH_ACTION(CONTRACT_CLASS, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \ - } \ - return false; \ - } \ - template \ - void for_each_session_action(F f) \ - { \ - EDEN_GET_AUTH_ACTION(CONTRACT_CLASS, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \ - } \ - inline eosio::name get_name_for_session_action(uint32_t index) \ - { \ - switch (index) \ - { \ - EDEN_NAME_FOR_AUTH_ACTION(CONTRACT_CLASS, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \ - } \ - return {}; \ - } \ - inline std::optional get_index_for_session_action(eosio::name name) \ - { \ - EDEN_INDEX_FOR_AUTH_ACTION(CONTRACT_CLASS, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \ - return {}; \ - } \ +#define EDEN_ACTIONS(CONTRACT_CLASS, CONTRACT_ACCOUNT, ...) \ + EOSIO_ACTIONS(CONTRACT_CLASS, CONTRACT_ACCOUNT, __VA_ARGS__) \ + namespace actions \ + { \ + inline bool session_dispatch(eosio::name contract, \ + uint32_t index, \ + const eosio::excluded_arg& current_session, \ + eosio::datastream& ds) \ + { \ + switch (index) \ + { \ + EDEN_DISPATCH_SESSION_ACTION(CONTRACT_CLASS, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \ + } \ + return false; \ + } \ + template \ + void for_each_session_action(F f) \ + { \ + EDEN_GET_SESSION_ACTION(CONTRACT_CLASS, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \ + } \ + inline eosio::name get_name_for_session_action(uint32_t index) \ + { \ + switch (index) \ + { \ + EDEN_NAME_FOR_SESSION_ACTION(CONTRACT_CLASS, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \ + } \ + return {}; \ + } \ + inline std::optional get_index_for_session_action(eosio::name name) \ + { \ + EDEN_INDEX_FOR_SESSION_ACTION(CONTRACT_CLASS, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \ + return {}; \ + } \ } diff --git a/contracts/eden/src/eden.cpp b/contracts/eden/src/eden.cpp index d30a6c885..12642b533 100644 --- a/contracts/eden/src/eden.cpp +++ b/contracts/eden/src/eden.cpp @@ -51,7 +51,7 @@ EOSIO_ABIGEN( "STRING_VEC"), variant("action", eden::action), actions(eden::actions), - auth_actions(eden::actions, "action", "unsupported_action"), + eden_session_actions(eden::actions, "action", "unsupported_action"), table("account"_n, eden::account_variant), table("auction"_n, eden::auction_variant), table("bylaws"_n, eden::bylaws_variant), From 09fc987f0bad6b8e8b79001f170f6e5c79a7933f Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Thu, 11 Nov 2021 13:15:46 -0500 Subject: [PATCH 18/54] Disable malfunctioning expiration for now --- contracts/eden/src/eden-micro-chain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/eden/src/eden-micro-chain.cpp b/contracts/eden/src/eden-micro-chain.cpp index 294b32456..cfe36f663 100644 --- a/contracts/eden/src/eden-micro-chain.cpp +++ b/contracts/eden/src/eden-micro-chain.cpp @@ -1948,7 +1948,7 @@ void filter_block(const subchain::eosio_block& block) } // for(action) // garbage collection housekeeping - remove_expired_inductions(block); + // remove_expired_inductions(block); eosio::check(!block_state.in_withdraw && !block_state.in_manual_transfer, "missing transfer notification"); From 76c617c72d874b7ce54ba497e058a7df6fdb6415 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Thu, 11 Nov 2021 14:05:23 -0500 Subject: [PATCH 19/54] force box build --- packages/box/src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/box/src/index.ts b/packages/box/src/index.ts index 992df2530..e3d3e7577 100644 --- a/packages/box/src/index.ts +++ b/packages/box/src/index.ts @@ -32,3 +32,4 @@ if (subchainConfig.enable) { createWSServer("/v1/subchain", server); startSubchain(); } + From cb29a3aa509c389d1f70b3c4ed6472d5895a63c2 Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Fri, 12 Nov 2021 08:56:20 -0500 Subject: [PATCH 20/54] Reset opt-in status when the election is rescheduled. --- contracts/eden/include/elections.hpp | 81 ++++++++++++---- contracts/eden/include/members.hpp | 9 +- contracts/eden/src/actions/elect.cpp | 2 +- contracts/eden/src/actions/induct.cpp | 2 +- contracts/eden/src/elections.cpp | 129 +++++++++++++++++--------- contracts/eden/src/members.cpp | 7 +- contracts/eden/tests/test-eden.cpp | 18 +++- 7 files changed, 172 insertions(+), 76 deletions(-) diff --git a/contracts/eden/include/elections.hpp b/contracts/eden/include/elections.hpp index ca5f29eec..f4d8074aa 100644 --- a/contracts/eden/include/elections.hpp +++ b/contracts/eden/include/elections.hpp @@ -109,14 +109,25 @@ namespace eden EOSIO_REFLECT(current_election_state_pending_date); EOSIO_COMPARE(current_election_state_pending_date); - struct current_election_state_registration + struct current_election_state_registration_v0 { eosio::block_timestamp start_time; - uint16_t - election_threshold; // The election may be moved forward if active membership reached this + // The election may be moved forward if active membership reached this + uint16_t election_threshold; + // The number of times that the election schedule has been updated + // always > 0 + uint8_t election_schedule_version = 1; }; - EOSIO_REFLECT(current_election_state_registration, start_time, election_threshold); - EOSIO_COMPARE(current_election_state_registration); + EOSIO_REFLECT(current_election_state_registration_v0, start_time, election_threshold); + EOSIO_COMPARE(current_election_state_registration_v0); + + struct current_election_state_registration_v1 : current_election_state_registration_v0 + { + }; + EOSIO_REFLECT(current_election_state_registration_v1, + base current_election_state_registration_v0, + election_schedule_version); + EOSIO_COMPARE(current_election_state_registration_v1); struct election_seeder { @@ -130,27 +141,44 @@ namespace eden EOSIO_REFLECT(election_seeder, current, start_time, end_time); EOSIO_COMPARE(election_seeder); - struct current_election_state_seeding + struct current_election_state_seeding_v0 { election_seeder seed; + std::uint8_t election_schedule_version = 1; + }; + EOSIO_REFLECT(current_election_state_seeding_v0, seed); + EOSIO_COMPARE(current_election_state_seeding_v0); + + struct current_election_state_seeding_v1 : current_election_state_seeding_v0 + { }; - EOSIO_REFLECT(current_election_state_seeding, seed); - EOSIO_COMPARE(current_election_state_seeding); + EOSIO_REFLECT(current_election_state_seeding_v1, + base current_election_state_seeding_v0, + election_schedule_version) // In this phase, every voter is assigned a unique random integer id in [0,N) - struct current_election_state_init_voters + struct current_election_state_init_voters_v0 { uint16_t next_member_idx; election_rng rng; eosio::name last_processed = {}; uint16_t next_report_index = 0; + uint8_t election_schedule_version = 1; }; - EOSIO_REFLECT(current_election_state_init_voters, + EOSIO_REFLECT(current_election_state_init_voters_v0, next_member_idx, rng, last_processed, next_report_index) - EOSIO_COMPARE(current_election_state_init_voters); + EOSIO_COMPARE(current_election_state_init_voters_v0); + + struct current_election_state_init_voters_v1 : current_election_state_init_voters_v0 + { + }; + EOSIO_REFLECT(current_election_state_init_voters_v1, + base current_election_state_init_voters_v0, + election_schedule_version) + EOSIO_COMPARE(current_election_state_init_voters_v1); struct current_election_state_active { @@ -188,15 +216,35 @@ namespace eden EOSIO_COMPARE(current_election_state_final); using current_election_state = std::variant; + current_election_state_final, + current_election_state_registration_v1, + current_election_state_seeding_v1, + current_election_state_init_voters_v1>; using current_election_state_singleton = eosio::singleton<"elect.curr"_n, current_election_state>; + template + T* get_if_derived(current_election_state* state) + { + return std::visit( + [](auto& s) -> T* { + if constexpr (std::is_base_of_v>) + { + return &s; + } + else + { + return nullptr; + } + }, + *state); + } + // Requirements: // - Except for the last round, the group size shall be in [4,6] // - The last round has a minimum group size of 3 @@ -219,7 +267,7 @@ namespace eden void set_state_sing(const current_election_state& new_value); void add_voter(election_rng& rng, uint8_t round, uint16_t& next_index, eosio::name member); - uint32_t randomize_voters(current_election_state_init_voters& state, uint32_t max_steps); + uint32_t randomize_voters(current_election_state_init_voters_v0& state, uint32_t max_steps); std::vector extract_board(); void finish_election(std::vector&& board, eosio::name winner); void set_board_permission(const std::vector& board); @@ -234,6 +282,7 @@ namespace eden { } std::optional get_next_election_time(); + std::uint8_t election_schedule_version(); void set_time(uint8_t day, const std::string& time); void set_default_election(eosio::time_point_sec origin_time); void trigger_election(); diff --git a/contracts/eden/include/members.hpp b/contracts/eden/include/members.hpp index 911d95a03..72fb72f39 100644 --- a/contracts/eden/include/members.hpp +++ b/contracts/eden/include/members.hpp @@ -17,12 +17,7 @@ namespace eden active_member = 1 }; - using election_participation_status_type = uint8_t; - enum election_participation_status : election_participation_status_type - { - not_in_election, - in_election - }; + inline constexpr std::uint8_t not_in_election = 0; struct migrate_member_v0 { @@ -38,7 +33,7 @@ namespace eden member_status_type status; uint64_t nft_template_id; // Only reflected in v1 - election_participation_status_type election_participation_status = not_in_election; + uint8_t election_participation_status = 0; uint8_t election_rank = 0; eosio::name representative{uint64_t(-1)}; std::optional encryption_key; diff --git a/contracts/eden/src/actions/elect.cpp b/contracts/eden/src/actions/elect.cpp index e73192224..c872277b4 100644 --- a/contracts/eden/src/actions/elect.cpp +++ b/contracts/eden/src/actions/elect.cpp @@ -39,7 +39,7 @@ namespace eden } else { - eosio::check(member.election_participation_status() == in_election, + eosio::check(member.election_participation_status() != not_in_election, "Not currently opted in"); } members.election_opt(member, participating); diff --git a/contracts/eden/src/actions/induct.cpp b/contracts/eden/src/actions/induct.cpp index 93b1d04c2..2d5e7cb49 100644 --- a/contracts/eden/src/actions/induct.cpp +++ b/contracts/eden/src/actions/induct.cpp @@ -147,7 +147,7 @@ namespace eden if (elect_state.exists()) { auto state = elect_state.get(); - if (auto* reg = std::get_if(&state); + if (auto* reg = get_if_derived(&state); reg && members.stats().active_members >= reg->election_threshold) { elections elections{get_self()}; diff --git a/contracts/eden/src/elections.cpp b/contracts/eden/src/elections.cpp index 0fac12fd5..7b4bdbc50 100644 --- a/contracts/eden/src/elections.cpp +++ b/contracts/eden/src/elections.cpp @@ -201,13 +201,13 @@ namespace eden const auto* old_value = state_sing.get_or_null(); if (old_value && *old_value == new_value) return; - if (auto n = std::get_if(&new_value)) + if (auto n = std::get_if(&new_value)) { push_event(election_event_schedule{.election_time = n->start_time, .election_threshold = n->election_threshold}, contract); } - else if (auto n = std::get_if(&new_value)) + else if (auto n = std::get_if(&new_value)) { push_event(election_event_seeding{.election_time = n->seed.end_time, .start_time = n->seed.start_time, @@ -280,30 +280,58 @@ namespace eden return {}; } auto state = state_sing.get(); - if (auto* r = std::get_if(&state)) + if (auto* r = get_if_derived(&state)) { return r->start_time; } - else if (auto* s = std::get_if(&state)) + else if (auto* s = get_if_derived(&state)) { return s->seed.end_time.to_time_point(); } return {}; } + std::uint8_t elections::election_schedule_version() + { + if (!state_sing.exists()) + { + return 1; + } + auto state = state_sing.get(); + if (auto* r = get_if_derived(&state)) + { + return r->election_schedule_version; + } + else if (auto* s = get_if_derived(&state)) + { + return s->election_schedule_version; + } + else if (auto* i = get_if_derived(&state)) + { + return i->election_schedule_version; + } + return 1; + } + void elections::set_next_election_time(eosio::time_point election_time) { auto lock_time = eosio::current_time_point() + eosio::days(30); eosio::check(election_time >= lock_time, "New election time is too close"); + uint8_t sequence = 1; if (state_sing.exists()) { auto state = state_sing.get(); - eosio::check( - std::holds_alternative(state) && - std::get(state).start_time >= lock_time, - "Election cannot be rescheduled"); + bool okay = false; + if (auto r = get_if_derived(&state)) + { + sequence = r->election_schedule_version + 1; + eosio::check(sequence != 0, "Integer overflow: election rescheduled too many times"); + okay = r->start_time >= lock_time; + } + eosio::check(okay, "Election cannot be rescheduled"); } - set_state_sing(current_election_state_registration{election_time, max_active_members + 1}); + set_state_sing( + current_election_state_registration_v1{election_time, max_active_members + 1, sequence}); } void elections::set_time(uint8_t day, const std::string& time) @@ -338,7 +366,7 @@ namespace eden : 0; uint16_t new_threshold = active_members + (active_members + 9) / 10; new_threshold = std::clamp(new_threshold, min_election_threshold, max_active_members); - set_state_sing(current_election_state_registration{ + set_state_sing(current_election_state_registration_v1{ get_election_time(state.election_start_time, origin_time + eosio::days(180)), new_threshold}); } @@ -363,14 +391,16 @@ namespace eden // Ignore events that would trigger an election unless they move // the next election closer. auto current_state = state_sing.get(); - if (auto* current = std::get_if(¤t_state)) + if (auto* current = get_if_derived(¤t_state)) { auto new_start_time = eosio::block_timestamp{ get_election_time(state.election_start_time, now + eosio::days(30))}; if (new_start_time < current->start_time) { - set_state_sing( - current_election_state_registration{new_start_time, max_active_members + 1}); + uint8_t sequence = current->election_schedule_version + 1; + eosio::check(sequence != 0, "Integer overflow: election rescheduled too many times"); + set_state_sing(current_election_state_registration_v1{ + new_start_time, max_active_members + 1, sequence}); } } } @@ -380,7 +410,7 @@ namespace eden { eosio::check(btc_header.data.size() == 80, "Wrong size for BTC block header"); auto state = state_sing.get(); - if (auto* registration = std::get_if(&state)) + if (auto* registration = get_if_derived(&state)) { auto now = eosio::current_block_time(); eosio::block_timestamp seeding_start = @@ -391,10 +421,11 @@ namespace eden .election_time = registration->start_time, }, contract); - state = current_election_state_seeding{ - {.start_time = seeding_start, .end_time = registration->start_time.to_time_point()}}; + state = current_election_state_seeding_v1{ + {{.start_time = seeding_start, .end_time = registration->start_time.to_time_point()}, + registration->election_schedule_version}}; } - if (auto* seeding = std::get_if(&state)) + if (auto* seeding = get_if_derived(&state)) { eosio::input_stream stream{btc_header.data}; seeding->seed.update(stream); @@ -413,13 +444,15 @@ namespace eden void elections::start_election() { - eosio::check(std::holds_alternative(state_sing.get()), - "Election seed not set"); - auto old_state = std::get(state_sing.get()); + auto old_state_variant = state_sing.get(); + auto old_state_ptr = get_if_derived(&old_state_variant); + eosio::check(old_state_ptr != nullptr, "Election seed not set"); + auto& old_state = *old_state_ptr; auto election_start_time = old_state.seed.end_time.to_time_point(); eosio::check(eosio::current_block_time() >= old_state.seed.end_time, "Seeding window is still open"); - set_state_sing(current_election_state_init_voters{0, election_rng{old_state.seed.current}}); + set_state_sing(current_election_state_init_voters_v1{ + 0, election_rng{old_state.seed.current}, {}, 0, old_state.election_schedule_version}); push_event(election_event_end_seeding{.election_time = election_start_time}, contract); // Must happen after the election is started @@ -439,7 +472,7 @@ namespace eden bylaws.new_board(); } - uint32_t elections::randomize_voters(current_election_state_init_voters& state, + uint32_t elections::randomize_voters(current_election_state_init_voters_v0& state, uint32_t max_steps) { members members(contract); @@ -450,18 +483,15 @@ namespace eden { if (iter->status() == member_status::active_member) { - switch (iter->election_participation_status()) + printf("%d, %d\n", iter->election_participation_status(), + state.election_schedule_version); + if (iter->election_participation_status() == state.election_schedule_version) { - case in_election: - { - add_voter(state.rng, 0, state.next_member_idx, iter->account()); - break; - } - case not_in_election: - { - members.set_rank(iter->account(), 0, eosio::name(-1)); - break; - } + add_voter(state.rng, 0, state.next_member_idx, iter->account()); + } + else + { + members.set_rank(iter->account(), 0, eosio::name(-1)); } } state.last_processed = iter->account(); @@ -495,7 +525,7 @@ namespace eden uint32_t elections::prepare_election(uint32_t max_steps) { auto state_variant = state_sing.get(); - if (auto* state = std::get_if(&state_variant)) + if (auto* state = get_if_derived(&state_variant)) { if (max_steps == 0) { @@ -505,7 +535,7 @@ namespace eden state_variant = state_sing.get(); --max_steps; } - if (auto* state = std::get_if(&state_variant)) + if (auto* state = get_if_derived(&state_variant)) { // This needs to happen before any members have their ranks adjusted max_steps = distribute_monthly(contract, max_steps); @@ -966,15 +996,27 @@ namespace eden return false; } + static bool is_election_running(current_election_state_singleton& state_sing) + { + if (!state_sing.exists()) + { + return false; + } + auto state = state_sing.get(); + if (std::holds_alternative(state_sing.get())) + { + return false; + } + if (auto* r = get_if_derived(&state)) + { + return eosio::current_block_time() >= r->start_time; + } + return true; + } + void elections::on_resign(eosio::name member) { - eosio::check( - !state_sing.exists() || - std::holds_alternative(state_sing.get()) || - (std::holds_alternative(state_sing.get()) && - std::get(state_sing.get()).start_time > - eosio::current_block_time()), - "Cannot resign during an election"); + eosio::check(!is_election_running(state_sing), "Cannot resign during an election"); if (remove_from_board(member)) { trigger_election(); @@ -987,8 +1029,7 @@ namespace eden if (iter == vote_tb.end()) { auto current_state = state_sing.get(); - bool valid_state = - !std::holds_alternative(current_state); + bool valid_state = !get_if_derived(¤t_state); election_state_singleton state{contract, default_scope}; auto end_time = std::get(state.get()).last_election_time.to_time_point() + diff --git a/contracts/eden/src/members.cpp b/contracts/eden/src/members.cpp index a7dde1e57..8ddca0292 100644 --- a/contracts/eden/src/members.cpp +++ b/contracts/eden/src/members.cpp @@ -150,7 +150,7 @@ namespace eden .name = name, .status = member_status::active_member, .nft_template_id = row.nft_template_id(), - .election_participation_status = not_in_election}}; + .election_participation_status = 0}}; }); } @@ -160,7 +160,7 @@ namespace eden row.value = std::visit([](auto& v) { return member_v1{v}; }, row.value); row.election_rank() = rank; row.representative() = representative; - row.election_participation_status() = not_in_election; + row.election_participation_status() = 0; }); auto stats = this->stats(); if (representative != eosio::name(-1)) @@ -194,7 +194,8 @@ namespace eden member_tb.modify(member, eosio::same_payer, [&](auto& row) { row.value = std::visit([](auto& v) { return member_v1{v}; }, row.value); - row.election_participation_status() = participating ? in_election : not_in_election; + row.election_participation_status() = + participating ? elections.election_schedule_version() : 0; }); } diff --git a/contracts/eden/tests/test-eden.cpp b/contracts/eden/tests/test-eden.cpp index f74c7f4f7..a773138ce 100644 --- a/contracts/eden/tests/test-eden.cpp +++ b/contracts/eden/tests/test-eden.cpp @@ -718,7 +718,7 @@ TEST_CASE("election") t.electdonate_all(); { eden::current_election_state_singleton state("eden.gm"_n, eden::default_scope); - auto current = std::get(state.get()); + auto current = std::get(state.get()); CHECK(eosio::convert_to_json(current.start_time) == "\"2020-07-04T15:30:00.000\""); } t.skip_to("2020-07-03T15:29:59.500"); @@ -738,6 +738,18 @@ TEST_CASE("election") CHECK(result.board == std::vector{"alice"_n, "egeon"_n, "pip"_n}); } +TEST_CASE("election reschedule") +{ + eden_tester t; + t.genesis(); + t.electdonate_all(); + t.eden_gm.act(s2t("2020-03-02T15:30:01.000")); + t.skip_to(t.next_election_time().to_time_point() - eosio::days(1)); + t.electseed(t.next_election_time().to_time_point() - eosio::days(1)); + t.skip_to(t.next_election_time().to_time_point()); + expect(t.alice.trace(100), "No voters"); +} + TEST_CASE("mid-election induction") { eden_tester t; @@ -951,9 +963,9 @@ TEST_CASE("budget distribution minimum period") t.genesis(); t.set_balance(s2a("36.0000 EOS")); t.run_election(); - t.electdonate_all(); t.set_balance(s2a("100000.0000 EOS")); t.eden_gm.act(s2t("2020-09-02T15:30:01.000")); + t.electdonate_all(); t.run_election(); std::map expected{ {s2t("2020-07-04T15:30:00.000"), s2a("1.8000 EOS")}, @@ -969,7 +981,6 @@ TEST_CASE("budget distribution exact") t.genesis(); t.set_balance(s2a("36.0000 EOS")); t.run_election(); - t.electdonate_all(); t.set_balance(s2a("1000.0000 EOS")); t.eden_gm.act(s2t("2020-09-02T15:30:00.000")); t.run_election(); @@ -986,7 +997,6 @@ TEST_CASE("budget distribution underflow") t.genesis(); t.set_balance(s2a("36.0000 EOS")); t.run_election(); - t.electdonate_all(); t.set_balance(s2a("1000.0000 EOS")); t.eden_gm.act(s2t("2020-09-02T15:30:01.000")); t.run_election(); From b1ec81a7f520f56636e364cd8fca055cdfbcfee3 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Fri, 12 Nov 2021 18:21:55 -0500 Subject: [PATCH 21/54] sessions --- contracts/eden/include/sessions.hpp | 2 ++ contracts/eden/src/actions/genesis.cpp | 1 + contracts/eden/src/actions/sessions.cpp | 19 +++++++++++++++++++ contracts/eden/src/members.cpp | 3 +++ 4 files changed, 25 insertions(+) diff --git a/contracts/eden/include/sessions.hpp b/contracts/eden/include/sessions.hpp index c709f379e..6b6ca9133 100644 --- a/contracts/eden/include/sessions.hpp +++ b/contracts/eden/include/sessions.hpp @@ -43,6 +43,8 @@ namespace eden eosio::const_mem_fun>>; uint32_t gc_sessions(eosio::name contract, uint32_t remaining); + void clearall_sessions(eosio::name contract); + void remove_sessions(eosio::name contract, eosio::name eden_account); struct session_info { diff --git a/contracts/eden/src/actions/genesis.cpp b/contracts/eden/src/actions/genesis.cpp index c8f6bde3c..41fc48112 100644 --- a/contracts/eden/src/actions/genesis.cpp +++ b/contracts/eden/src/actions/genesis.cpp @@ -27,6 +27,7 @@ namespace eden bylaws{get_self()}.clear_all(); encrypt{get_self(), "induction"_n}.clear_all(); encrypt{get_self(), "election"_n}.clear_all(); + clearall_sessions(get_self()); } void eden::gensetexpire(uint64_t induction_id, eosio::time_point new_expiration) diff --git a/contracts/eden/src/actions/sessions.cpp b/contracts/eden/src/actions/sessions.cpp index 4d401b3c9..927c7f0d7 100644 --- a/contracts/eden/src/actions/sessions.cpp +++ b/contracts/eden/src/actions/sessions.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -37,6 +38,24 @@ namespace eden return remaining; } + void clearall_sessions(eosio::name contract) + { + sessions_table_type table(contract, default_scope); + while (table.begin() != table.end()) + table.erase(table.begin()); + } + + void remove_sessions(eosio::name contract, eosio::name eden_account) + { + sessions_table_type table(contract, default_scope); + auto it = table.find(eden_account.value); + if (it == table.end()) + return; + for (auto& session : it->sessions()) + push_event(session_del_event{eden_account, session.key}, contract); + table.erase(it); + } + void eden::newsession(eosio::name eden_account, const eosio::public_key& key, eosio::block_timestamp expiration, diff --git a/contracts/eden/src/members.cpp b/contracts/eden/src/members.cpp index a7dde1e57..59fe7b8f5 100644 --- a/contracts/eden/src/members.cpp +++ b/contracts/eden/src/members.cpp @@ -1,5 +1,6 @@ #include #include +#include namespace eden { @@ -102,6 +103,7 @@ namespace eden break; } member_stats.set(stats, contract); + remove_sessions(contract, iter->account()); return member_tb.erase(iter); } @@ -117,6 +119,7 @@ namespace eden const auto& member = member_tb.get(account.value); if (member.status() == member_status::pending_membership) { + remove_sessions(contract, account); member_tb.erase(member); auto stats = this->stats(); eosio::check(stats.pending_members != 0, "Integer overflow"); From 479bbdeda200c689e68b90c2d23066be21253cb1 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Mon, 15 Nov 2021 17:47:20 -0500 Subject: [PATCH 22/54] sessions --- contracts/eden/src/eden-micro-chain.cpp | 125 ++++- .../tests/data/contract-auth-elect.expected | 485 ++++++++++++++++++ .../tests/data/contract-auth-induct.expected | 57 ++ .../eden/tests/data/contract-auth.expected | 46 ++ contracts/eden/tests/test-eden.cpp | 9 + libraries/clchain/include/clchain/graphql.hpp | 2 + 6 files changed, 723 insertions(+), 1 deletion(-) create mode 100644 contracts/eden/tests/data/contract-auth-elect.expected create mode 100644 contracts/eden/tests/data/contract-auth-induct.expected create mode 100644 contracts/eden/tests/data/contract-auth.expected diff --git a/contracts/eden/src/eden-micro-chain.cpp b/contracts/eden/src/eden-micro-chain.cpp index 0264514e7..e534e6c9b 100644 --- a/contracts/eden/src/eden-micro-chain.cpp +++ b/contracts/eden/src/eden-micro-chain.cpp @@ -32,6 +32,23 @@ const eosio::name account_max = eosio::name{~uint64_t(0)}; const eosio::block_timestamp block_timestamp_min = eosio::block_timestamp{0}; const eosio::block_timestamp block_timestamp_max = eosio::block_timestamp{~uint32_t(0)}; +const eosio::ecc_public_key ecc_public_key_min = { + char(0x80), char(0x80), char(0x80), char(0x80), char(0x80), char(0x80), char(0x80), + char(0x80), char(0x80), char(0x80), char(0x80), char(0x80), char(0x80), char(0x80), + char(0x80), char(0x80), char(0x80), char(0x80), char(0x80), char(0x80), char(0x80), + char(0x80), char(0x80), char(0x80), char(0x80), char(0x80), char(0x80), char(0x80), + char(0x80), char(0x80), char(0x80), char(0x80), char(0x80), +}; + +const eosio::ecc_public_key ecc_public_key_max = { + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, +}; + +const eosio::public_key public_key_min_k1{std::in_place_index_t<0>{}, ecc_public_key_min}; +const eosio::public_key public_key_max_r1{std::in_place_index_t<1>{}, ecc_public_key_max}; + // TODO: switch to uint64_t (js BigInt) after we upgrade to nodejs >= 15 extern "C" void __wasm_call_ctors(); [[clang::export_name("initialize")]] void initialize(uint32_t eden_account_low, @@ -158,6 +175,7 @@ enum tables balance_history_table, induction_table, member_table, + session_table, election_table, election_round_table, election_group_table, @@ -366,6 +384,23 @@ using member_index = mic, ordered_by_createdAt>; +using SessionKey = std::tuple; + +struct session_object : public chainbase::object +{ + CHAINBASE_DEFAULT_CONSTRUCTOR(session_object) + + id_type id; + eosio::name eden_account; + eosio::public_key key; + eosio::block_timestamp expiration; + std::string description; + + SessionKey by_pk() const { return {eden_account, key}; } +}; +using session_index = + mic, ordered_by_pk>; + struct election_object : public chainbase::object { CHAINBASE_DEFAULT_CONSTRUCTOR(election_object) @@ -520,6 +555,7 @@ struct database chainbase::generic_index balance_history; chainbase::generic_index inductions; chainbase::generic_index members; + chainbase::generic_index sessions; chainbase::generic_index elections; chainbase::generic_index election_rounds; chainbase::generic_index election_groups; @@ -535,6 +571,7 @@ struct database db.add_index(balance_history); db.add_index(inductions); db.add_index(members); + db.add_index(sessions); db.add_index(elections); db.add_index(election_rounds); db.add_index(election_groups); @@ -794,6 +831,17 @@ Member Balance::account() const return *get_member(_account, true); } +struct Session +{ + const session_object* session; + + auto member() const { return get_member(session->eden_account); } + const auto& key() const { return session->key; } + const auto& expiration() const { return session->expiration; } + const auto& description() const { return session->description; } +}; +EOSIO_REFLECT2(Session, member, key, expiration, description) + struct InductionEndorsingMemberStatus { eosio::name endorserAccount; @@ -1245,6 +1293,7 @@ void clearall() clear_table(db.balance_history); clear_table(db.inductions); clear_table(db.members); + clear_table(db.sessions); clear_table(db.elections); clear_table(db.election_rounds); clear_table(db.election_groups); @@ -1254,6 +1303,11 @@ void clearall() clear_table(db.nfts); } +void delsession(eosio::name eden_account, const eosio::public_key& key) +{ + // ignored; events handle session creation and deletion +} + eosio::asset add_balance(eosio::name account, const eosio::asset& delta) { eosio::asset result; @@ -1454,6 +1508,14 @@ void inductprofil(uint64_t id, eden::new_member_profile profile) }); } +void inductmeetin(eosio::name account, + uint64_t id, + const std::vector& keys, + const eosio::bytes& data, + const std::optional& old_data) +{ +} + void inductvideo(eosio::name account, uint64_t id, std::string video) { modify(db.inductions, id, [&](auto& obj) { @@ -1568,6 +1630,14 @@ void electvote(uint8_t round, eosio::name voter, eosio::name candidate) db.votes.modify(vote, [&](auto& vote) { vote.candidate = candidate; }); } +void electmeeting(eosio::name account, + uint8_t round, + const std::vector& keys, + const eosio::bytes& data, + const std::optional& old_data) +{ +} + void electvideo(uint8_t round, eosio::name voter, const std::string& video) { auto& election_idx = db.elections.get(); @@ -1819,6 +1889,21 @@ void handle_event(const eden::distribution_event_fund& event) }); } +void handle_event(const eden::session_new_event& event) +{ + db.sessions.emplace([&](auto& session) { + session.eden_account = event.eden_account; + session.key = event.key; + session.expiration = event.expiration; + session.description = event.description; + }); +} + +void handle_event(const eden::session_del_event& event) +{ + remove_if_exists(db.sessions, SessionKey{event.eden_account, event.key}); +} + void handle_event(const auto& event) {} void handle_event(const action_context& context, const auto& event) @@ -1896,7 +1981,7 @@ void execsession(const action_context& context, eosio::input_stream& s) auto name = eden::actions::get_name_for_session_action(index); if (!dispatch(name, context, s)) // fatal because this throws off the rest of the stream - eosio::check(false, "execsession dispatch failed for " + std::to_string(index) + " " + + eosio::check(false, "execsession: action not found: " + std::to_string(index) + " " + name.to_string()); } eosio::check(!s.remaining(), "unpack error (extra data) within execsession"); @@ -1908,6 +1993,8 @@ bool dispatch(eosio::name action_name, const action_context& context, eosio::inp execsession(context, s); else if (action_name == "clearall"_n) call(clearall, context, s); + else if (action_name == "delsession"_n) + call(delsession, context, s); else if (action_name == "withdraw"_n) call(withdraw, context, s); else if (action_name == "donate"_n) @@ -1926,6 +2013,8 @@ bool dispatch(eosio::name action_name, const action_context& context, eosio::inp call(inductinit, context, s); else if (action_name == "inductprofil"_n) call(inductprofil, context, s); + else if (action_name == "inductmeetin"_n) + call(inductmeetin, context, s); else if (action_name == "inductvideo"_n) call(inductvideo, context, s); else if (action_name == "inductcancel"_n) @@ -1940,6 +2029,8 @@ bool dispatch(eosio::name action_name, const action_context& context, eosio::inp call(electopt, context, s); else if (action_name == "electvote"_n) call(electvote, context, s); + else if (action_name == "electmeeting"_n) + call(electmeeting, context, s); else if (action_name == "electvideo"_n) call(electvideo, context, s); else @@ -2237,6 +2328,11 @@ constexpr const char MemberEdge_name[] = "MemberEdge"; using MemberConnection = clchain::Connection>; +constexpr const char SessionConnection_name[] = "SessionConnection"; +constexpr const char SessionEdge_name[] = "SessionEdge"; +using SessionConnection = clchain::Connection< + clchain::ConnectionConfig>; + constexpr const char ElectionConnection_name[] = "ElectionConnection"; constexpr const char ElectionEdge_name[] = "ElectionEdge"; using ElectionConnection = clchain::Connection< @@ -2330,6 +2426,32 @@ struct Query [](auto& members, auto key) { return members.upper_bound(key); }); } + SessionConnection sessions(std::optional gt, + std::optional ge, + std::optional lt, + std::optional le, + std::optional first, + std::optional last, + std::optional before, + std::optional after) const + { + return clchain::make_connection( + gt ? std::optional{SessionKey{*gt, public_key_max_r1}} // + : std::nullopt, // + ge ? std::optional{SessionKey{*ge, public_key_min_k1}} // + : std::nullopt, // + lt ? std::optional{SessionKey{*lt, public_key_min_k1}} // + : std::nullopt, // + le ? std::optional{SessionKey{*le, public_key_max_r1}} // + : std::nullopt, // + first, last, before, after, // + db.sessions.get(), // + [](auto& obj) { return obj.by_pk(); }, // + [](auto& obj) { return Session{&obj}; }, + [](auto& sessions, auto key) { return sessions.lower_bound(key); }, + [](auto& sessions, auto key) { return sessions.upper_bound(key); }); + } + InductionConnection inductions(std::optional gt, std::optional ge, std::optional lt, @@ -2423,6 +2545,7 @@ EOSIO_REFLECT2( method(balances, "gt", "ge", "lt", "le", "first", "last", "before", "after"), method(members, "gt", "ge", "lt", "le", "first", "last", "before", "after"), method(membersByCreatedAt, "gt", "ge", "lt", "le", "first", "last", "before", "after"), + method(sessions, "gt", "ge", "lt", "le", "first", "last", "before", "after"), method(inductions, "gt", "ge", "lt", "le", "first", "last", "before", "after"), method(inductionsByCreatedAt, "gt", "ge", "lt", "le", "first", "last", "before", "after"), method(elections, "gt", "ge", "lt", "le", "first", "last", "before", "after"), diff --git a/contracts/eden/tests/data/contract-auth-elect.expected b/contracts/eden/tests/data/contract-auth-elect.expected new file mode 100644 index 000000000..82f6e8e82 --- /dev/null +++ b/contracts/eden/tests/data/contract-auth-elect.expected @@ -0,0 +1,485 @@ +[ + "set_pool_event", + { + "pool": "master", + "monthly_distribution_pct": 5 + } +] +[ + "election_event_schedule", + { + "election_time": "2020-07-04T15:30:00.000", + "election_threshold": 1000 + } +] +[ + "session_new_event", + { + "eden_account": "alice", + "key": "PUB_K1_665ajq1JUMwWH3bHcRxxTqiZBZBc6CakwUfLkZJxRqp4vAtJaV", + "expiration": "2020-03-31T00:01:40.500", + "description": "" + } +] +[ + "session_new_event", + { + "eden_account": "pip", + "key": "PUB_K1_8YQhKe3x1xTA1KHmkBPznWqa3UGQsaHTUMkJJtcds9giKNsHGv", + "expiration": "2020-03-31T00:01:40.500", + "description": "" + } +] +[ + "session_new_event", + { + "eden_account": "egeon", + "key": "PUB_K1_8kBx4XYj3zZ3Z1Sb8vdq43ursVTebfcShKMDUymiA2cteLVLM1", + "expiration": "2020-03-31T00:01:40.500", + "description": "" + } +] +[ + "election_event_begin", + { + "election_time": "2020-07-04T15:30:00.000" + } +] +[ + "election_event_seeding", + { + "election_time": "2020-07-04T15:30:00.000", + "start_time": "2020-07-03T15:30:00.000", + "end_time": "2020-07-04T15:30:00.000", + "seed": "79BC69E1EF2D91BDFD54CF74E8B3784EEDC110F7A0BA571297FAD90DEC3B97C7" + } +] +[ + "election_event_end_seeding", + { + "election_time": "2020-07-04T15:30:00.000" + } +] +[ + "distribution_event_schedule", + { + "distribution_time": "2020-07-04T15:30:00.000" + } +] +[ + "distribution_event_reserve", + { + "distribution_time": "2020-07-04T15:30:00.000", + "pool": "master", + "target_amount": "51.5000 EOS" + } +] +[ + "distribution_event_schedule", + { + "distribution_time": "2020-08-03T15:30:00.000" + } +] +[ + "election_event_config_summary", + { + "election_time": "2020-07-04T15:30:00.000", + "num_rounds": 3, + "num_participants": 103 + } +] +[ + "election_event_create_round", + { + "election_time": "2020-07-04T15:30:00.000", + "round": 0, + "requires_voting": true, + "num_participants": 103, + "num_groups": 25 + } +] +[ + "election_event_create_group", + { + "election_time": "2020-07-04T15:30:00.000", + "round": 0, + "voters": [ + "edenmember2p", + "edenmember43", + "edenmember2l", + "edenmember1m", + "edenmember3q" + ] + } +] +[ + "election_event_create_group", + { + "election_time": "2020-07-04T15:30:00.000", + "round": 0, + "voters": [ + "edenmember44", + "edenmember3t", + "edenmember12", + "edenmember22", + "edenmember1n" + ] + } +] +[ + "election_event_create_group", + { + "election_time": "2020-07-04T15:30:00.000", + "round": 0, + "voters": [ + "edenmember21", + "edenmember3d", + "edenmember13", + "edenmember3w", + "edenmember2q" + ] + } +] +[ + "election_event_create_group", + { + "election_time": "2020-07-04T15:30:00.000", + "round": 0, + "voters": [ + "edenmember2n", + "edenmember3g", + "edenmember1o", + "edenmember2t" + ] + } +] +[ + "election_event_create_group", + { + "election_time": "2020-07-04T15:30:00.000", + "round": 0, + "voters": [ + "edenmember3p", + "edenmember1k", + "edenmember2b", + "edenmember3z" + ] + } +] +[ + "election_event_create_group", + { + "election_time": "2020-07-04T15:30:00.000", + "round": 0, + "voters": [ + "edenmember3a", + "edenmember1v", + "edenmember3s", + "edenmember15" + ] + } +] +[ + "election_event_create_group", + { + "election_time": "2020-07-04T15:30:00.000", + "round": 0, + "voters": [ + "edenmember1a", + "edenmember3j", + "edenmember1t", + "edenmember1z" + ] + } +] +[ + "election_event_create_group", + { + "election_time": "2020-07-04T15:30:00.000", + "round": 0, + "voters": [ + "edenmember3b", + "edenmember23", + "edenmember2m", + "edenmember2i" + ] + } +] +[ + "election_event_create_group", + { + "election_time": "2020-07-04T15:30:00.000", + "round": 0, + "voters": [ + "edenmember3x", + "edenmember3h", + "edenmember24", + "edenmember1q" + ] + } +] +[ + "election_event_create_group", + { + "election_time": "2020-07-04T15:30:00.000", + "round": 0, + "voters": [ + "edenmember1j", + "alice", + "edenmember1s", + "edenmember1b" + ] + } +] +[ + "election_event_create_group", + { + "election_time": "2020-07-04T15:30:00.000", + "round": 0, + "voters": [ + "edenmember1h", + "edenmember4b", + "edenmember41", + "edenmember3u" + ] + } +] +[ + "election_event_create_group", + { + "election_time": "2020-07-04T15:30:00.000", + "round": 0, + "voters": [ + "edenmember1x", + "edenmember3l", + "edenmember35", + "edenmember1f" + ] + } +] +[ + "election_event_create_group", + { + "election_time": "2020-07-04T15:30:00.000", + "round": 0, + "voters": [ + "egeon", + "edenmember2u", + "edenmember2s", + "edenmember25" + ] + } +] +[ + "election_event_create_group", + { + "election_time": "2020-07-04T15:30:00.000", + "round": 0, + "voters": [ + "edenmember2d", + "edenmember2k", + "edenmember3m", + "edenmember3v" + ] + } +] +[ + "election_event_create_group", + { + "election_time": "2020-07-04T15:30:00.000", + "round": 0, + "voters": [ + "edenmember3o", + "edenmember33", + "edenmember3n", + "edenmember45" + ] + } +] +[ + "election_event_create_group", + { + "election_time": "2020-07-04T15:30:00.000", + "round": 0, + "voters": [ + "edenmember4a", + "edenmember2v", + "edenmember2o", + "edenmember1y" + ] + } +] +[ + "election_event_create_group", + { + "election_time": "2020-07-04T15:30:00.000", + "round": 0, + "voters": [ + "edenmember2f", + "edenmember2e", + "edenmember1i", + "edenmember2y" + ] + } +] +[ + "election_event_create_group", + { + "election_time": "2020-07-04T15:30:00.000", + "round": 0, + "voters": [ + "edenmember1c", + "edenmember32", + "edenmember3e", + "edenmember3k" + ] + } +] +[ + "election_event_create_group", + { + "election_time": "2020-07-04T15:30:00.000", + "round": 0, + "voters": [ + "edenmember3y", + "edenmember2w", + "edenmember2r", + "edenmember2x" + ] + } +] +[ + "election_event_create_group", + { + "election_time": "2020-07-04T15:30:00.000", + "round": 0, + "voters": [ + "edenmember3c", + "edenmember34", + "edenmember1e", + "edenmember1g" + ] + } +] +[ + "election_event_create_group", + { + "election_time": "2020-07-04T15:30:00.000", + "round": 0, + "voters": [ + "edenmember2j", + "edenmember2h", + "edenmember2a", + "edenmember3f" + ] + } +] +[ + "election_event_create_group", + { + "election_time": "2020-07-04T15:30:00.000", + "round": 0, + "voters": [ + "edenmember1p", + "edenmember31", + "edenmember3i", + "edenmember1d" + ] + } +] +[ + "election_event_create_group", + { + "election_time": "2020-07-04T15:30:00.000", + "round": 0, + "voters": [ + "edenmember11", + "edenmember42", + "edenmember3r", + "edenmember1r" + ] + } +] +[ + "election_event_create_group", + { + "election_time": "2020-07-04T15:30:00.000", + "round": 0, + "voters": [ + "edenmember1l", + "edenmember14", + "edenmember1w", + "pip" + ] + } +] +[ + "election_event_create_group", + { + "election_time": "2020-07-04T15:30:00.000", + "round": 0, + "voters": [ + "edenmember2c", + "edenmember2z", + "edenmember1u", + "edenmember2g" + ] + } +] +[ + "election_event_begin_round_voting", + { + "election_time": "2020-07-04T15:30:00.000", + "round": 0, + "voting_begin": "2020-07-04T15:40:00.000", + "voting_end": "2020-07-04T16:40:00.000" + } +] +[ + "session_del_event", + { + "eden_account": "alice", + "key": "PUB_K1_665ajq1JUMwWH3bHcRxxTqiZBZBc6CakwUfLkZJxRqp4vAtJaV" + } +] +[ + "session_del_event", + { + "eden_account": "egeon", + "key": "PUB_K1_8kBx4XYj3zZ3Z1Sb8vdq43ursVTebfcShKMDUymiA2cteLVLM1" + } +] +[ + "session_del_event", + { + "eden_account": "pip", + "key": "PUB_K1_8YQhKe3x1xTA1KHmkBPznWqa3UGQsaHTUMkJJtcds9giKNsHGv" + } +] +[ + "session_new_event", + { + "eden_account": "alice", + "key": "PUB_K1_665ajq1JUMwWH3bHcRxxTqiZBZBc6CakwUfLkZJxRqp4vAtJaV", + "expiration": "2020-10-02T15:40:00.000", + "description": "" + } +] +[ + "session_new_event", + { + "eden_account": "pip", + "key": "PUB_K1_8YQhKe3x1xTA1KHmkBPznWqa3UGQsaHTUMkJJtcds9giKNsHGv", + "expiration": "2020-10-02T15:40:00.000", + "description": "" + } +] +[ + "session_new_event", + { + "eden_account": "egeon", + "key": "PUB_K1_8kBx4XYj3zZ3Z1Sb8vdq43ursVTebfcShKMDUymiA2cteLVLM1", + "expiration": "2020-10-02T15:40:00.000", + "description": "" + } +] diff --git a/contracts/eden/tests/data/contract-auth-induct.expected b/contracts/eden/tests/data/contract-auth-induct.expected new file mode 100644 index 000000000..51081072e --- /dev/null +++ b/contracts/eden/tests/data/contract-auth-induct.expected @@ -0,0 +1,57 @@ +[ + "set_pool_event", + { + "pool": "master", + "monthly_distribution_pct": 5 + } +] +[ + "election_event_schedule", + { + "election_time": "2020-07-04T15:30:00.000", + "election_threshold": 1000 + } +] +[ + "session_new_event", + { + "eden_account": "alice", + "key": "PUB_K1_665ajq1JUMwWH3bHcRxxTqiZBZBc6CakwUfLkZJxRqp4vAtJaV", + "expiration": "2020-03-31T00:00:00.500", + "description": "" + } +] +[ + "session_new_event", + { + "eden_account": "pip", + "key": "PUB_K1_8YQhKe3x1xTA1KHmkBPznWqa3UGQsaHTUMkJJtcds9giKNsHGv", + "expiration": "2020-03-31T00:00:00.500", + "description": "" + } +] +[ + "session_new_event", + { + "eden_account": "egeon", + "key": "PUB_K1_8kBx4XYj3zZ3Z1Sb8vdq43ursVTebfcShKMDUymiA2cteLVLM1", + "expiration": "2020-03-31T00:00:00.500", + "description": "" + } +] +[ + "session_new_event", + { + "eden_account": "bertie", + "key": "PUB_K1_5iALbhfqEZvqkUifUGbfMQSFnd1ui8ZsXVHT23XWh1HM4B1jNS", + "expiration": "2020-03-31T00:00:00.500", + "description": "" + } +] +[ + "session_del_event", + { + "eden_account": "bertie", + "key": "PUB_K1_5iALbhfqEZvqkUifUGbfMQSFnd1ui8ZsXVHT23XWh1HM4B1jNS" + } +] diff --git a/contracts/eden/tests/data/contract-auth.expected b/contracts/eden/tests/data/contract-auth.expected new file mode 100644 index 000000000..b702f86ee --- /dev/null +++ b/contracts/eden/tests/data/contract-auth.expected @@ -0,0 +1,46 @@ +[ + "set_pool_event", + { + "pool": "master", + "monthly_distribution_pct": 5 + } +] +[ + "election_event_schedule", + { + "election_time": "2020-07-04T15:30:00.000", + "election_threshold": 1000 + } +] +[ + "session_new_event", + { + "eden_account": "alice", + "key": "PUB_K1_665ajq1JUMwWH3bHcRxxTqiZBZBc6CakwUfLkZJxRqp4vAtJaV", + "expiration": "2020-03-31T00:00:00.500", + "description": "four score and seven" + } +] +[ + "session_new_event", + { + "eden_account": "alice", + "key": "PUB_K1_8VWTR1mogYHEd9HJxgG2Tj3GbPghrnJqMfWfdHbTE11BLxqvo3", + "expiration": "2020-03-31T00:00:00.500", + "description": "another session" + } +] +[ + "session_del_event", + { + "eden_account": "alice", + "key": "PUB_K1_665ajq1JUMwWH3bHcRxxTqiZBZBc6CakwUfLkZJxRqp4vAtJaV" + } +] +[ + "session_del_event", + { + "eden_account": "alice", + "key": "PUB_K1_8VWTR1mogYHEd9HJxgG2Tj3GbPghrnJqMfWfdHbTE11BLxqvo3" + } +] diff --git a/contracts/eden/tests/test-eden.cpp b/contracts/eden/tests/test-eden.cpp index b5c4bfef6..dc5323fed 100644 --- a/contracts/eden/tests/test-eden.cpp +++ b/contracts/eden/tests/test-eden.cpp @@ -1304,6 +1304,9 @@ TEST_CASE("contract-auth") "Recovered session key PUB_K1_8VWTR1mogYHEd9HJxgG2Tj3GbPghrnJqMfWfdHbTE11BLxqvo3 " "is either expired or not found", sact("alice"_n, alice_session_2_pub_key)); + + t.write_dfuse_history("dfuse-contract-auth.json"); + CompareFile{"contract-auth"}.write_events(t.chain).compare(); } // TEST_CASE("contract-auth") TEST_CASE("contract-auth-induct") @@ -1374,6 +1377,9 @@ TEST_CASE("contract-auth-induct") sact("alice"_n, 1234)); t.execsession(pip_session_priv_key, "pip"_n, 4, nullptr, sact("pip"_n, 1234)); + + t.write_dfuse_history("dfuse-contract-auth-induct.json"); + CompareFile{"contract-auth-induct"}.write_events(t.chain).compare(); } // TEST_CASE("contract-auth-induct") TEST_CASE("contract-auth-elect") @@ -1434,4 +1440,7 @@ TEST_CASE("contract-auth-elect") t.execsession( alice_session_priv_key, "alice"_n, 1, nullptr, sact(0, "alice"_n, "Qmb7WmZiSDXss5HfuKfoSf6jxTDrHzr8AoAUDeDMLNDuws")); + + t.write_dfuse_history("dfuse-contract-auth-elect.json"); + CompareFile{"contract-auth-elect"}.write_events(t.chain).compare(); } // TEST_CASE("contract-auth-elect") diff --git a/libraries/clchain/include/clchain/graphql.hpp b/libraries/clchain/include/clchain/graphql.hpp index cc92af90b..dd0509697 100644 --- a/libraries/clchain/include/clchain/graphql.hpp +++ b/libraries/clchain/include/clchain/graphql.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,7 @@ namespace eosio inline constexpr bool use_json_string_for_gql(symbol_code*) { return true; } inline constexpr bool use_json_string_for_gql(symbol*) { return true; } inline constexpr bool use_json_string_for_gql(asset*) { return true; } + inline constexpr bool use_json_string_for_gql(public_key*) { return true; } template constexpr bool use_json_string_for_gql(fixed_bytes*) From cc96be90aa536d2482c124418f1cccde7715ef73 Mon Sep 17 00:00:00 2001 From: SparkPlug0025 <79721020+sparkplug0025@users.noreply.github.com> Date: Wed, 17 Nov 2021 08:33:14 -0500 Subject: [PATCH 23/54] Ephemeral chain instructions (#608) * Ephemeral chain instructions * fix typo * Proper nodeos waiting command --- .github/workflows/build.yml | 1 + README.md | 61 ++++++++++++++++++++++++++++++++++++ scripts/eden_chain_runner.sh | 6 ++++ 3 files changed, 68 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bd611b909..e780458ea 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -353,6 +353,7 @@ jobs: - "tsconfig.build.json" - "tsconfig.json" - "yarn.lock" + - "scripts/eden_chain_runner.sh" - "packages/**" - "contracts/**" diff --git a/README.md b/README.md index 4a20985d4..1fee9a04f 100644 --- a/README.md +++ b/README.md @@ -61,3 +61,64 @@ wget https://nodejs.org/dist/v14.16.0/node-v14.16.0-linux-x64.tar.xz tar xf node-v14.16.0-linux-x64.tar.xz npm i -g yarn ``` + +### Running Eden with Ephemeral Chains Locally + +Ephemeral chains are instances of the EOS blockchain spawned by `nodeos` locally, with manipulated data from our chain runners, eg: [Basic Genesis Runner](contracts/eden/tests/run-genesis.cpp) or [Full Election Runner](contracts/eden/tests/run-full-election.cpp). By running a ephemeral chain you are in full control of the blockchain, giving you more flexibility to test the Eden contracts. + +#### Get the executables + +You will need to build the repo locally by following the below **build** steps. If you don't have a proper C++ environment setup you can download it from our current [main branch artifacts](https://github.com/eoscommunity/Eden/actions/workflows/build.yml?query=branch%3Amain). + +If you built locally, you can skip these steps. + +**Downloading the executables** + +- Open our [main branch builds](https://github.com/eoscommunity/Eden/actions/workflows/build.yml?query=branch%3Amain). +- Click in the most recent successful one +- Scroll down to the artifacts section +- Download the following files: + - Eden Microchain + - Ephemeral Eden Chains Runners + - clsdk +- From the root of this repo, run the following commands: + +```sh +mkdir build +# unzip all of the above artifact files in this build folder +cd build +tar -xvf clsdk-ubuntu-20-04.tar.gz clsdk/bin +cp ../scripts/eden_chain_runner.sh ./ +``` + +Now you have all the files needed for running the ephemeral chain inside the `build` folder. + +If you are on a Linux machine compatible with Ubuntu arch you can spin it up by just running: `./eden_chain_runner.sh run-genesis.wasm 1` + +Otherwise you can spin it up with the following docker command: + +```sh +docker run --name eden-genesis \ + -v "$(pwd)":/app \ + -w /app \ + -p 8080:8080 -p 8888:8888 \ + -d -it ghcr.io/eoscommunity/eden-builder:sub-chain \ + bash ./eden_chain_runner.sh run-genesis.wasm 1 +``` + +To see if the chain is running successfully you can execute `cleos get info` or watch the nodeos logs: `tail -fn +1 eden-runner.log` + +With the ephemeral chain running you can just spin up our local environment with: + +```sh +yarn +NODE_ENV=test yarn build --stream +NODE_ENV=test yarn start --stream +open http://localhost:3000 +``` + +**Re-running ephemeral chains** + +Running the above commands again will just setup a brand new chain! Just watch out to kill nodeos and unlock your keos wallet if built locally or remove your docker container. Also don't forget to restart the `yarn` environment because the blocks state needs to be refreshed. + +In the above instructions we ran a simple genesis case with 3 inducted members, but you can also try `run-full-election.wasm` to see a community with more than 100 members with chief delegates already elected. diff --git a/scripts/eden_chain_runner.sh b/scripts/eden_chain_runner.sh index fcdab550b..d7202d55a 100755 --- a/scripts/eden_chain_runner.sh +++ b/scripts/eden_chain_runner.sh @@ -14,9 +14,15 @@ cleos wallet import --private-key $CONTRACTS_PKS || true echo "Executing $RUNNER" cltester -v $RUNNER > eden-runner.log 2>&1 & +until cleos get info | grep -m 1 "chain_id"; do + echo "Waiting for nodeos to be responsive..." + sleep 1 +done sleep 5 cleos set abi eosio.token token.abi cleos set abi atomicassets atomicassets.abi cleos set abi atomicmarket atomicmarket.abi cleos set abi eden.gm eden.abi + +if [ -n "$2" ]; then tail -fn +1 eden-runner.log; fi From 5c508c0cba0690219acb5d69f616eb58740289b7 Mon Sep 17 00:00:00 2001 From: SparkPlug0025 <79721020+sparkplug0025@users.noreply.github.com> Date: Thu, 18 Nov 2021 08:55:51 -0500 Subject: [PATCH 24/54] Add induction check on donate and fix microchain (#609) * Add induction check on donate and fix microchain * Address PR review items * Refines migration checks on microchain * Fix migration event triggers --- contracts/eden/include/events.hpp | 10 ++++- contracts/eden/include/inductions.hpp | 2 +- contracts/eden/include/migrations.hpp | 8 +++- contracts/eden/src/actions/induct.cpp | 1 + contracts/eden/src/eden-micro-chain.cpp | 44 ++++++++++++++----- contracts/eden/src/migrations.cpp | 9 ++-- .../eden/tests/data/test-election.expected | 6 +++ 7 files changed, 62 insertions(+), 18 deletions(-) diff --git a/contracts/eden/include/events.hpp b/contracts/eden/include/events.hpp index b317acb1c..bd7769995 100644 --- a/contracts/eden/include/events.hpp +++ b/contracts/eden/include/events.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -30,6 +31,12 @@ namespace eden // // election_event_end + struct migration_event + { + eosio::varuint32 index; + }; + EOSIO_REFLECT(migration_event, index) + struct election_event_schedule { eosio::block_timestamp election_time; @@ -225,7 +232,8 @@ namespace eden distribution_event_return_excess, distribution_event_fund, distribution_event_end, - distribution_event_return>; + distribution_event_return, + migration_event>; void push_event(const event& e, eosio::name self); void send_events(eosio::name self); diff --git a/contracts/eden/include/inductions.hpp b/contracts/eden/include/inductions.hpp index 18499f2e2..c1c6c9f91 100644 --- a/contracts/eden/include/inductions.hpp +++ b/contracts/eden/include/inductions.hpp @@ -144,7 +144,6 @@ namespace eden void check_new_induction(eosio::name invitee, eosio::name inviter) const; bool is_valid_induction(const induction& induction) const; - void check_valid_induction(const induction& induction) const; void validate_profile(const new_member_profile& new_member_profile) const; void validate_video(const std::string& video) const; void check_valid_endorsers(eosio::name inviter, @@ -165,6 +164,7 @@ namespace eden const induction& get_induction(uint64_t id) const; const induction& get_endorsed_induction(eosio::name invitee) const; bool has_induction(eosio::name invitee) const; + void check_valid_induction(const induction& induction) const; void initialize_induction(uint64_t id, eosio::name inviter, diff --git a/contracts/eden/include/migrations.hpp b/contracts/eden/include/migrations.hpp index 1e3d779cb..89834f224 100644 --- a/contracts/eden/include/migrations.hpp +++ b/contracts/eden/include/migrations.hpp @@ -18,11 +18,17 @@ namespace eden }; EOSIO_REFLECT(no_migration<0>); EOSIO_REFLECT(no_migration<1>); + EOSIO_REFLECT(no_migration<2>); + + // No-op migration to notify history the fix for checking expired inductions on inductdonate + using fix_inductdonate_expiration_check = no_migration<2>; + using migration_variant = std::variant, migrate_member_v0, - no_migration<1>>; + no_migration<1>, + fix_inductdonate_expiration_check>; using migration_singleton = eosio::singleton<"migration"_n, migration_variant>; diff --git a/contracts/eden/src/actions/induct.cpp b/contracts/eden/src/actions/induct.cpp index 93b1d04c2..9deaf2366 100644 --- a/contracts/eden/src/actions/induct.cpp +++ b/contracts/eden/src/actions/induct.cpp @@ -108,6 +108,7 @@ namespace eden accounts user_accounts{get_self()}; const auto& induction = inductions.get_induction(id); + inductions.check_valid_induction(induction); eosio::check(payer == induction.invitee(), "only inductee may donate using this action"); eosio::check(quantity == globals.get().minimum_donation, "incorrect donation"); user_accounts.sub_balance(payer, quantity); diff --git a/contracts/eden/src/eden-micro-chain.cpp b/contracts/eden/src/eden-micro-chain.cpp index cfe36f663..603ab85e1 100644 --- a/contracts/eden/src/eden-micro-chain.cpp +++ b/contracts/eden/src/eden-micro-chain.cpp @@ -12,6 +12,7 @@ #include #include #include +#include using namespace eosio::literals; @@ -217,6 +218,13 @@ struct status eosio::block_timestamp nextElection; uint16_t electionThreshold = 0; uint16_t numElectionParticipants = 0; + uint16_t migrationIndex = 0; + + template + bool isMigrationCompleted() const + { + return migrationIndex >= boost::mp11::mp_find::value; + } }; struct status_object : public chainbase::object @@ -1644,6 +1652,12 @@ void logtransfer(const action_context& context, } } +void handle_event(const eden::migration_event& event) +{ + db.status.modify(get_status(), + [&](auto& status) { status.status.migrationIndex = event.index; }); +} + void handle_event(const eden::election_event_schedule& event) { db.status.modify(get_status(), [&](auto& status) { @@ -1853,19 +1867,12 @@ void call(void (*f)(const action_context&, Args...), std::apply([&](auto&&... args) { f(context, std::move(args)...); }, t); } -void remove_expired_inductions(const subchain::eosio_block& block) +void remove_expired_inductions(const eosio::time_point& block_time, const status& status) { - auto& idx = db.status.get(); - if (idx.size() < 1) - return; // skip if genesis is not complete + if (status.isMigrationCompleted()) + return; // skip if migration is not ready to collect expired records - const auto& status = get_status(); - if (!status.status.active) - return; // skip if not active - - auto expiration_time = - eosio::block_timestamp(block.timestamp).to_time_point().sec_since_epoch() - - eden::induction_expiration_secs; + auto expiration_time = block_time.sec_since_epoch() - eden::induction_expiration_secs; auto& index = db.inductions.get(); auto it = index.begin(); @@ -1880,6 +1887,19 @@ void remove_expired_inductions(const subchain::eosio_block& block) } } +void clean_data(const subchain::eosio_block& block) +{ + auto& idx = db.status.get(); + if (idx.size() < 1) + return; // skip if genesis is not complete + + const auto& status = get_status(); + if (!status.status.active) + return; // skip if contract is not active + + remove_expired_inductions(block.timestamp, status.status); +} + void filter_block(const subchain::eosio_block& block) { block_state block_state{}; @@ -1948,7 +1968,7 @@ void filter_block(const subchain::eosio_block& block) } // for(action) // garbage collection housekeeping - // remove_expired_inductions(block); + clean_data(block); eosio::check(!block_state.in_withdraw && !block_state.in_manual_transfer, "missing transfer notification"); diff --git a/contracts/eden/src/migrations.cpp b/contracts/eden/src/migrations.cpp index 2d19f85d5..7b8185656 100644 --- a/contracts/eden/src/migrations.cpp +++ b/contracts/eden/src/migrations.cpp @@ -1,3 +1,4 @@ +#include #include namespace eden @@ -13,9 +14,9 @@ namespace eden void migrations::init() { - migration_sing.set(std::variant_alternative_t - 1, - migration_variant>(), - contract); + constexpr size_t index = std::variant_size_v - 1; + migration_sing.set(std::variant_alternative_t(), contract); + push_event(migration_event{static_cast(index)}, contract); } uint32_t migrations::migrate_some(uint32_t max_steps) @@ -28,6 +29,8 @@ namespace eden max_steps = current_state.migrate_some(contract, max_steps); if (max_steps) { + push_event(migration_event{static_cast(state.index())}, + contract); constexpr std::size_t next_index = boost::mp11::mp_find>::value + diff --git a/contracts/eden/tests/data/test-election.expected b/contracts/eden/tests/data/test-election.expected index 0c71953b5..7a1229c9d 100644 --- a/contracts/eden/tests/data/test-election.expected +++ b/contracts/eden/tests/data/test-election.expected @@ -1,3 +1,9 @@ +[ + "migration_event", + { + "index": 5 + } +] [ "set_pool_event", { From 0bff7cd7e5a2177c3d97da338bc941f74e3e7d12 Mon Sep 17 00:00:00 2001 From: Brandon Fancher Date: Thu, 18 Nov 2021 13:02:21 -0500 Subject: [PATCH 25/54] Componentize Election UI (#610) * Break down ongoing-round into subcomponents. * Add ephemeral chain test to set up and start election. * Set election time. --- .github/workflows/build.yml | 4 +- contracts/eden/CMakeLists.txt | 2 +- contracts/eden/tests/include/tester-base.hpp | 7 +- ...un-full-election.cpp => run-elections.cpp} | 5 +- .../webapp/src/elections/components/index.ts | 1 - .../chiefs-round-segment.tsx | 2 +- .../completed-round-segment.tsx | 2 +- .../ongoing-election-components/index.ts | 2 +- .../ongoing-round-segment.tsx | 294 ++---------------- .../ongoing-round/header-components/index.ts | 1 + .../header-components}/pie-mer.tsx | 0 .../ongoing-round/header.tsx | 99 ++++++ .../ongoing-round/index.ts | 4 + .../participants-voting-panel.tsx | 137 ++++++++ .../participants-waiting-panel.tsx | 24 ++ .../ongoing-round/round-info-panel.tsx | 76 +++++ .../round-info}/consensometer.tsx | 0 .../ongoing-round/round-info/index.ts | 1 + .../round-info}/meeting-link/index.ts | 0 .../meeting-link/meeting-buttons.tsx | 0 .../meeting-link/meeting-link-modal.tsx | 0 .../round-info}/meeting-link/meeting-link.md | 0 .../round-info}/meeting-link/meeting-link.tsx | 4 +- .../voting-round-participants.tsx | 1 - .../video-upload-button.tsx | 0 packages/webapp/src/pages/election/stats.tsx | 6 +- 26 files changed, 392 insertions(+), 280 deletions(-) rename contracts/eden/tests/{run-full-election.cpp => run-elections.cpp} (61%) create mode 100644 packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/header-components/index.ts rename packages/webapp/src/elections/components/{ => ongoing-election-components/ongoing-round/header-components}/pie-mer.tsx (100%) create mode 100644 packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/header.tsx create mode 100644 packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/index.ts create mode 100644 packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/participants-voting-panel.tsx create mode 100644 packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/participants-waiting-panel.tsx create mode 100644 packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/round-info-panel.tsx rename packages/webapp/src/elections/components/ongoing-election-components/{ => ongoing-round/round-info}/consensometer.tsx (100%) create mode 100644 packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/round-info/index.ts rename packages/webapp/src/elections/components/ongoing-election-components/{ => ongoing-round/round-info}/meeting-link/index.ts (100%) rename packages/webapp/src/elections/components/ongoing-election-components/{ => ongoing-round/round-info}/meeting-link/meeting-buttons.tsx (100%) rename packages/webapp/src/elections/components/ongoing-election-components/{ => ongoing-round/round-info}/meeting-link/meeting-link-modal.tsx (100%) rename packages/webapp/src/elections/components/ongoing-election-components/{ => ongoing-round/round-info}/meeting-link/meeting-link.md (100%) rename packages/webapp/src/elections/components/ongoing-election-components/{ => ongoing-round/round-info}/meeting-link/meeting-link.tsx (98%) rename packages/webapp/src/elections/components/ongoing-election-components/{ => ongoing-round}/voting-round-participants.tsx (98%) rename packages/webapp/src/elections/components/{ => ongoing-election-components}/video-upload-button.tsx (100%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e780458ea..a8a967f0f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -126,7 +126,7 @@ jobs: cp eden-micro-chain.wasm ../product_cache cp eden.abi ../product_cache cp eden.wasm ../product_cache - cp run-full-election.wasm ../product_cache + cp run-elections.wasm ../product_cache cp run-genesis.wasm ../product_cache cp token.abi ../product_cache cp token.wasm ../product_cache @@ -181,7 +181,7 @@ jobs: product_cache/token.abi product_cache/token.wasm product_cache/run-genesis.wasm - product_cache/run-full-election.wasm + product_cache/run-elections.wasm build-micro-chain: name: Build Micro Chain diff --git a/contracts/eden/CMakeLists.txt b/contracts/eden/CMakeLists.txt index ec019e00d..769d64dbe 100644 --- a/contracts/eden/CMakeLists.txt +++ b/contracts/eden/CMakeLists.txt @@ -62,7 +62,7 @@ eden_tester_test(test-eden) # Chain Runners add_test_eden("run-genesis" "") -add_test_eden("run-full-election" "") +add_test_eden("run-elections" "") file(CREATE_LINK ${CMAKE_CURRENT_SOURCE_DIR}/tests/data ${ROOT_BINARY_DIR}/eden-test-data SYMBOLIC) diff --git a/contracts/eden/tests/include/tester-base.hpp b/contracts/eden/tests/include/tester-base.hpp index 621b30cb2..67edac9eb 100644 --- a/contracts/eden/tests/include/tester-base.hpp +++ b/contracts/eden/tests/include/tester-base.hpp @@ -403,7 +403,7 @@ struct eden_tester } } - void run_election(bool auto_donate = true, uint32_t batch_size = 10000, bool add_video = false) + void start_election(bool auto_donate = true, uint32_t batch_size = 10000) { if (auto_donate) { @@ -414,6 +414,11 @@ struct eden_tester skip_to(next_election_time().to_time_point()); setup_election(batch_size); + } + + void run_election(bool auto_donate = true, uint32_t batch_size = 10000, bool add_video = false) + { + start_election(auto_donate, batch_size); uint8_t round = 0; diff --git a/contracts/eden/tests/run-full-election.cpp b/contracts/eden/tests/run-elections.cpp similarity index 61% rename from contracts/eden/tests/run-full-election.cpp rename to contracts/eden/tests/run-elections.cpp index d2ad76af5..515c0b00d 100644 --- a/contracts/eden/tests/run-full-election.cpp +++ b/contracts/eden/tests/run-elections.cpp @@ -2,7 +2,7 @@ TEST_CASE("Setup Eden chain with full election") { - nodeos_runner r("chain-full-election"); + nodeos_runner r("chain-run-elections"); r.tester.genesis(); r.tester.run_election(true, 10000, true); @@ -10,6 +10,9 @@ TEST_CASE("Setup Eden chain with full election") r.tester.induct_n(100); r.checkpoint("inductions"); r.tester.run_election(true, 10000, true); + r.checkpoint("full_election"); + r.tester.eden_gm.act(s2t("2021-11-18T04:12:00.000")); + r.tester.start_election(true, 10000); r.start_nodeos(); } diff --git a/packages/webapp/src/elections/components/index.ts b/packages/webapp/src/elections/components/index.ts index f655d7a6c..815b5039f 100644 --- a/packages/webapp/src/elections/components/index.ts +++ b/packages/webapp/src/elections/components/index.ts @@ -3,5 +3,4 @@ export * from "./election-member-chips"; export * from "./election-community-room-button"; export * from "./error-loading-election"; export * from "./ongoing-election"; -export * from "./pie-mer"; export * from "./registration-election"; diff --git a/packages/webapp/src/elections/components/ongoing-election-components/chiefs-round-segment.tsx b/packages/webapp/src/elections/components/ongoing-election-components/chiefs-round-segment.tsx index d05fb4891..83590a451 100644 --- a/packages/webapp/src/elections/components/ongoing-election-components/chiefs-round-segment.tsx +++ b/packages/webapp/src/elections/components/ongoing-election-components/chiefs-round-segment.tsx @@ -10,7 +10,7 @@ import { import { Container, Expander, Heading, Loader, Text } from "_app/ui"; import { DelegateChip, ErrorLoadingElection } from "elections"; import { MembersGrid } from "members"; -import { VideoUploadButton } from "../video-upload-button"; +import { VideoUploadButton } from "./video-upload-button"; import RoundHeader from "./round-header"; diff --git a/packages/webapp/src/elections/components/ongoing-election-components/completed-round-segment.tsx b/packages/webapp/src/elections/components/ongoing-election-components/completed-round-segment.tsx index b7c1d3235..16b4e5076 100644 --- a/packages/webapp/src/elections/components/ongoing-election-components/completed-round-segment.tsx +++ b/packages/webapp/src/elections/components/ongoing-election-components/completed-round-segment.tsx @@ -8,7 +8,7 @@ import { ElectionParticipantChip } from "elections"; import { EdenMember, MembersGrid } from "members"; import RoundHeader from "./round-header"; -import { VideoUploadButton } from "../video-upload-button"; +import { VideoUploadButton } from "./video-upload-button"; interface CompletedRoundSegmentProps { roundIndex: number; diff --git a/packages/webapp/src/elections/components/ongoing-election-components/index.ts b/packages/webapp/src/elections/components/ongoing-election-components/index.ts index b7655d13e..89033cb82 100644 --- a/packages/webapp/src/elections/components/ongoing-election-components/index.ts +++ b/packages/webapp/src/elections/components/ongoing-election-components/index.ts @@ -1,6 +1,6 @@ export * from "./chiefs-round-segment"; export * from "./completed-round-segment"; -export * from "./consensometer"; export * from "./ongoing-round-segment"; export * from "./round-header"; export * from "./support-segment"; +export * from "./video-upload-button"; diff --git a/packages/webapp/src/elections/components/ongoing-election-components/ongoing-round-segment.tsx b/packages/webapp/src/elections/components/ongoing-election-components/ongoing-round-segment.tsx index 4c59b9bdf..f54d85ccd 100644 --- a/packages/webapp/src/elections/components/ongoing-election-components/ongoing-round-segment.tsx +++ b/packages/webapp/src/elections/components/ongoing-election-components/ongoing-round-segment.tsx @@ -1,25 +1,15 @@ -import React, { useEffect, useState } from "react"; -import { useQueryClient } from "react-query"; -import dayjs, { Dayjs } from "dayjs"; -import { BiCheck } from "react-icons/bi"; +import React from "react"; +import { Dayjs } from "dayjs"; import { election as electionEnvVars } from "config"; -import { onError, useCountdown, useTimeout, useUALAccount } from "_app"; import { - queryMemberGroupParticipants, useCurrentMember, useMemberDataFromVoteData, useMemberGroupParticipants, useVoteData, } from "_app/hooks/queries"; -import { Button, Container, Expander, Heading, Loader, Text } from "_app/ui"; -import { MemberData } from "members/interfaces"; -import { - ErrorLoadingElection, - ElectionParticipantChip, - VotePieMer, - useRoundStageTimes, -} from "elections"; +import { Container, Expander, Loader } from "_app/ui"; +import { ErrorLoadingElection, useRoundStageTimes } from "elections"; import { ActiveStateConfigType, Election, @@ -27,12 +17,12 @@ import { RoundStage, } from "elections/interfaces"; -import Consensometer from "./consensometer"; -import RoundHeader from "./round-header"; -import { MeetingLink } from "./meeting-link"; -import VotingRoundParticipants from "./voting-round-participants"; -import { setVote } from "../../transactions"; -import { VideoUploadButton } from "../video-upload-button"; +import { + Header, + ParticipantsVotingPanel, + ParticipantsWaitingPanel, + RoundInfoPanel, +} from "./ongoing-round"; export interface RoundSegmentProps { ongoingElectionData?: Election; @@ -45,7 +35,7 @@ export interface RoundSegmentProps { onRoundEnd: () => void; } -// TODO: Much of the building up of the data shouldn't be done in the UI layer. What do we want the API to provide? What data does this UI really need? We could even define a new OngoingElection type to provide to this UI. +// TODO: After first election, refactor to use new box election state engine. export const OngoingRoundSegment = ({ electionState, roundIndex, @@ -55,8 +45,6 @@ export const OngoingRoundSegment = ({ electionConfig, onRoundEnd, }: RoundSegmentProps) => { - const queryClient = useQueryClient(); - // duration of time periods before and after election meeting call // stages: meeting prep -> meeting -> post-meeting finalization -> round end const meetingBreakDurationMs = @@ -77,10 +65,6 @@ export const OngoingRoundSegment = ({ stage ); - const [selectedMember, setSelected] = useState(null); - const [isSubmittingVote, setIsSubmittingVote] = useState(false); - - const [ualAccount] = useUALAccount(); const { data: loggedInMember, isLoading: isLoadingCurrentMember, @@ -141,46 +125,7 @@ export const OngoingRoundSegment = ({ return ; } - const userVoterStats = voteData!.find( - (vs) => vs.member === loggedInMember?.account - ); - - const userVotingFor = members?.find( - (m) => m.account === userVoterStats?.candidate - ); - - const onSubmitVote = async () => { - if (!selectedMember) return; - setIsSubmittingVote(true); - try { - const authorizerAccount = ualAccount.accountName; - const transaction = setVote( - authorizerAccount, - roundIndex, - selectedMember?.account - ); - await ualAccount.signTransaction(transaction, { - broadcast: true, - }); - - // invalidate current member query to update participating status - await new Promise((resolve) => setTimeout(resolve, 3000)); - queryClient.invalidateQueries( - queryMemberGroupParticipants( - loggedInMember?.account, - roundIndex, - electionConfig - ).queryKey - ); - } catch (error) { - console.error(error); - onError(error as Error); - } - setIsSubmittingVote(false); - }; - return ( - // TODO: Move this out into a separate component to simplify and make this more readable
- -
- {[RoundStage.PreMeeting, RoundStage.Meeting].includes( - stage - ) && ( -
- -
- )} - {[RoundStage.Meeting, RoundStage.PostMeeting].includes( - stage - ) && ( -
- -
- )} -
-
- Meeting group members - - {stage === RoundStage.PreMeeting - ? "Make sure you have your meeting link ready and stand by. You'll be on a video call with the following Eden members momentarily." - : stage === RoundStage.Meeting - ? "Meet with your group. Align on a delegate >2/3 majority. Select your delegate and submit your vote below." - : stage === RoundStage.Complete - ? "If you're the delegate elect, stand by. The next round will start momentarily." - : "This round is finalizing. Please submit any outstanding votes now. You will be able to come back later to upload election videos if your video isn't ready yet."} - -
-
- {voteData && isVotingOpen && ( - - )} -
-
+
+ +
{voteData && isVotingOpen ? ( - <> - setSelected(m)} - userVotingFor={userVotingFor?.account} - /> - -
-
- -
- -
- -
-
-
- + ) : ( -
- {members?.map((member) => ( - - ))} -
+ )}
); }; - -interface VoteButtonProps { - selectedMember: MemberData | null; - isSubmittingVote: boolean; - userVotingFor?: MemberData; - onSubmitVote: () => Promise; -} - -const VoteButton = ({ - selectedMember, - isSubmittingVote, - userVotingFor, - onSubmitVote, -}: VoteButtonProps) => ( - -); - -interface HeaderProps { - stage: RoundStage; - roundIndex: number; - roundStartTime: Dayjs; - roundEndTime: Dayjs; - currentStageEndTime: Dayjs; - meetingStartTime: Dayjs; - postMeetingStartTime: Dayjs; -} - -const Header = ({ - stage, - roundIndex, - roundStartTime, - roundEndTime, - currentStageEndTime, - meetingStartTime, - postMeetingStartTime, -}: HeaderProps) => { - return ( - - } - sublineComponent={ - - {roundStartTime.format("LT")} -{" "} - {roundEndTime.format("LT z")} - - } - > - {stage === RoundStage.Meeting && ( - - )} - - ); -}; - -interface HeadlineProps { - roundIndex: number; - stage: RoundStage; - currentStageEndTime: Dayjs; -} - -const RoundHeaderHeadline = ({ - roundIndex, - stage, - currentStageEndTime, -}: HeadlineProps) => { - const { hmmss } = useCountdown({ endTime: currentStageEndTime.toDate() }); - const roundNum = roundIndex + 1; - switch (stage) { - case RoundStage.PreMeeting: - return ( - - Round {roundNum} starts in:{" "} - {hmmss} - - ); - case RoundStage.PostMeeting: - return ( - - Round {roundNum} finalizes in:{" "} - {hmmss} - - ); - case RoundStage.Complete: - return ( - - Round {roundIndex + 1} finalizing... - - ); - } - return ( - - Round {roundIndex + 1} in progress - - ); -}; diff --git a/packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/header-components/index.ts b/packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/header-components/index.ts new file mode 100644 index 000000000..3fbfacafc --- /dev/null +++ b/packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/header-components/index.ts @@ -0,0 +1 @@ +export * from "./pie-mer"; diff --git a/packages/webapp/src/elections/components/pie-mer.tsx b/packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/header-components/pie-mer.tsx similarity index 100% rename from packages/webapp/src/elections/components/pie-mer.tsx rename to packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/header-components/pie-mer.tsx diff --git a/packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/header.tsx b/packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/header.tsx new file mode 100644 index 000000000..33241d930 --- /dev/null +++ b/packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/header.tsx @@ -0,0 +1,99 @@ +import React from "react"; +import dayjs from "dayjs"; + +import { useCountdown } from "_app"; +import { Text } from "_app/ui"; +import { RoundStage } from "elections/interfaces"; + +import RoundHeader from "../round-header"; +import { VotePieMer } from "./header-components"; + +interface HeaderProps { + stage: RoundStage; + roundIndex: number; + roundStartTime: dayjs.Dayjs; + roundEndTime: dayjs.Dayjs; + currentStageEndTime: dayjs.Dayjs; + meetingStartTime: dayjs.Dayjs; + postMeetingStartTime: dayjs.Dayjs; +} + +export const Header = ({ + stage, + roundIndex, + roundStartTime, + roundEndTime, + currentStageEndTime, + meetingStartTime, + postMeetingStartTime, +}: HeaderProps) => { + return ( + + } + sublineComponent={ + + {roundStartTime.format("LT")} -{" "} + {roundEndTime.format("LT z")} + + } + > + {stage === RoundStage.Meeting && ( + + )} + + ); +}; + +export default Header; + +interface HeadlineProps { + roundIndex: number; + stage: RoundStage; + currentStageEndTime: dayjs.Dayjs; +} + +const RoundHeaderHeadline = ({ + roundIndex, + stage, + currentStageEndTime, +}: HeadlineProps) => { + const { hmmss } = useCountdown({ endTime: currentStageEndTime.toDate() }); + const roundNum = roundIndex + 1; + switch (stage) { + case RoundStage.PreMeeting: + return ( + + Round {roundNum} starts in:{" "} + {hmmss} + + ); + case RoundStage.PostMeeting: + return ( + + Round {roundNum} finalizes in:{" "} + {hmmss} + + ); + case RoundStage.Complete: + return ( + + Round {roundIndex + 1} finalizing... + + ); + } + return ( + + Round {roundIndex + 1} in progress + + ); +}; diff --git a/packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/index.ts b/packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/index.ts new file mode 100644 index 000000000..8ee29f62c --- /dev/null +++ b/packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/index.ts @@ -0,0 +1,4 @@ +export * from "./header"; +export * from "./round-info-panel"; +export * from "./participants-voting-panel"; +export * from "./participants-waiting-panel"; diff --git a/packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/participants-voting-panel.tsx b/packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/participants-voting-panel.tsx new file mode 100644 index 000000000..38a0cb818 --- /dev/null +++ b/packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/participants-voting-panel.tsx @@ -0,0 +1,137 @@ +import React, { useState } from "react"; +import { useQueryClient } from "react-query"; +import { BiCheck } from "react-icons/bi"; + +import { onError, useUALAccount } from "_app"; +import { + queryMemberGroupParticipants, + useCurrentMember, +} from "_app/hooks/queries"; +import { Button, Container } from "_app/ui"; +import { MemberData } from "members/interfaces"; +import { ActiveStateConfigType, VoteData } from "elections/interfaces"; + +import { setVote } from "../../../transactions"; +import { VideoUploadButton } from "../video-upload-button"; +import VotingRoundParticipants from "./voting-round-participants"; + +interface ParticipantsVotingPanelProps { + members?: MemberData[]; + voteData: VoteData[]; + roundIndex: number; + electionConfig?: ActiveStateConfigType; +} + +export const ParticipantsVotingPanel = ({ + members, + voteData, + roundIndex, + electionConfig, +}: ParticipantsVotingPanelProps) => { + const [selectedMember, setSelected] = useState(null); + const [isSubmittingVote, setIsSubmittingVote] = useState(false); + + const queryClient = useQueryClient(); + + const [ualAccount] = useUALAccount(); + const { data: loggedInMember } = useCurrentMember(); + + const userVoterStats = voteData!.find( + (vs) => vs.member === loggedInMember?.account + ); + + const userVotingFor = members?.find( + (m) => m.account === userVoterStats?.candidate + ); + + const onSubmitVote = async () => { + if (!selectedMember) return; + setIsSubmittingVote(true); + try { + const authorizerAccount = ualAccount.accountName; + const transaction = setVote( + authorizerAccount, + roundIndex, + selectedMember?.account + ); + await ualAccount.signTransaction(transaction, { + broadcast: true, + }); + + // invalidate current member query to update participating status + await new Promise((resolve) => setTimeout(resolve, 3000)); + queryClient.invalidateQueries( + queryMemberGroupParticipants( + loggedInMember?.account, + roundIndex, + electionConfig + ).queryKey + ); + } catch (error) { + console.error(error); + onError(error as Error); + } + setIsSubmittingVote(false); + }; + + return ( + <> + setSelected(m)} + userVotingFor={userVotingFor?.account} + /> + +
+
+ +
+ +
+ +
+
+
+ + ); +}; + +export default ParticipantsVotingPanel; + +interface VoteButtonProps { + selectedMember: MemberData | null; + isSubmittingVote: boolean; + userVotingFor?: MemberData; + onSubmitVote: () => Promise; +} + +const VoteButton = ({ + selectedMember, + isSubmittingVote, + userVotingFor, + onSubmitVote, +}: VoteButtonProps) => ( + +); diff --git a/packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/participants-waiting-panel.tsx b/packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/participants-waiting-panel.tsx new file mode 100644 index 000000000..1c5cff501 --- /dev/null +++ b/packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/participants-waiting-panel.tsx @@ -0,0 +1,24 @@ +import React from "react"; +import { MemberData } from "members/interfaces"; +import { ElectionParticipantChip } from "elections"; + +interface ParticipantsWaitingPanelProps { + members?: MemberData[]; + roundIndex: number; +} + +export const ParticipantsWaitingPanel = ({ + members, + roundIndex, +}: ParticipantsWaitingPanelProps) => ( +
+ {members?.map((member) => ( + + ))} +
+); + +export default ParticipantsWaitingPanel; diff --git a/packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/round-info-panel.tsx b/packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/round-info-panel.tsx new file mode 100644 index 000000000..bc1635a41 --- /dev/null +++ b/packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/round-info-panel.tsx @@ -0,0 +1,76 @@ +import React from "react"; +import dayjs from "dayjs"; + +import { Container, Heading, Text } from "_app/ui"; +import { + ActiveStateConfigType, + RoundStage, + VoteData, +} from "elections/interfaces"; + +import { Consensometer } from "./round-info"; +import { MeetingLink } from "./round-info/meeting-link"; +import { VideoUploadButton } from "../video-upload-button"; + +interface RoundInfoPanelProps { + stage: RoundStage; + roundIndex: number; + meetingStartTime: dayjs.Dayjs; + electionConfig?: ActiveStateConfigType; + voteData?: VoteData[]; + isVotingOpen: boolean; +} + +export const RoundInfoPanel = ({ + stage, + roundIndex, + meetingStartTime, + electionConfig, + voteData, + isVotingOpen, +}: RoundInfoPanelProps) => { + return ( + +
+ {[RoundStage.PreMeeting, RoundStage.Meeting].includes( + stage + ) && ( +
+ +
+ )} + {[RoundStage.Meeting, RoundStage.PostMeeting].includes( + stage + ) && ( +
+ +
+ )} +
+
+ Meeting group members + + {stage === RoundStage.PreMeeting + ? "Make sure you have your meeting link ready and stand by. You'll be on a video call with the following Eden members momentarily." + : stage === RoundStage.Meeting + ? "Meet with your group. Align on a leader >2/3 majority. Select your leader and submit your vote below." + : stage === RoundStage.Complete + ? "If you're the delegate elect, stand by. The next round will start momentarily." + : "This round is finalizing. Please submit any outstanding votes now. You will be able to come back later to upload election videos if your video isn't ready yet."} + +
+
+ {voteData && isVotingOpen && ( + + )} +
+
+ ); +}; + +export default RoundInfoPanel; diff --git a/packages/webapp/src/elections/components/ongoing-election-components/consensometer.tsx b/packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/round-info/consensometer.tsx similarity index 100% rename from packages/webapp/src/elections/components/ongoing-election-components/consensometer.tsx rename to packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/round-info/consensometer.tsx diff --git a/packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/round-info/index.ts b/packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/round-info/index.ts new file mode 100644 index 000000000..1a1536695 --- /dev/null +++ b/packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/round-info/index.ts @@ -0,0 +1 @@ +export * from "./consensometer"; diff --git a/packages/webapp/src/elections/components/ongoing-election-components/meeting-link/index.ts b/packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/round-info/meeting-link/index.ts similarity index 100% rename from packages/webapp/src/elections/components/ongoing-election-components/meeting-link/index.ts rename to packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/round-info/meeting-link/index.ts diff --git a/packages/webapp/src/elections/components/ongoing-election-components/meeting-link/meeting-buttons.tsx b/packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/round-info/meeting-link/meeting-buttons.tsx similarity index 100% rename from packages/webapp/src/elections/components/ongoing-election-components/meeting-link/meeting-buttons.tsx rename to packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/round-info/meeting-link/meeting-buttons.tsx diff --git a/packages/webapp/src/elections/components/ongoing-election-components/meeting-link/meeting-link-modal.tsx b/packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/round-info/meeting-link/meeting-link-modal.tsx similarity index 100% rename from packages/webapp/src/elections/components/ongoing-election-components/meeting-link/meeting-link-modal.tsx rename to packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/round-info/meeting-link/meeting-link-modal.tsx diff --git a/packages/webapp/src/elections/components/ongoing-election-components/meeting-link/meeting-link.md b/packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/round-info/meeting-link/meeting-link.md similarity index 100% rename from packages/webapp/src/elections/components/ongoing-election-components/meeting-link/meeting-link.md rename to packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/round-info/meeting-link/meeting-link.md diff --git a/packages/webapp/src/elections/components/ongoing-election-components/meeting-link/meeting-link.tsx b/packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/round-info/meeting-link/meeting-link.tsx similarity index 98% rename from packages/webapp/src/elections/components/ongoing-election-components/meeting-link/meeting-link.tsx rename to packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/round-info/meeting-link/meeting-link.tsx index e8679b4ae..576b13fab 100644 --- a/packages/webapp/src/elections/components/ongoing-election-components/meeting-link/meeting-link.tsx +++ b/packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/round-info/meeting-link/meeting-link.tsx @@ -30,7 +30,6 @@ export enum MeetingStep { interface RequestMeetingLinkProps { roundIndex: number; meetingStartTime: Dayjs; - meetingDurationMs: number; electionConfig: ActiveStateConfigType; stage: RoundStage; } @@ -42,7 +41,6 @@ interface RequestMeetingLinkProps { export const MeetingLink = ({ roundIndex, meetingStartTime, - meetingDurationMs, electionConfig, stage, }: RequestMeetingLinkProps) => { @@ -138,7 +136,7 @@ export const MeetingLink = ({ await checkSubmissionIsAllowed(); const topic = `Eden Election - Round #${roundIndex + 1}`; - const durationInMinutes = meetingDurationMs / 1000 / 60; + const durationInMinutes = electionEnvVars.meetingDurationMs / 1000 / 60; const startTime = meetingStartTime.toISOString().split(".")[0] + "Z"; const responseData = await generateZoomMeetingLink( diff --git a/packages/webapp/src/elections/components/ongoing-election-components/voting-round-participants.tsx b/packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/voting-round-participants.tsx similarity index 98% rename from packages/webapp/src/elections/components/ongoing-election-components/voting-round-participants.tsx rename to packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/voting-round-participants.tsx index e66e22182..0c84a0d34 100644 --- a/packages/webapp/src/elections/components/ongoing-election-components/voting-round-participants.tsx +++ b/packages/webapp/src/elections/components/ongoing-election-components/ongoing-round/voting-round-participants.tsx @@ -1,7 +1,6 @@ import { Flipper, Flipped } from "react-flip-toolkit"; import { VotingMemberChip } from "elections"; import { VoteData } from "elections/interfaces"; -import { MembersGrid } from "members"; import { MemberData } from "members/interfaces"; interface VotingRoundParticipantsProps { diff --git a/packages/webapp/src/elections/components/video-upload-button.tsx b/packages/webapp/src/elections/components/ongoing-election-components/video-upload-button.tsx similarity index 100% rename from packages/webapp/src/elections/components/video-upload-button.tsx rename to packages/webapp/src/elections/components/ongoing-election-components/video-upload-button.tsx diff --git a/packages/webapp/src/pages/election/stats.tsx b/packages/webapp/src/pages/election/stats.tsx index c3bd15de6..2f139f86e 100644 --- a/packages/webapp/src/pages/election/stats.tsx +++ b/packages/webapp/src/pages/election/stats.tsx @@ -19,10 +19,8 @@ import { VotingMemberChip, } from "elections"; import { MemberData, MembersGrid } from "members"; -import { - ConsensometerBlocks, - RoundHeader, -} from "elections/components/ongoing-election-components"; +import { RoundHeader } from "elections/components/ongoing-election-components"; +import { ConsensometerBlocks } from "elections/components/ongoing-election-components/ongoing-round/round-info/consensometer"; export const ElectionStatsPage = () => { const { From aa1c8bc9f223e787fb847039e21713e2d296a43e Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Thu, 18 Nov 2021 13:30:51 -0500 Subject: [PATCH 26/54] address feedback --- contracts/eden/src/actions/sessions.cpp | 27 ++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/contracts/eden/src/actions/sessions.cpp b/contracts/eden/src/actions/sessions.cpp index 927c7f0d7..b63e596bc 100644 --- a/contracts/eden/src/actions/sessions.cpp +++ b/contracts/eden/src/actions/sessions.cpp @@ -5,6 +5,14 @@ namespace eden { + void set_expiration(session_container& sc) + { + auto expiration = eosio::block_timestamp::max(); + for (auto& session : sc.sessions()) + expiration = std::min(expiration, session.expiration); + sc.earliest_expiration() = expiration; + } + void expire(eosio::name contract, session_container& sc) { auto now = eosio::current_block_time(); @@ -15,11 +23,6 @@ namespace eden auto new_end = std::remove_if(sessions.begin(), sessions.end(), [&](auto& session) { return session.expiration <= now; }); sessions.erase(new_end, sessions.end()); - - auto expiration = eosio::block_timestamp::max(); - for (auto& session : sessions) - expiration = std::min(expiration, session.expiration); - sc.earliest_expiration() = expiration; } uint32_t gc_sessions(eosio::name contract, uint32_t remaining) @@ -30,7 +33,10 @@ namespace eden while (remaining && idx.begin() != idx.end() && idx.begin()->earliest_expiration() <= now) { auto& sc = *idx.begin(); - table.modify(sc, contract, [&](auto& sc) { expire(contract, sc); }); + table.modify(sc, contract, [&](auto& sc) { + expire(contract, sc); + set_expiration(sc); + }); if (sc.sessions().empty()) table.erase(sc); --remaining; @@ -86,6 +92,7 @@ namespace eden else { table.modify(sc, get_self(), [&](auto& sc) { + expire(get_self(), sc); auto& sessions = sc.sessions(); auto session = std::find_if(sessions.begin(), sessions.end(), [&](auto& session) { return session.key == key; }); @@ -97,7 +104,7 @@ namespace eden }); if (sessions.size() > 4) sessions.erase(sessions.begin()); - expire(get_self(), sc); // also sets earliest_expiration + set_expiration(sc); }); } push_event(session_new_event{eden_account, key, expiration, description}, get_self()); @@ -119,7 +126,8 @@ namespace eden eosio::check(session != sessions.end(), "Session key is either expired or not found"); push_event(session_del_event{sc.eden_account(), session->key}, get_self()); sessions.erase(session); - expire(get_self(), sc); // also sets earliest_expiration + expire(get_self(), sc); + set_expiration(sc); empty = sessions.empty(); }); if (empty) @@ -147,6 +155,7 @@ namespace eden " is either expired or not found"); table.modify(sc, get_self(), [&](auto& sc) { expire(get_self(), sc); + set_expiration(sc); auto& sessions = sc.sessions(); auto session = std::find_if(sessions.begin(), sessions.end(), [&](auto& session) { return session.key == recovered; }); @@ -157,7 +166,7 @@ namespace eden auto& sequences = session->sequences; if (sequences.begin() != sequences.end()) { - if (sequence.value < *sequences.begin()) + if (sequence.value < *sequences.begin() && sequences.size() < 20) eosio::check(false, "received duplicate sequence " + std::to_string(sequence.value)); else if (sequence.value > sequences.end()[-1].value + 10) eosio::check(false, From 6417a5b43229785954e767eb2bd7dd83552e1724 Mon Sep 17 00:00:00 2001 From: Brandon Fancher Date: Thu, 18 Nov 2021 14:06:35 -0500 Subject: [PATCH 27/54] Elections test runner: start election with current time. --- contracts/eden/tests/run-elections.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contracts/eden/tests/run-elections.cpp b/contracts/eden/tests/run-elections.cpp index 515c0b00d..cdddedd13 100644 --- a/contracts/eden/tests/run-elections.cpp +++ b/contracts/eden/tests/run-elections.cpp @@ -11,7 +11,8 @@ TEST_CASE("Setup Eden chain with full election") r.checkpoint("inductions"); r.tester.run_election(true, 10000, true); r.checkpoint("full_election"); - r.tester.eden_gm.act(s2t("2021-11-18T04:12:00.000")); + r.tester.eden_gm.act( + time_point_sec{static_cast(time(nullptr))}); r.tester.start_election(true, 10000); r.start_nodeos(); From bc92b0b523a64d8086650f90780d84358e8e5de7 Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Fri, 19 Nov 2021 09:41:12 -0500 Subject: [PATCH 28/54] Remove debug print --- contracts/eden/src/elections.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/contracts/eden/src/elections.cpp b/contracts/eden/src/elections.cpp index 7b4bdbc50..7df42071a 100644 --- a/contracts/eden/src/elections.cpp +++ b/contracts/eden/src/elections.cpp @@ -483,8 +483,6 @@ namespace eden { if (iter->status() == member_status::active_member) { - printf("%d, %d\n", iter->election_participation_status(), - state.election_schedule_version); if (iter->election_participation_status() == state.election_schedule_version) { add_voter(state.rng, 0, state.next_member_idx, iter->account()); From 96565528f03c80f576f82af2378396aee30997e4 Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Fri, 19 Nov 2021 09:59:39 -0500 Subject: [PATCH 29/54] Fix condition in electopt --- contracts/eden/src/actions/elect.cpp | 10 ---------- contracts/eden/src/members.cpp | 18 ++++++++++++++++-- contracts/eden/tests/test-eden.cpp | 4 ++-- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/contracts/eden/src/actions/elect.cpp b/contracts/eden/src/actions/elect.cpp index c872277b4..a082aaa3d 100644 --- a/contracts/eden/src/actions/elect.cpp +++ b/contracts/eden/src/actions/elect.cpp @@ -32,16 +32,6 @@ namespace eden members members{get_self()}; const auto& member = members.get_member(voter); - if (participating) - { - eosio::check(member.election_participation_status() == not_in_election, - "Not currently opted out"); - } - else - { - eosio::check(member.election_participation_status() != not_in_election, - "Not currently opted in"); - } members.election_opt(member, participating); } diff --git a/contracts/eden/src/members.cpp b/contracts/eden/src/members.cpp index 8ddca0292..2a7484878 100644 --- a/contracts/eden/src/members.cpp +++ b/contracts/eden/src/members.cpp @@ -192,10 +192,24 @@ namespace eden election_time->to_time_point(), "Registration has closed"); + uint8_t new_participation_status; + if (participating) + { + new_participation_status = elections.election_schedule_version(); + eosio::check(member.election_participation_status() != new_participation_status, + "Not currently opted out"); + } + else + { + new_participation_status = not_in_election; + eosio::check( + member.election_participation_status() == elections.election_schedule_version(), + "Not currently opted in"); + } + member_tb.modify(member, eosio::same_payer, [&](auto& row) { row.value = std::visit([](auto& v) { return member_v1{v}; }, row.value); - row.election_participation_status() = - participating ? elections.election_schedule_version() : 0; + row.election_participation_status() = new_participation_status; }); } diff --git a/contracts/eden/tests/test-eden.cpp b/contracts/eden/tests/test-eden.cpp index a773138ce..8d1f64014 100644 --- a/contracts/eden/tests/test-eden.cpp +++ b/contracts/eden/tests/test-eden.cpp @@ -1220,9 +1220,9 @@ TEST_CASE("settablerows") t.eden_gm.act( eosio::name(eden::default_scope), std::vector{ - eden::current_election_state_registration{s2t("2020-01-02T00:00:00.0000")}}); + eden::current_election_state_registration_v1{s2t("2020-01-02T00:00:00.0000")}}); eden::current_election_state_singleton state{"eden.gm"_n, eden::default_scope}; - auto value = std::get(state.get()); + auto value = std::get(state.get()); CHECK(value.start_time.to_time_point() == s2t("2020-01-02T00:00:00.0000")); } From 1d51787f7b39ac952059757fc0781c91d9cd712f Mon Sep 17 00:00:00 2001 From: Brandon Fancher Date: Thu, 18 Nov 2021 14:36:43 -0500 Subject: [PATCH 30/54] Nav profile: member data from box query. --- packages/webapp/src/_app/ui/nav-profile.tsx | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/packages/webapp/src/_app/ui/nav-profile.tsx b/packages/webapp/src/_app/ui/nav-profile.tsx index 07ee4b1b1..2833a28b2 100644 --- a/packages/webapp/src/_app/ui/nav-profile.tsx +++ b/packages/webapp/src/_app/ui/nav-profile.tsx @@ -5,13 +5,10 @@ import { usePopper } from "react-popper"; import { IoMdLogIn } from "react-icons/io"; import { MemberStatus } from "_app"; -import { - useCurrentMember, - useMemberDataFromEdenMembers, - useSignOut, -} from "_app/hooks"; +import { useCurrentMember, useSignOut } from "_app/hooks"; import { Button, ProfileImage, Text } from "_app/ui"; import { ROUTES } from "_app/routes"; +import { useMemberByAccountName } from "members/hooks"; import { useUALAccount } from "../eos"; @@ -23,21 +20,11 @@ export const NavProfile = ({ location }: Props) => { const [ualAccount, _, ualShowModal] = useUALAccount(); const accountName = ualAccount?.accountName; - const { - data: member, - isLoading: isLoadingCurrentMember, - isError: isErrorCurrentMember, - } = useCurrentMember(); - const { - data: memberData, - isLoading: isLoadingMemberData, - isError: isErrorMemberData, - } = useMemberDataFromEdenMembers(member ? [member] : []); + const { data: member } = useCurrentMember(); + const { data: userProfile } = useMemberByAccountName(member?.account ?? ""); const isActiveMember = member?.status === MemberStatus.ActiveMember; - const userProfile = memberData?.[0]; - if (!ualAccount) { return (
From 853d01a3c9534c60799abf43e2e44546da2cfbfa Mon Sep 17 00:00:00 2001 From: Brandon Fancher Date: Thu, 18 Nov 2021 17:19:27 -0500 Subject: [PATCH 31/54] My delegation: remove AA/AH dependency and make work with ephemeral chain. --- .../src/delegates/components/my-delegation.tsx | 14 ++++---------- packages/webapp/src/members/hooks/queries.ts | 10 ++++++++++ packages/webapp/src/pages/delegates/index.tsx | 7 ++++--- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/packages/webapp/src/delegates/components/my-delegation.tsx b/packages/webapp/src/delegates/components/my-delegation.tsx index 1e112ceb4..00619fb57 100644 --- a/packages/webapp/src/delegates/components/my-delegation.tsx +++ b/packages/webapp/src/delegates/components/my-delegation.tsx @@ -12,7 +12,7 @@ import { ElectionParticipantChip, ElectionState, } from "elections"; -import { MembersGrid } from "members"; +import { MembersGrid, useMembersByAccountNames } from "members"; import { EdenMember, MemberData } from "members/interfaces"; import { ErrorLoadingDelegation } from "./statuses"; @@ -108,19 +108,13 @@ const ChiefDelegates = ({ .map((chiefQR) => chiefQR.data) .filter((el) => Boolean(el)); - const nftTemplateIds = chiefsAsMembers.map( - (member) => member!.nft_template_id - ); - const { data: memberData, isLoading: isLoadingMemberData, isError: isErrorMemberData, - } = useQuery({ - ...queryMembers(1, allChiefAccountNames.length, nftTemplateIds), - staleTime: Infinity, - enabled: Boolean(chiefsAsMembers?.length), - }); + } = useMembersByAccountNames( + chiefsAsMembers.map((chief) => chief!.account) + ); const isLoading = isLoadingMemberStats || isLoadingMemberData; const isError = isErrorMemberStats || isErrorMemberData; diff --git a/packages/webapp/src/members/hooks/queries.ts b/packages/webapp/src/members/hooks/queries.ts index 50c57a19c..7c48d257a 100644 --- a/packages/webapp/src/members/hooks/queries.ts +++ b/packages/webapp/src/members/hooks/queries.ts @@ -42,6 +42,16 @@ export const useMembers = () => { return { ...result, data: formattedMembers }; }; +export const useMembersByAccountNames = ( + accountNames: string[] | undefined = [] +) => { + const { data: allMembers, ...memberQueryMetaData } = useMembers(); + const members = allMembers.filter((member) => + accountNames.includes(member.account) + ); + return { data: members, ...memberQueryMetaData }; +}; + export const useMemberByAccountName = (account: string) => { const result = useBoxQuery(`{ members(ge: "${account}", le: "${account}") { diff --git a/packages/webapp/src/pages/delegates/index.tsx b/packages/webapp/src/pages/delegates/index.tsx index 4e7772dcc..c71f700e9 100644 --- a/packages/webapp/src/pages/delegates/index.tsx +++ b/packages/webapp/src/pages/delegates/index.tsx @@ -5,12 +5,11 @@ import { SideNavLayout, useCurrentElection, useElectionState, - useMemberDataFromEdenMembers, useMyDelegation, } from "_app"; import { Container, Heading, LoadingContainer, Text } from "_app/ui"; import { ElectionStatus } from "elections/interfaces"; -import { MemberGateContainer } from "members"; +import { MemberGateContainer, useMembersByAccountNames } from "members"; import { ErrorLoadingDelegation, ElectionInProgress, @@ -45,7 +44,9 @@ export const DelegatesPage = () => { data: myDelegationMemberData, isLoading: isLoadingMemberData, isError: isErrorMemberData, - } = useMemberDataFromEdenMembers(myDelegation); + } = useMembersByAccountNames( + myDelegation?.map((delegate) => delegate.account) + ); const isLoading = isLoadingCurrentElection || From d44b1c69bcf0ae826b076c711f655b1791285b54 Mon Sep 17 00:00:00 2001 From: Brandon Fancher Date: Thu, 18 Nov 2021 18:34:32 -0500 Subject: [PATCH 32/54] Get ongoing elections UI working with ephmeral chain - no AA/AH dependencies. --- packages/webapp/src/_app/hooks/queries.ts | 33 ++++--------------- .../completed-round-segment.tsx | 13 +++----- 2 files changed, 12 insertions(+), 34 deletions(-) diff --git a/packages/webapp/src/_app/hooks/queries.ts b/packages/webapp/src/_app/hooks/queries.ts index 0a2747345..6bcfe3c6a 100644 --- a/packages/webapp/src/_app/hooks/queries.ts +++ b/packages/webapp/src/_app/hooks/queries.ts @@ -8,6 +8,7 @@ import { getMembersStats, MemberData, MemberStats, + useMembersByAccountNames, } from "members"; import { getCommunityGlobals, getTokenBalanceForAccount } from "_app/api"; import { @@ -409,25 +410,6 @@ export const useVoteData = ( ...queryOptions, }); -export const useMemberDataFromEdenMembers = ( - members?: EdenMember[], - queryOptions: any = {} -) => { - const nftTemplateIds = members?.map((em) => em.nft_template_id); - - let enabled = Boolean(nftTemplateIds?.length); - if ("enabled" in queryOptions) { - enabled = enabled && queryOptions.enabled; - } - - return useQuery({ - ...queryMembers(1, nftTemplateIds?.length, nftTemplateIds), - staleTime: Infinity, - ...queryOptions, - enabled, - }); -}; - export const useMemberDataFromVoteData = (voteData?: VoteData[]) => { const responses = useMemberListByAccountNames( voteData?.map((participant) => participant.member) ?? [] @@ -436,19 +418,18 @@ export const useMemberDataFromVoteData = (voteData?: VoteData[]) => { const areQueriesComplete = responses.every((res) => res.isSuccess); const isLoading = responses.some((res) => res.isLoading); - const edenMembers = responses - .filter((res) => Boolean(res?.data?.nft_template_id)) - .map((res) => res.data as EdenMember); + const accountNames = responses + .filter((res) => Boolean(res?.data?.account)) + .map((res) => res.data as EdenMember) + .map((member) => member.account); - const memberDataRes = useMemberDataFromEdenMembers(edenMembers, { - enabled: !isFetchError && areQueriesComplete, - }); + const memberDataRes = useMembersByAccountNames(accountNames); return { ...memberDataRes, isLoading: memberDataRes.isLoading || isLoading, isError: memberDataRes.isError || isFetchError, - isSuccess: memberDataRes.isSuccess || areQueriesComplete, + isSuccess: areQueriesComplete, } as UseQueryResult; }; diff --git a/packages/webapp/src/elections/components/ongoing-election-components/completed-round-segment.tsx b/packages/webapp/src/elections/components/ongoing-election-components/completed-round-segment.tsx index 16b4e5076..60d0c7706 100644 --- a/packages/webapp/src/elections/components/ongoing-election-components/completed-round-segment.tsx +++ b/packages/webapp/src/elections/components/ongoing-election-components/completed-round-segment.tsx @@ -1,11 +1,7 @@ -import { - isValidDelegate, - useMemberDataFromEdenMembers, - useParticipantsInMyCompletedRound, -} from "_app"; +import { isValidDelegate, useParticipantsInMyCompletedRound } from "_app"; import { Container, Expander, Text } from "_app/ui"; import { ElectionParticipantChip } from "elections"; -import { EdenMember, MembersGrid } from "members"; +import { EdenMember, MembersGrid, useMembersByAccountNames } from "members"; import RoundHeader from "./round-header"; import { VideoUploadButton } from "./video-upload-button"; @@ -18,8 +14,9 @@ export const CompletedRoundSegment = ({ roundIndex, }: CompletedRoundSegmentProps) => { const { data } = useParticipantsInMyCompletedRound(roundIndex); - const { data: participantsMemberData } = useMemberDataFromEdenMembers( - data?.participants + + const { data: participantsMemberData } = useMembersByAccountNames( + data?.participants.map((participant) => participant.account) ); if (!participantsMemberData || !participantsMemberData.length) return null; From bac9fffcb0ad799e3d7eedd5b5bc0d249a22fa96 Mon Sep 17 00:00:00 2001 From: Brandon Fancher Date: Fri, 19 Nov 2021 17:27:15 -0500 Subject: [PATCH 33/54] Create new Member model and apply to simple component. --- packages/webapp/src/_app/ui/nav-profile.tsx | 46 +++++++++---------- .../webapp/src/members/helpers/formatters.ts | 31 ++++++++++++- packages/webapp/src/members/hooks/queries.ts | 33 ++++++++++++- packages/webapp/src/members/interfaces.ts | 33 +++++++++++++ 4 files changed, 117 insertions(+), 26 deletions(-) diff --git a/packages/webapp/src/_app/ui/nav-profile.tsx b/packages/webapp/src/_app/ui/nav-profile.tsx index 2833a28b2..ef0f21989 100644 --- a/packages/webapp/src/_app/ui/nav-profile.tsx +++ b/packages/webapp/src/_app/ui/nav-profile.tsx @@ -4,11 +4,10 @@ import { Popover } from "@headlessui/react"; import { usePopper } from "react-popper"; import { IoMdLogIn } from "react-icons/io"; -import { MemberStatus } from "_app"; -import { useCurrentMember, useSignOut } from "_app/hooks"; -import { Button, ProfileImage, Text } from "_app/ui"; import { ROUTES } from "_app/routes"; -import { useMemberByAccountName } from "members/hooks"; +import { useSignOut } from "_app/hooks"; +import { Button, ProfileImage, Text } from "_app/ui"; +import { Member, useCurrentMember } from "members"; import { useUALAccount } from "../eos"; @@ -17,15 +16,10 @@ interface Props { } export const NavProfile = ({ location }: Props) => { - const [ualAccount, _, ualShowModal] = useUALAccount(); - const accountName = ualAccount?.accountName; - + const [_ualAccount, _, ualShowModal] = useUALAccount(); const { data: member } = useCurrentMember(); - const { data: userProfile } = useMemberByAccountName(member?.account ?? ""); - - const isActiveMember = member?.status === MemberStatus.ActiveMember; - if (!ualAccount) { + if (!member?.accountName) { return (
+ +
+ + ); +}; + +export default Sessions; diff --git a/yarn.lock b/yarn.lock index 2120f0826..c7be123ca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6006,6 +6006,13 @@ iconv-lite@^0.6.2: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" +idb-keyval@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/idb-keyval/-/idb-keyval-6.0.3.tgz#e47246a15e55d0fff9fa204fd9ca06f90ff30c52" + integrity sha512-yh8V7CnE6EQMu9YDwQXhRxwZh4nv+8xm/HV4ZqK4IiYFJBWYGjJuykADJbSP+F/GDXUBwCSSNn/14IpGL81TuA== + dependencies: + safari-14-idb-fix "^3.0.0" + ieee754@^1.1.13, ieee754@^1.1.4, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" @@ -9674,6 +9681,11 @@ rxjs@^6.5.3, rxjs@^6.6.0, rxjs@^6.6.7: dependencies: tslib "^1.9.0" +safari-14-idb-fix@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/safari-14-idb-fix/-/safari-14-idb-fix-3.0.0.tgz#450fc049b996ec7f3fd9ca2f89d32e0761583440" + integrity sha512-eBNFLob4PMq8JA1dGyFn6G97q3/WzNtFK4RnzT1fnLq+9RyrGknzYiM/9B12MnKAxuj1IXr7UKYtTNtjyKMBog== + safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" From 0de0fdefa27c7e515cbbbaa3a1a84d26ae301d84 Mon Sep 17 00:00:00 2001 From: SparkPlug0025 <79721020+sparkplug0025@users.noreply.github.com> Date: Thu, 16 Dec 2021 09:53:03 -0500 Subject: [PATCH 51/54] Handle new ElectionState variant structs (#652) --- .../webapp/src/elections/api/eden-contract.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/packages/webapp/src/elections/api/eden-contract.ts b/packages/webapp/src/elections/api/eden-contract.ts index e656b8312..68c3fe3c5 100644 --- a/packages/webapp/src/elections/api/eden-contract.ts +++ b/packages/webapp/src/elections/api/eden-contract.ts @@ -276,7 +276,7 @@ export const getCurrentElection = async () => { if (devUseFixtureData) return fixtureCurrentElection; const rawRows = await getTableRawRows(CONTRACT_CURRENT_ELECTION_TABLE); - const electionState = rawRows[0][0]; + const electionState = convertElectionState(rawRows[0][0]); const rows = rawRows.map((row) => row[1]); @@ -287,6 +287,22 @@ export const getCurrentElection = async () => { return { electionState, ...rows[0] }; }; +const convertElectionState = (variantName: string) => { + switch (variantName) { + case "current_election_state_registration_v0": + case "current_election_state_registration_v1": + return "current_election_state_registration"; + case "current_election_state_seeding_v0": + case "current_election_state_seeding_v1": + return "current_election_state_seeding"; + case "current_election_state_init_voters_v0": + case "current_election_state_init_voters_v1": + return "current_election_state_init_voters"; + default: + return variantName; + } +}; + export const getElectionState = async () => { if (devUseFixtureData) return fixtureElectionState; From 2f090673227154be56851a251ee89e1b329c358d Mon Sep 17 00:00:00 2001 From: SparkPlug0025 <79721020+sparkplug0025@users.noreply.github.com> Date: Mon, 20 Dec 2021 07:25:36 -0500 Subject: [PATCH 52/54] Add local session key sequence control (#653) --- packages/webapp/src/_app/eos/sessions.ts | 43 ++++++++++++++++++++++-- packages/webapp/src/pages/sessions.tsx | 16 ++------- 2 files changed, 42 insertions(+), 17 deletions(-) diff --git a/packages/webapp/src/_app/eos/sessions.ts b/packages/webapp/src/_app/eos/sessions.ts index b6e50cb78..fad39d4b7 100644 --- a/packages/webapp/src/_app/eos/sessions.ts +++ b/packages/webapp/src/_app/eos/sessions.ts @@ -2,12 +2,12 @@ import dayjs from "dayjs"; import { generateKeyPair, sha256 } from "eosjs/dist/eosjs-key-conversions"; import { KeyType } from "eosjs/dist/eosjs-numeric"; import { PrivateKey } from "eosjs/dist/eosjs-jssig"; -import { SerialBuffer } from "eosjs/dist/eosjs-serialize"; +import { hexToUint8Array, SerialBuffer } from "eosjs/dist/eosjs-serialize"; import { get as idbGet, set as idbSet } from "idb-keyval"; import { SessionSignRequest } from "@edenos/common"; import { edenContractAccount, box } from "config"; -import { eosDefaultApi } from "_app"; +import { eosDefaultApi, eosJsonRpc } from "_app"; const DEFAULT_EXPIRATION_SECONDS = 30 * 24 * 60 * 60; // 30 days const DEFAULT_SESSION_DESCRIPTION = "eden login"; @@ -16,6 +16,7 @@ interface SessionKeyData { publicKey: string; privateKey: string; expiration: Date; + lastSequence: number; } class SessionKeysStorage { @@ -29,6 +30,20 @@ class SessionKeysStorage { async saveKey(keyData: SessionKeyData) { return idbSet(this.storageKey, keyData); } + + async advanceSequence() { + const data = await this.getKey(); + if (!data) { + throw new Error( + "Unable to advance sequence on missing session key" + ); + } + + data.lastSequence += 1; + await this.saveKey(data); + + return data.lastSequence; + } } export const sessionKeysStorage = new SessionKeysStorage(); @@ -45,6 +60,7 @@ export const generateSessionKey = async ( return { publicKey: publicKey.toString(), privateKey: privateKey.toString(), + lastSequence: 0, expiration, }; }; @@ -80,6 +96,27 @@ export const newSessionTransaction = async ( }; }; +export const signAndBroadcastSessionTransaction = async ( + authorizerAccount: string, + actions: any[] +) => { + const signedSessionTrx = await signSessionTransaction( + authorizerAccount, + actions + ); + console.info("generated signedSessionTrx trx", signedSessionTrx); + + const broadcastedRunTrx = await eosJsonRpc.send_transaction({ + signatures: signedSessionTrx.signatures, + serializedTransaction: hexToUint8Array(signedSessionTrx.packed_trx), + }); + console.info("broadcasted run trx >>>", broadcastedRunTrx); + + await sessionKeysStorage.advanceSequence(); + + return broadcastedRunTrx; +}; + export const signSessionTransaction = async ( authorizerAccount: string, actions: any[] @@ -89,7 +126,7 @@ export const signSessionTransaction = async ( throw new Error("Session key is not present"); } - const sequence = 1; // TODO: get sequence dynamically + const sequence = sessionKey.lastSequence + 1; const verbs = convertActionsToVerbs(actions); const signatureAuthSha = await makeSignatureAuthSha( diff --git a/packages/webapp/src/pages/sessions.tsx b/packages/webapp/src/pages/sessions.tsx index 5ee5864b2..19396b813 100644 --- a/packages/webapp/src/pages/sessions.tsx +++ b/packages/webapp/src/pages/sessions.tsx @@ -1,15 +1,12 @@ -import { hexToUint8Array } from "eosjs/dist/eosjs-serialize"; - import { Button, onError, SideNavLayout, useCurrentMember, useUALAccount, - eosJsonRpc, } from "_app"; import { - signSessionTransaction, + signAndBroadcastSessionTransaction, generateSessionKey, newSessionTransaction, sessionKeysStorage, @@ -60,19 +57,10 @@ export const Sessions = () => { const { actions } = inductionTrx; // sign actions with session key - const signedSessionTrx = await signSessionTransaction( + await signAndBroadcastSessionTransaction( ualAccount.accountName, actions ); - console.info("generated signedSessionTrx trx", signedSessionTrx); - - const broadcastedRunTrx = await eosJsonRpc.send_transaction({ - signatures: signedSessionTrx.signatures, - serializedTransaction: hexToUint8Array( - signedSessionTrx.packed_trx - ), - }); - console.info("broadcasted run trx >>>", broadcastedRunTrx); } catch (e) { console.error(e); onError(e as Error); From 0fd878a662a4424978d840c10695ab6527a305f5 Mon Sep 17 00:00:00 2001 From: Brandon Fancher Date: Wed, 5 Jan 2022 16:28:53 -0500 Subject: [PATCH 53/54] Update call to AH Auctions API to fix members lists. AtomicHub updated their policy to fail on invalid inputs. We had the limit set to 10000, which was invalid (100 is max). --- packages/webapp/src/members/hooks/queries.ts | 2 +- packages/webapp/src/nfts/api/nfts.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/webapp/src/members/hooks/queries.ts b/packages/webapp/src/members/hooks/queries.ts index 3f1af9282..3400ee1a7 100644 --- a/packages/webapp/src/members/hooks/queries.ts +++ b/packages/webapp/src/members/hooks/queries.ts @@ -91,7 +91,7 @@ export const queryNewMembers = (page: number, pageSize: number) => ({ }); export const useMembersWithAssets = () => { - const NEW_MEMBERS_PAGE_SIZE = 10000; + const NEW_MEMBERS_PAGE_SIZE = 100; const newMembers = useReactQuery({ ...queryNewMembers(1, NEW_MEMBERS_PAGE_SIZE), }); diff --git a/packages/webapp/src/nfts/api/nfts.ts b/packages/webapp/src/nfts/api/nfts.ts index 201c33a70..da76b6e35 100644 --- a/packages/webapp/src/nfts/api/nfts.ts +++ b/packages/webapp/src/nfts/api/nfts.ts @@ -38,7 +38,7 @@ export const getAuctions = async ( seller?: string, templateIds?: string[], page = 1, - limit = 9999 + limit = 100 // max 100 enforced by AA Auctions API ): Promise => { let url = `${atomicAssets.apiMarketUrl}/auctions?state=1&collection_name=${atomicAssets.collection}&schema_name=${atomicAssets.schema}&page=${page}&limit=${limit}&order=desc&sort=created${FETCH_AFTER_TIMESTAMP}`; From 5fedc5b11ef2b6f3574e78376ef0a232f5f2710d Mon Sep 17 00:00:00 2001 From: Brandon Fancher Date: Wed, 5 Jan 2022 17:35:49 -0500 Subject: [PATCH 54/54] Touch build.yml to retrigger jobs. --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a8a967f0f..30cc914e3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -365,7 +365,7 @@ jobs: name: Eden Microchain path: build - - name: Download Ephemeral Chain Runners + - name: Download Ephemeral Eden Chain Runners if: steps.filter.outputs.src == 'true' uses: actions/download-artifact@v2 with: