From 0b438213fed384c949e2d456828a918e17fddeb6 Mon Sep 17 00:00:00 2001 From: Andrew Falaleev Date: Fri, 6 Mar 2020 13:50:46 +0700 Subject: [PATCH 01/21] Refactor cyber.govern to remove setactprods. #317 --- cyber.bios/src/cyber.bios.cpp | 16 +++++++- .../include/cyber.govern/cyber.govern.hpp | 36 +++++++++++++----- cyber.govern/src/cyber.govern.cpp | 38 +++++++------------ tests/cyber.govern_tests.cpp | 1 + 4 files changed, 56 insertions(+), 35 deletions(-) diff --git a/cyber.bios/src/cyber.bios.cpp b/cyber.bios/src/cyber.bios.cpp index 4e216f0d2..0e3175bd6 100644 --- a/cyber.bios/src/cyber.bios.cpp +++ b/cyber.bios/src/cyber.bios.cpp @@ -29,8 +29,22 @@ void bios::onblock(ignore header) { eosio::block_timestamp timestamp; name producer; + uint32_t schedule_version; + + static constexpr std::size_t skip_size = + // eosio::block_timestamp timestamp + // name producer + 16 / 8 + // uint16_t confirmed + 256 / 8 + // eosio::block_id_type previous + 256 / 8 + // eosio::checksum256_type transaction_mroot + 256 / 8; // eosio::checksum256_type _action_mroot + // uint32_t schedule_version + // ... + _ds >> timestamp >> producer; - INLINE_ACTION_SENDER(govern, onblock)(govern_name, {{govern_name, active_name}}, {producer}); + _ds.skip(skip_size); + _ds >> schedule_version; + INLINE_ACTION_SENDER(govern, onblock)(govern_name, {{govern_name, active_name}}, {producer, schedule_version}); //TODO: update names } diff --git a/cyber.govern/include/cyber.govern/cyber.govern.hpp b/cyber.govern/include/cyber.govern/cyber.govern.hpp index c7581dc59..9652a93bb 100644 --- a/cyber.govern/include/cyber.govern/cyber.govern.hpp +++ b/cyber.govern/include/cyber.govern/cyber.govern.hpp @@ -4,6 +4,7 @@ #include #include #include +#include namespace cyber { using eosio::name; @@ -22,6 +23,27 @@ struct structures { uint32_t last_propose_block_num = 0; uint16_t required_producers_num = config::min_producers_num; uint16_t last_producers_num = 1; + + // using of binary_extension is a temporary decision only for upgrade phase + eosio::binary_extension schedule_version; + + // this operator is required to set binary extension fields + void operator = (const state_info& s) { + last_schedule_increase = s.last_schedule_increase; + block_num = s.block_num; + target_emission_per_block = s.target_emission_per_block; + funds = s.funds; + last_propose_block_num = s.last_propose_block_num; + required_producers_num = s.required_producers_num; + last_producers_num = s.last_producers_num; + + // temporary decision only for upgrade phase + if (s.schedule_version.has_value()) { + schedule_version.emplace(s.schedule_version.value()); + } else { + schedule_version.reset(); + } + } }; struct schedule_resize_info { @@ -39,11 +61,7 @@ struct structures { name account; uint64_t primary_key()const { return account.value; } }; - - struct pending_producers_info { - std::vector accounts; - }; - + struct omission_struct { name account; uint16_t count; @@ -58,12 +76,11 @@ struct structures { using balances [[eosio::order("account","asc")]] = eosio::multi_index<"balance"_n, structures::balance_struct>; using unconfirmed_balances [[eosio::order("account","asc")]] = eosio::multi_index<"uncbalance"_n, structures::balance_struct>; using obliged_producers [[eosio::order("account","asc")]] = eosio::multi_index<"obligedprod"_n, structures::producer_struct>; - using pending_producers [[eosio::order("id","asc")]] = eosio::singleton<"pendingprods"_n, structures::pending_producers_info>; - + using omission_count_index [[using eosio: order("count","desc"), non_unique]] = eosio::indexed_by<"bycount"_n, eosio::const_mem_fun >; using omissions [[eosio::order("account","asc")]] = eosio::multi_index<"omission"_n, structures::omission_struct, omission_count_index>; - void maybe_promote_producers(); + void promote_producers(); void propose_producers(structures::state_info& s); void reward_producers(balances& balances_table, structures::state_info& s); void reward_workers(structures::state_info& s); @@ -71,8 +88,7 @@ struct structures { public: using contract::contract; - [[eosio::action]] void onblock(name producer); - [[eosio::action]] void setactprods(std::vector pending_active_producers); + [[eosio::action]] void onblock(name producer, eosio::binary_extension schedule_version); [[eosio::action]] void setshift(int8_t shift); }; diff --git a/cyber.govern/src/cyber.govern.cpp b/cyber.govern/src/cyber.govern.cpp index 694684c90..694dddaaf 100644 --- a/cyber.govern/src/cyber.govern.cpp +++ b/cyber.govern/src/cyber.govern.cpp @@ -9,12 +9,12 @@ using namespace cyber::config; namespace cyber { -void govern::onblock(name producer) { +void govern::onblock(name producer, eosio::binary_extension schedule_version) { require_auth(_self); auto state = state_singleton(_self, _self.value); auto s = state.get_or_default(structures::state_info { .last_schedule_increase = eosio::current_time_point() }); - + s.block_num++; int64_t block_reward = 0; @@ -72,7 +72,14 @@ void govern::onblock(name producer) { if ((s.block_num >= s.last_producers_num * schedule_period_factor + s.last_propose_block_num) || !s.last_propose_block_num) { propose_producers(s); } - maybe_promote_producers(); + + // the schedule version temporarily has the binary extension type only for the upgrade phase + if (schedule_version.has_value()) { + if (s.schedule_version.has_value() && s.schedule_version.value() != schedule_version.value()) { + promote_producers(); + } + s.schedule_version.emplace(schedule_version.value()); + } state.set(s, _self); } @@ -198,17 +205,6 @@ int64_t govern::get_target_emission_per_block(int64_t supply) const { return emission_per_year / config::blocks_per_year; } -void govern::setactprods(std::vector pending_active_producers) { - require_auth(_self); - pending_producers pending_prods_table(_self, _self.value); - auto prods = pending_prods_table.get_or_default(structures::pending_producers_info{}); - if (!prods.accounts.empty()) { - eosio::print("WARNING! govern::setactprods, pending_prods_table was not empty\n"); - } - prods.accounts = pending_active_producers; - pending_prods_table.set(prods, _self); -} - void govern::setshift(int8_t shift) { eosio::check(schedule_size_shift_min <= shift && shift <= schedule_size_shift_max, "incorrect shift"); require_auth(producers_name); @@ -219,12 +215,7 @@ void govern::setshift(int8_t shift) { sched_state.set(sched, _self); } -void govern::maybe_promote_producers() { - pending_producers pending_prods_table(_self, _self.value); - auto prods = pending_prods_table.get_or_default(structures::pending_producers_info{}); - if (prods.accounts.empty()) { - return; - } +void govern::promote_producers() { obliged_producers obliged_prods_table(_self, _self.value); omissions omissions_table(_self, _self.value); unconfirmed_balances unconfirmed_balances_table(_self, _self.value); @@ -269,12 +260,11 @@ void govern::maybe_promote_producers() { }); } } - - for (const auto& acc : prods.accounts) { + + auto prods_accounts = eosio::get_active_producers(); + for (const auto& acc : prods_accounts) { obliged_prods_table.emplace(_self, [&](auto& p) { p = structures::producer_struct { .account = acc }; }); } - prods.accounts.clear(); - pending_prods_table.set(prods, _self); } } diff --git a/tests/cyber.govern_tests.cpp b/tests/cyber.govern_tests.cpp index 546ce0b86..8a260026b 100644 --- a/tests/cyber.govern_tests.cpp +++ b/tests/cyber.govern_tests.cpp @@ -413,6 +413,7 @@ BOOST_FIXTURE_TEST_CASE(producer_replacement, cyber_govern_tester) try { BOOST_CHECK_EQUAL(stake.get_candidate(cur_producers[p], token._symbol)["signing_key"].as(), get_public_key(cur_producers[p], "active")); } govern.wait_schedule_activation(true, {cur_producers[0]}); + produce_block(); // producer key resetting happens on the second block of the schedule round BOOST_CHECK_EQUAL(govern.get_active_producers(), govern.make_producers_group(cur_producers)); BOOST_CHECK_EQUAL(stake.get_candidate(cur_producers[0], token._symbol)["signing_key"].as(), public_key_type()); for (size_t p = 1; p < cur_producers.size(); p++) { From 8b7c08081853c5e449c957daed75ff4185636f3e Mon Sep 17 00:00:00 2001 From: Andrew Falaleev Date: Fri, 6 Mar 2020 19:19:53 +0700 Subject: [PATCH 02/21] Refactor cyber.govern::schedresize table. #317 --- .../include/cyber.govern/cyber.govern.hpp | 23 ++++++------ cyber.govern/src/cyber.govern.cpp | 35 ++++++++++--------- 2 files changed, 32 insertions(+), 26 deletions(-) diff --git a/cyber.govern/include/cyber.govern/cyber.govern.hpp b/cyber.govern/include/cyber.govern/cyber.govern.hpp index 9652a93bb..73f657df3 100644 --- a/cyber.govern/include/cyber.govern/cyber.govern.hpp +++ b/cyber.govern/include/cyber.govern/cyber.govern.hpp @@ -25,6 +25,8 @@ struct structures { uint16_t last_producers_num = 1; // using of binary_extension is a temporary decision only for upgrade phase + eosio::binary_extension last_resize_step; + eosio::binary_extension resize_shift; eosio::binary_extension schedule_version; // this operator is required to set binary extension fields @@ -37,20 +39,22 @@ struct structures { required_producers_num = s.required_producers_num; last_producers_num = s.last_producers_num; + set_extension_field(last_resize_step, s.last_resize_step); + set_extension_field(resize_shift, s.resize_shift); + set_extension_field(schedule_version, s.schedule_version); + } + + template + void set_extension_field(eosio::binary_extension& dst, const eosio::binary_extension& src) const { // temporary decision only for upgrade phase - if (s.schedule_version.has_value()) { - schedule_version.emplace(s.schedule_version.value()); + if (src.has_value()) { + dst.emplace(src.value()); } else { - schedule_version.reset(); + dst.reset(); } } }; - - struct schedule_resize_info { - time_point_sec last_step; - int8_t shift = 1; - }; - + struct [[using eosio: event("burnreward"), contract("cyber.govern")]] balance_struct { name account; int64_t amount; @@ -72,7 +76,6 @@ struct structures { }; using state_singleton [[eosio::order("id","asc")]] = eosio::singleton<"governstate"_n, structures::state_info>; - using schedule_resize_singleton [[eosio::order("id","asc")]] = eosio::singleton<"schedresize"_n, structures::schedule_resize_info>; using balances [[eosio::order("account","asc")]] = eosio::multi_index<"balance"_n, structures::balance_struct>; using unconfirmed_balances [[eosio::order("account","asc")]] = eosio::multi_index<"uncbalance"_n, structures::balance_struct>; using obliged_producers [[eosio::order("account","asc")]] = eosio::multi_index<"obligedprod"_n, structures::producer_struct>; diff --git a/cyber.govern/src/cyber.govern.cpp b/cyber.govern/src/cyber.govern.cpp index 694dddaaf..d99e22477 100644 --- a/cyber.govern/src/cyber.govern.cpp +++ b/cyber.govern/src/cyber.govern.cpp @@ -151,24 +151,27 @@ void govern::reward_producers(balances& balances_table, structures::state_info& void govern::propose_producers(structures::state_info& s) { s.last_propose_block_num = s.block_num; - - auto sched_state = schedule_resize_singleton(_self, _self.value); - auto sched = sched_state.get_or_default(structures::schedule_resize_info { .last_step = eosio::current_time_point() }); - - if ((eosio::current_time_point() - sched.last_step).to_seconds() >= schedule_resize_min_delay) { - s.required_producers_num += sched.shift; + + if (!s.last_resize_step.has_value()) { + s.last_resize_step.emplace(eosio::current_time_point()); + } + if (!s.resize_shift.has_value()) { + s.resize_shift.emplace(1); + } + + if ((eosio::current_time_point() - s.last_resize_step.value()).to_seconds() >= schedule_resize_min_delay) { + s.required_producers_num += s.resize_shift.value(); s.required_producers_num = std::min(std::max(s.required_producers_num, min_producers_num), max_producers_num); - sched.last_step = eosio::current_time_point(); + s.last_resize_step.emplace(eosio::current_time_point()); } - sched_state.set(sched, _self); - + auto new_producers = stake::get_top(system_token.code(), s.required_producers_num - active_reserve_producers_num, active_reserve_producers_num); auto new_producers_num = new_producers.size(); auto min_new_producers_num = s.last_producers_num; - if (sched.shift < 0) { - min_new_producers_num -= std::min(min_new_producers_num, std::abs(sched.shift)); + if (s.resize_shift.value() < 0) { + min_new_producers_num -= std::min(min_new_producers_num, std::abs(s.resize_shift.value())); } if (new_producers_num < min_new_producers_num) { return; @@ -208,11 +211,11 @@ int64_t govern::get_target_emission_per_block(int64_t supply) const { void govern::setshift(int8_t shift) { eosio::check(schedule_size_shift_min <= shift && shift <= schedule_size_shift_max, "incorrect shift"); require_auth(producers_name); - auto sched_state = schedule_resize_singleton(_self, _self.value); - auto sched = sched_state.get_or_default(structures::schedule_resize_info { .last_step = eosio::current_time_point() }); - eosio::check(shift != sched.shift, "the shift has not changed"); - sched.shift = shift; - sched_state.set(sched, _self); + auto state = state_singleton(_self, _self.value); + auto s = state.get(); // no default values, because it was created on the first block, errors can happens only in tests + eosio::check(shift != s.resize_shift.value(), "the shift has not changed"); + s.resize_shift.emplace(shift); + state.set(s, _self); } void govern::promote_producers() { From 1b892f3b7a05e0faf10f3f663df7e7ba7b1ca1e6 Mon Sep 17 00:00:00 2001 From: Andrew Falaleev Date: Wed, 11 Mar 2020 00:27:20 +0700 Subject: [PATCH 03/21] Refactor govern tables to the table with producers. #317 --- cyber.govern/include/cyber.govern/config.hpp | 4 +- .../include/cyber.govern/cyber.govern.hpp | 26 +-- cyber.govern/src/cyber.govern.cpp | 186 ++++++++++-------- tests/cyber.govern_test_api.hpp | 12 +- tests/cyber.govern_tests.cpp | 6 +- 5 files changed, 129 insertions(+), 105 deletions(-) diff --git a/cyber.govern/include/cyber.govern/config.hpp b/cyber.govern/include/cyber.govern/config.hpp index 8e5b3a97d..7f4e6e2f9 100644 --- a/cyber.govern/include/cyber.govern/config.hpp +++ b/cyber.govern/include/cyber.govern/config.hpp @@ -34,8 +34,8 @@ static constexpr auto emission_max_arg = 75 * _1percent; static constexpr auto block_reward_pct = 10 * _1percent; static constexpr auto workers_reward_pct = 2222 * _1percent / 100; // not including block reward -static uint16_t omission_limit = 100; -static uint16_t resets_limit = 4; +static constexpr uint16_t omission_limit = 100; +static constexpr uint16_t resets_limit = 4; } } // cyber::config diff --git a/cyber.govern/include/cyber.govern/cyber.govern.hpp b/cyber.govern/include/cyber.govern/cyber.govern.hpp index 73f657df3..c38c16644 100644 --- a/cyber.govern/include/cyber.govern/cyber.govern.hpp +++ b/cyber.govern/include/cyber.govern/cyber.govern.hpp @@ -63,29 +63,29 @@ struct structures { struct producer_struct { name account; - uint64_t primary_key()const { return account.value; } - }; + bool is_oblidged = false; + int64_t unconfirmed_amount = 0; + uint16_t omission_count = 0; + uint16_t omission_resets = 0; - struct omission_struct { - name account; - uint16_t count; - uint16_t resets = 0; uint64_t primary_key()const { return account.value; } - uint16_t by_count()const { return count; } + bool by_oblidged()const { return is_oblidged; } + + bool is_empty() const { + return !unconfirmed_amount && !omission_count && !omission_resets && !is_oblidged; + } }; }; using state_singleton [[eosio::order("id","asc")]] = eosio::singleton<"governstate"_n, structures::state_info>; using balances [[eosio::order("account","asc")]] = eosio::multi_index<"balance"_n, structures::balance_struct>; - using unconfirmed_balances [[eosio::order("account","asc")]] = eosio::multi_index<"uncbalance"_n, structures::balance_struct>; - using obliged_producers [[eosio::order("account","asc")]] = eosio::multi_index<"obligedprod"_n, structures::producer_struct>; - using omission_count_index [[using eosio: order("count","desc"), non_unique]] = eosio::indexed_by<"bycount"_n, eosio::const_mem_fun >; - using omissions [[eosio::order("account","asc")]] = eosio::multi_index<"omission"_n, structures::omission_struct, omission_count_index>; + using oblidged_index [[using eosio: order("is_oblidged","desc"), non_unique]] = eosio::indexed_by<"byoblidged"_n, eosio::const_mem_fun >; + using producers [[eosio::order("account","asc")]] = eosio::multi_index<"producer"_n, structures::producer_struct, oblidged_index>; - void promote_producers(); + void promote_producers(producers& producers_table); void propose_producers(structures::state_info& s); - void reward_producers(balances& balances_table, structures::state_info& s); + void reward_producers(producers& producers_table, balances& balances_table, structures::state_info& s); void reward_workers(structures::state_info& s); int64_t get_target_emission_per_block(int64_t supply) const; diff --git a/cyber.govern/src/cyber.govern.cpp b/cyber.govern/src/cyber.govern.cpp index d99e22477..53871a2e0 100644 --- a/cyber.govern/src/cyber.govern.cpp +++ b/cyber.govern/src/cyber.govern.cpp @@ -4,6 +4,7 @@ #include #include #include +#include using namespace cyber::config; @@ -16,7 +17,7 @@ void govern::onblock(name producer, eosio::binary_extension schedule_v auto s = state.get_or_default(structures::state_info { .last_schedule_increase = eosio::current_time_point() }); s.block_num++; - + int64_t block_reward = 0; if (producer != config::internal_name) { auto supply = eosio::token::get_supply (config::token_name, system_token.code()).amount; @@ -30,27 +31,21 @@ void govern::onblock(name producer, eosio::binary_extension schedule_v block_reward = safe_pct(cur_block_emission, config::block_reward_pct); s.funds += cur_block_emission - block_reward; } - + balances balances_table(_self, _self.value); - - obliged_producers obliged_prods_table(_self, _self.value); - omissions omissions_table(_self, _self.value); - unconfirmed_balances unconfirmed_balances_table(_self, _self.value); - + producers producers_table(_self, _self.value); + if (producer != config::internal_name && s.block_num != 1) { int64_t just_confirmed_balance = 0; - auto u = unconfirmed_balances_table.find(producer.value); - if (u != unconfirmed_balances_table.end()) { - just_confirmed_balance = u->amount; - unconfirmed_balances_table.erase(u); - } - auto p = obliged_prods_table.find(producer.value); - if (p != obliged_prods_table.end()) { - obliged_prods_table.erase(p); - } - auto o = omissions_table.find(producer.value); - if (o != omissions_table.end()) { - omissions_table.erase(o); + auto utr = producers_table.find(producer.value); + if (utr != producers_table.end() && !utr->is_empty()) { + just_confirmed_balance = utr->unconfirmed_amount; + producers_table.modify(utr, eosio::same_payer, [&](auto& u) { + u.unconfirmed_amount = 0; + u.omission_resets = 0; + u.omission_count = 0; + u.is_oblidged = false; + }); } auto b = balances_table.find(producer.value); if (b != balances_table.end()) { @@ -65,7 +60,7 @@ void govern::onblock(name producer, eosio::binary_extension schedule_v } if (s.block_num % config::reward_interval == 0) { - reward_producers(balances_table, s); + reward_producers(producers_table, balances_table, s); reward_workers(s); } @@ -76,7 +71,7 @@ void govern::onblock(name producer, eosio::binary_extension schedule_v // the schedule version temporarily has the binary extension type only for the upgrade phase if (schedule_version.has_value()) { if (s.schedule_version.has_value() && s.schedule_version.value() != schedule_version.value()) { - promote_producers(); + promote_producers(producers_table); } s.schedule_version.emplace(schedule_version.value()); } @@ -92,22 +87,22 @@ void govern::reward_workers(structures::state_info& s) { } } -void govern::reward_producers(balances& balances_table, structures::state_info& s) { - std::map rewards; +void govern::reward_producers(producers& producers_table, balances& balances_table, structures::state_info& s) { + std::vector> rewards; + rewards.reserve(config::max_producers_num + 16); for (auto i = balances_table.begin(); i != balances_table.end();) { if (i->amount) { - rewards[i->account] += i->amount; + rewards.emplace_back(i->account, i->amount); } i = balances_table.erase(i); } if (rewards.size()) { INLINE_ACTION_SENDER(cyber::stake, reward)(config::stake_name, {config::issuer_name, config::active_name}, - {std::vector >(rewards.begin(), rewards.end()), system_token}); + {rewards, system_token}); } auto top = stake::get_top(system_token.code(), s.required_producers_num + rewarded_for_votes_limit_displ, 0); - auto actual_elected_num = top.size(); int64_t votes_sum = 0; for (const auto& t : top) { @@ -120,31 +115,39 @@ void govern::reward_producers(balances& balances_table, structures::state_info& return; } - std::map unconfirmed_rewards; + std::vector> unconfirmed_rewards; + unconfirmed_rewards.reserve(actual_elected_num + 16); auto change = reward_of_elected; - for (size_t i = 0; i < actual_elected_num; i++) { - auto cur_reward = safe_prop(reward_of_elected, top[i].votes, votes_sum); + for (const auto& t : top) { + auto cur_reward = safe_prop(reward_of_elected, t.votes, votes_sum); if (cur_reward) { - unconfirmed_rewards[top[i].account] += cur_reward; + unconfirmed_rewards.emplace_back(t.account, cur_reward); change -= cur_reward; } } if (change) { - unconfirmed_rewards[top[s.block_num % actual_elected_num].account] += change; + auto idx = s.block_num % actual_elected_num; + if (idx >= unconfirmed_rewards.size()) { + unconfirmed_rewards.emplace_back(top[idx].account, change); + } else { + unconfirmed_rewards[idx].second += change; + } } s.funds -= reward_of_elected; - - unconfirmed_balances unconfirmed_balances_table(_self, _self.value); + for (auto& r : unconfirmed_rewards) { - auto b = unconfirmed_balances_table.find(r.first.value); - if (b != unconfirmed_balances_table.end()) { - unconfirmed_balances_table.modify(b, name(), [&](auto& b) { b.amount += r.second; } ); + auto b = producers_table.find(r.first.value); + if (b != producers_table.end()) { + producers_table.modify(b, name(), [&](auto& b) { + b.unconfirmed_amount += r.second; + }); } else { - unconfirmed_balances_table.emplace(_self, [&](auto& b) { b = structures::balance_struct { - .account = r.first, - .amount = r.second - };}); + producers_table.emplace(_self, [&](auto& b) { + b.is_oblidged = false; + b.account = r.first; + b.unconfirmed_amount = r.second; + }); } } } @@ -178,7 +181,7 @@ void govern::propose_producers(structures::state_info& s) { } std::vector schedule; - schedule.reserve(new_producers_num); + schedule.reserve(new_producers_num + 16); for (const auto& t : new_producers) { schedule.emplace_back(eosio::producer_key{t.account, t.signing_key}); } @@ -187,7 +190,7 @@ void govern::propose_producers(structures::state_info& s) { } s.last_producers_num = new_producers_num; std::vector accounts; - accounts.reserve(new_producers_num); + accounts.reserve(new_producers_num + 16); for (const auto& t : new_producers) { accounts.emplace_back(t.account); } @@ -218,55 +221,70 @@ void govern::setshift(int8_t shift) { state.set(s, _self); } -void govern::promote_producers() { - obliged_producers obliged_prods_table(_self, _self.value); - omissions omissions_table(_self, _self.value); - unconfirmed_balances unconfirmed_balances_table(_self, _self.value); - - for (auto i = obliged_prods_table.begin(); i != obliged_prods_table.end();) { - auto o = omissions_table.find(i->account.value); - if (o != omissions_table.end()) { - omissions_table.modify(o, name(), [&](auto& o) { o.count += 1; } ); - } - else { - omissions_table.emplace(_self, [&](auto& o) { o = structures::omission_struct { - .account = i->account, - .count = 1 - };}); - } - - auto b = unconfirmed_balances_table.find(i->account.value); - if (b != unconfirmed_balances_table.end()) { - eosio::event(_self, "burnreward"_n, *b).send(); - unconfirmed_balances_table.erase(b); - } - i = obliged_prods_table.erase(i); +void govern::promote_producers(producers& producers_table) { + static constexpr auto token_code = system_token.code(); + auto obliged_idx = producers_table.get_index<"byoblidged"_n>(); + boost::container::flat_set active_producers; + + active_producers.reserve(config::max_producers_num + 16); + for (const auto& acc: eosio::get_active_producers()) { + active_producers.insert(acc); } - symbol_code token_code = system_token.code(); - - auto omissions_idx = omissions_table.get_index<"bycount"_n>(); - auto omission_itr = omissions_idx.lower_bound(std::numeric_limits::max()); - if (omission_itr != omissions_idx.end() && omission_itr->count >= config::omission_limit) { - if (omission_itr->resets >= config::resets_limit) { + for (auto itr = obliged_idx.begin(); itr != obliged_idx.end() && itr->is_oblidged; ) { + bool should_erase = false; + if (!cyber::stake::candidate_exists(itr->account, token_code)) { + should_erase = true; + } else if (itr->omission_resets >= config::resets_limit) { INLINE_ACTION_SENDER(cyber::stake, setproxylvl)(config::stake_name, {config::issuer_name, config::active_name}, - {omission_itr->account, token_code, stake::get_max_level(token_code)}); // agent cannot disappear - omissions_idx.erase(omission_itr); + {itr->account, token_code, stake::get_max_level(token_code)}); // agent cannot disappear + should_erase = true; } - else { - if (cyber::stake::candidate_exists(omission_itr->account, token_code)) { - INLINE_ACTION_SENDER(cyber::stake, setkey)(config::stake_name, {config::issuer_name, config::active_name}, - {omission_itr->account, token_code, public_key{}}); + + auto atr = active_producers.find(itr->account); + if (should_erase) { + if (active_producers.end() != atr) { + active_producers.erase(atr); } - omissions_idx.modify(omission_itr, name(), [&](auto& o) { - o.count = 0; - o.resets += 1; - }); + burn_reward(itr->account, itr->amount + itr->unconfirmed_amount); + itr = obliged_idx.erase(itr); + continue; } + + burn_reward(itr->account, itr->unconfirmed_amount); + + obliged_idx.modify(itr, eosio::same_payer, [&](auto& o){ + if (active_producers.end() != atr) { + active_producers.erase(atr); + } else { + o.is_oblidged = false; + } + + o.unconfirmed_amount = 0; + o.omission_count += 1; + + if (o.omission_count >= config::omission_limit) { + INLINE_ACTION_SENDER(cyber::stake, setkey)(config::stake_name, {config::issuer_name, config::active_name}, + {o.account, token_code, public_key{}}); + + o.omission_count = 0; + o.omission_resets += 1; + } + }); + ++itr; } - auto prods_accounts = eosio::get_active_producers(); - for (const auto& acc : prods_accounts) { - obliged_prods_table.emplace(_self, [&](auto& p) { p = structures::producer_struct { .account = acc }; }); + for (const auto& acc : active_producers) { + auto itr = producers_table.find(acc.value); + if (producers_table.end() == itr) { + producers_table.emplace(_self, [&](auto& p) { + p.account = acc; + p.is_oblidged = true; + }); + } else if (!itr->is_oblidged) { + producers_table.modify(itr, eosio::same_payer, [&](auto& p) { + p.is_oblidged = true; + }); + } } } diff --git a/tests/cyber.govern_test_api.hpp b/tests/cyber.govern_test_api.hpp index d5cf5c282..3011b870c 100644 --- a/tests/cyber.govern_test_api.hpp +++ b/tests/cyber.govern_test_api.hpp @@ -24,8 +24,14 @@ struct cyber_govern_api: base_contract_api { ////tables int64_t get_balance(account_name account, bool confirmed = true) { - auto s = get_struct(_code, confirmed ? N(balance) : N(uncbalance), account.value, "balance_struct"); - return !s.is_null() ? s["amount"].as() : -1; + if (confirmed) { + auto s = get_struct(_code, N(balance), account.value, "balance_struct"); + return !s.is_null() ? s["amount"].as() : -1; + } else { + auto s = get_struct(_code, N(producer), account.value, "producer_struct"); + auto r = !s.is_null() ? s["unconfirmed_amount"].as() : -1; + return (!r) ? (-1) : r; + } } variant get_state()const { @@ -98,7 +104,7 @@ struct cyber_govern_api: base_contract_api { BOOST_REQUIRE_EQUAL(_tester->control->head_block_header().schedule_version, prev_version); BOOST_REQUIRE_EQUAL(get_block_offset(), prev_block_offset); - BOOST_REQUIRE(_tester->control->pending_block_state()->active_schedule.version == prev_version + static_cast(change_version)); + BOOST_REQUIRE_EQUAL(_tester->control->pending_block_state()->active_schedule.version, prev_version + static_cast(change_version)); auto ret = _tester->control->head_block_num() - prev_block; BOOST_TEST_MESSAGE("--- waited " << ret << " more blocks for schedule activation"); ret += blocks_for_update; diff --git a/tests/cyber.govern_tests.cpp b/tests/cyber.govern_tests.cpp index 8a260026b..e6f66cef8 100644 --- a/tests/cyber.govern_tests.cpp +++ b/tests/cyber.govern_tests.cpp @@ -270,10 +270,10 @@ BOOST_FIXTURE_TEST_CASE(no_rewards_test, cyber_govern_tester) try { BOOST_CHECK(stake.get_agent(_whale, token._symbol)["balance"].as() > init_amount); BOOST_CHECK_EQUAL(stake.get_candidate(_bob, token._symbol)["signing_key"].as(), public_key_type()); - BOOST_CHECK(govern.get_balance(_alice, false) > 0); + BOOST_CHECK_GT(govern.get_balance(_alice, false), 0); BOOST_CHECK_EQUAL(govern.get_balance(_bob, false), -1); - BOOST_CHECK(govern.get_balance(_carol, false) > 0); - BOOST_CHECK(govern.get_balance(_whale, false) > 0); + BOOST_CHECK_GT(govern.get_balance(_carol, false), 0); + BOOST_CHECK_GT(govern.get_balance(_whale, false), 0); BOOST_CHECK_EQUAL(govern.get_balance(_alice), -1); BOOST_CHECK_EQUAL(govern.get_balance(_bob), -1); From 110268d0d49e5d9fc45b008f018c6945b4cf16a2 Mon Sep 17 00:00:00 2001 From: Andrew Falaleev Date: Fri, 13 Mar 2020 16:31:16 +0700 Subject: [PATCH 04/21] Refactor cyber.govern::balance to part of cyber.govern::producer. #317 --- .../include/cyber.govern/cyber.govern.hpp | 8 +- cyber.govern/src/cyber.govern.cpp | 172 +++++++++++------- tests/cyber.govern_test_api.hpp | 11 +- tests/cyber.govern_tests.cpp | 3 +- 4 files changed, 121 insertions(+), 73 deletions(-) diff --git a/cyber.govern/include/cyber.govern/cyber.govern.hpp b/cyber.govern/include/cyber.govern/cyber.govern.hpp index c38c16644..18a1a745e 100644 --- a/cyber.govern/include/cyber.govern/cyber.govern.hpp +++ b/cyber.govern/include/cyber.govern/cyber.govern.hpp @@ -64,12 +64,14 @@ struct structures { struct producer_struct { name account; bool is_oblidged = false; + int64_t amount = 0; int64_t unconfirmed_amount = 0; uint16_t omission_count = 0; uint16_t omission_resets = 0; uint64_t primary_key()const { return account.value; } bool by_oblidged()const { return is_oblidged; } + int64_t by_amount()const { return amount; } bool is_empty() const { return !unconfirmed_amount && !omission_count && !omission_resets && !is_oblidged; @@ -81,12 +83,14 @@ struct structures { using balances [[eosio::order("account","asc")]] = eosio::multi_index<"balance"_n, structures::balance_struct>; using oblidged_index [[using eosio: order("is_oblidged","desc"), non_unique]] = eosio::indexed_by<"byoblidged"_n, eosio::const_mem_fun >; - using producers [[eosio::order("account","asc")]] = eosio::multi_index<"producer"_n, structures::producer_struct, oblidged_index>; + using balance_index [[using eosio: order("amount","desc"), non_unique]] = eosio::indexed_by<"bybalance"_n, eosio::const_mem_fun >; + using producers [[eosio::order("account","asc")]] = eosio::multi_index<"producer"_n, structures::producer_struct, oblidged_index, balance_index>; void promote_producers(producers& producers_table); void propose_producers(structures::state_info& s); - void reward_producers(producers& producers_table, balances& balances_table, structures::state_info& s); + void reward_producers(producers& producers_table, structures::state_info& s); void reward_workers(structures::state_info& s); + void burn_reward(const eosio::name& account, const int64_t& amount) const; int64_t get_target_emission_per_block(int64_t supply) const; public: diff --git a/cyber.govern/src/cyber.govern.cpp b/cyber.govern/src/cyber.govern.cpp index 53871a2e0..34a91099a 100644 --- a/cyber.govern/src/cyber.govern.cpp +++ b/cyber.govern/src/cyber.govern.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include using namespace cyber::config; @@ -32,35 +33,29 @@ void govern::onblock(name producer, eosio::binary_extension schedule_v s.funds += cur_block_emission - block_reward; } - balances balances_table(_self, _self.value); producers producers_table(_self, _self.value); if (producer != config::internal_name && s.block_num != 1) { - int64_t just_confirmed_balance = 0; auto utr = producers_table.find(producer.value); - if (utr != producers_table.end() && !utr->is_empty()) { - just_confirmed_balance = utr->unconfirmed_amount; + if (utr != producers_table.end()) { producers_table.modify(utr, eosio::same_payer, [&](auto& u) { + u.amount += block_reward + u.unconfirmed_amount; u.unconfirmed_amount = 0; u.omission_resets = 0; u.omission_count = 0; u.is_oblidged = false; }); - } - auto b = balances_table.find(producer.value); - if (b != balances_table.end()) { - balances_table.modify(b, name(), [&](auto& b) { b.amount += block_reward + just_confirmed_balance; } ); - } - else { - balances_table.emplace(_self, [&](auto& b) { b = structures::balance_struct { - .account = producer, - .amount = block_reward + just_confirmed_balance - };}); + } else if (block_reward > 0) { + producers_table.emplace(_self, [&](auto& u) { + u.account = producer; + u.amount = block_reward; + u.is_oblidged = false; + }); } } if (s.block_num % config::reward_interval == 0) { - reward_producers(producers_table, balances_table, s); + reward_producers(producers_table, s); reward_workers(s); } @@ -87,69 +82,122 @@ void govern::reward_workers(structures::state_info& s) { } } -void govern::reward_producers(producers& producers_table, balances& balances_table, structures::state_info& s) { - std::vector> rewards; +void govern::burn_reward(const eosio::name& account, const int64_t& amount) const { + if (amount > 0) { + auto data = structures::balance_struct{account, amount}; + eosio::event(_self, "burnreward"_n, data).send(); + } +} + +void govern::reward_producers(producers& producers_table, structures::state_info& s) { + static constexpr auto token_code = system_token.code(); + std::vector> unconfirmed_rewards; + std::vector> rewards; + + unconfirmed_rewards.reserve(config::max_producers_num + 16); rewards.reserve(config::max_producers_num + 16); - for (auto i = balances_table.begin(); i != balances_table.end();) { - if (i->amount) { - rewards.emplace_back(i->account, i->amount); + + // the temporary decision only for the upgrade phase, + // the balances table can be removed after migrating to the producers table + boost::container::flat_map old_rewards; + balances balances_table(_self, _self.value); + + old_rewards.reserve(config::max_producers_num + 16); + for (auto itr = balances_table.begin(); itr != balances_table.end();) { + if (itr->amount) { + old_rewards.emplace(itr->account, itr->amount); } - i = balances_table.erase(i); - } - - if (rewards.size()) { - INLINE_ACTION_SENDER(cyber::stake, reward)(config::stake_name, {config::issuer_name, config::active_name}, - {rewards, system_token}); + itr = balances_table.erase(itr); } - + + auto get_old_reward = [&](const eosio::name& account) -> int64_t { + int64_t amount = 0; + auto itr = old_rewards.find(account); + if (old_rewards.end() != itr) { + amount = itr->second; + old_rewards.erase(itr); + } + return amount; + }; + // end of the temporary decision + auto top = stake::get_top(system_token.code(), s.required_producers_num + rewarded_for_votes_limit_displ, 0); - auto actual_elected_num = top.size(); int64_t votes_sum = 0; for (const auto& t : top) { votes_sum += t.votes; } - + auto reward_of_elected = safe_pct(s.funds, config::_100percent - config::workers_reward_pct); - - if (!votes_sum || !reward_of_elected) { - return; - } - - std::vector> unconfirmed_rewards; - unconfirmed_rewards.reserve(actual_elected_num + 16); - auto change = reward_of_elected; - for (const auto& t : top) { - auto cur_reward = safe_prop(reward_of_elected, t.votes, votes_sum); - if (cur_reward) { - unconfirmed_rewards.emplace_back(t.account, cur_reward); - change -= cur_reward; + if (votes_sum && reward_of_elected) { + s.funds -= reward_of_elected; + + auto change = reward_of_elected; + for (const auto& t : top) { + auto cur_reward = safe_prop(reward_of_elected, t.votes, votes_sum); + if (cur_reward) { + unconfirmed_rewards.emplace_back(t.account, cur_reward); + change -= cur_reward; + } else { + break; + } } - } - if (change) { - auto idx = s.block_num % actual_elected_num; - if (idx >= unconfirmed_rewards.size()) { - unconfirmed_rewards.emplace_back(top[idx].account, change); - } else { - unconfirmed_rewards[idx].second += change; + + if (change) { + if (!unconfirmed_rewards.empty()) { + auto idx = s.block_num % unconfirmed_rewards.size(); + unconfirmed_rewards[idx].second += change; + } else { + auto idx = s.block_num % top.size(); + unconfirmed_rewards.emplace_back(top[idx].account, change); + } } - } - s.funds -= reward_of_elected; - for (auto& r : unconfirmed_rewards) { - auto b = producers_table.find(r.first.value); - if (b != producers_table.end()) { - producers_table.modify(b, name(), [&](auto& b) { - b.unconfirmed_amount += r.second; - }); + for (auto& r: unconfirmed_rewards) { + auto btr = producers_table.find(r.first.value); + int64_t amount = get_old_reward(r.first); + + if (btr != producers_table.end()) { + producers_table.modify(btr, name(), [&](auto& b) { + amount += b.amount; + b.amount = 0; + b.unconfirmed_amount += r.second; + }); + } else { + producers_table.emplace(_self, [&](auto& b) { + b.is_oblidged = false; + b.account = r.first; + b.unconfirmed_amount = r.second; + }); + } + + if (amount > 0) { + rewards.emplace_back(r.first, amount); + } } - else { - producers_table.emplace(_self, [&](auto& b) { - b.is_oblidged = false; - b.account = r.first; - b.unconfirmed_amount = r.second; + } + + auto balances_idx = producers_table.get_index<"bybalance"_n>(); + for (auto itr = balances_idx.begin(); itr != balances_idx.end() && itr->amount;) { + rewards.emplace_back(itr->account, itr->amount + get_old_reward(itr->account)); + if (!cyber::stake::candidate_exists(itr->account, token_code)) { + burn_reward(itr->account, itr->unconfirmed_amount); + itr = balances_idx.erase(itr); + } else { + balances_idx.modify(itr, eosio::same_payer, [&](auto& u){ + u.amount = 0; }); + ++itr; } } + + for (const auto& r: old_rewards) { + rewards.emplace_back(r.first, r.second); + } + + if (rewards.size()) { + INLINE_ACTION_SENDER(cyber::stake, reward)(config::stake_name, {config::issuer_name, config::active_name}, + {rewards, system_token}); + } } void govern::propose_producers(structures::state_info& s) { diff --git a/tests/cyber.govern_test_api.hpp b/tests/cyber.govern_test_api.hpp index 3011b870c..5876c0078 100644 --- a/tests/cyber.govern_test_api.hpp +++ b/tests/cyber.govern_test_api.hpp @@ -24,14 +24,9 @@ struct cyber_govern_api: base_contract_api { ////tables int64_t get_balance(account_name account, bool confirmed = true) { - if (confirmed) { - auto s = get_struct(_code, N(balance), account.value, "balance_struct"); - return !s.is_null() ? s["amount"].as() : -1; - } else { - auto s = get_struct(_code, N(producer), account.value, "producer_struct"); - auto r = !s.is_null() ? s["unconfirmed_amount"].as() : -1; - return (!r) ? (-1) : r; - } + auto s = get_struct(_code, N(producer), account.value, "producer_struct"); + auto r = !s.is_null() ? s[confirmed ? "amount" : "unconfirmed_amount"].as() : -1; + return (!r) ? (-1) : r; } variant get_state()const { diff --git a/tests/cyber.govern_tests.cpp b/tests/cyber.govern_tests.cpp index e6f66cef8..2908ffabc 100644 --- a/tests/cyber.govern_tests.cpp +++ b/tests/cyber.govern_tests.cpp @@ -118,7 +118,8 @@ class cyber_govern_tester : public golos_tester { produce_block(); auto emission = get_emission_per_block(rate); BOOST_CHECK_EQUAL(govern.get_target_emission_per_block(), emission); - BOOST_CHECK_EQUAL(govern.get_balance(_alice), + auto alice_balance = govern.get_balance(_alice); + BOOST_CHECK_EQUAL(alice_balance == -1 ? 0 : alice_balance, supply_amount != max_supply_amount ? safe_pct(cfg::block_reward_pct, emission) : 0); produce_block(); } From 67338a4f408064c2cb9dfd55f1c4accfeb7c23e9 Mon Sep 17 00:00:00 2001 From: Andrew Falaleev Date: Sun, 15 Mar 2020 22:41:10 +0700 Subject: [PATCH 05/21] Add clearing of old producers in cyber.govern. #317 --- .../include/cyber.govern/cyber.govern.hpp | 10 +++--- cyber.govern/src/cyber.govern.cpp | 35 +++++++++++++------ 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/cyber.govern/include/cyber.govern/cyber.govern.hpp b/cyber.govern/include/cyber.govern/cyber.govern.hpp index 18a1a745e..a8e799718 100644 --- a/cyber.govern/include/cyber.govern/cyber.govern.hpp +++ b/cyber.govern/include/cyber.govern/cyber.govern.hpp @@ -68,14 +68,12 @@ struct structures { int64_t unconfirmed_amount = 0; uint16_t omission_count = 0; uint16_t omission_resets = 0; + eosio::time_point_sec last_time; uint64_t primary_key()const { return account.value; } bool by_oblidged()const { return is_oblidged; } int64_t by_amount()const { return amount; } - - bool is_empty() const { - return !unconfirmed_amount && !omission_count && !omission_resets && !is_oblidged; - } + eosio::time_point_sec by_last_time()const { return last_time; } }; }; @@ -84,13 +82,15 @@ struct structures { using oblidged_index [[using eosio: order("is_oblidged","desc"), non_unique]] = eosio::indexed_by<"byoblidged"_n, eosio::const_mem_fun >; using balance_index [[using eosio: order("amount","desc"), non_unique]] = eosio::indexed_by<"bybalance"_n, eosio::const_mem_fun >; - using producers [[eosio::order("account","asc")]] = eosio::multi_index<"producer"_n, structures::producer_struct, oblidged_index, balance_index>; + using last_time_index [[using eosio: order("last_time","asc"), non_unique]] = eosio::indexed_by<"bytime"_n, eosio::const_mem_fun >; + using producers [[eosio::order("account","asc")]] = eosio::multi_index<"producer"_n, structures::producer_struct, oblidged_index, balance_index, last_time_index>; void promote_producers(producers& producers_table); void propose_producers(structures::state_info& s); void reward_producers(producers& producers_table, structures::state_info& s); void reward_workers(structures::state_info& s); void burn_reward(const eosio::name& account, const int64_t& amount) const; + void remove_old_producers(producers& producers_table); int64_t get_target_emission_per_block(int64_t supply) const; public: diff --git a/cyber.govern/src/cyber.govern.cpp b/cyber.govern/src/cyber.govern.cpp index 34a91099a..35b2542db 100644 --- a/cyber.govern/src/cyber.govern.cpp +++ b/cyber.govern/src/cyber.govern.cpp @@ -44,12 +44,14 @@ void govern::onblock(name producer, eosio::binary_extension schedule_v u.omission_resets = 0; u.omission_count = 0; u.is_oblidged = false; + u.last_time = eosio::current_time_point(); }); } else if (block_reward > 0) { producers_table.emplace(_self, [&](auto& u) { u.account = producer; u.amount = block_reward; u.is_oblidged = false; + u.last_time = eosio::current_time_point(); }); } } @@ -67,6 +69,7 @@ void govern::onblock(name producer, eosio::binary_extension schedule_v if (schedule_version.has_value()) { if (s.schedule_version.has_value() && s.schedule_version.value() != schedule_version.value()) { promote_producers(producers_table); + remove_old_producers(producers_table); } s.schedule_version.emplace(schedule_version.value()); } @@ -177,17 +180,11 @@ void govern::reward_producers(producers& producers_table, structures::state_info } auto balances_idx = producers_table.get_index<"bybalance"_n>(); - for (auto itr = balances_idx.begin(); itr != balances_idx.end() && itr->amount;) { + for (auto itr = balances_idx.begin(); itr != balances_idx.end() && itr->amount; ++itr) { rewards.emplace_back(itr->account, itr->amount + get_old_reward(itr->account)); - if (!cyber::stake::candidate_exists(itr->account, token_code)) { - burn_reward(itr->account, itr->unconfirmed_amount); - itr = balances_idx.erase(itr); - } else { - balances_idx.modify(itr, eosio::same_payer, [&](auto& u){ - u.amount = 0; - }); - ++itr; - } + balances_idx.modify(itr, eosio::same_payer, [&](auto& u){ + u.amount = 0; + }); } for (const auto& r: old_rewards) { @@ -327,6 +324,7 @@ void govern::promote_producers(producers& producers_table) { producers_table.emplace(_self, [&](auto& p) { p.account = acc; p.is_oblidged = true; + p.last_time = eosio::current_time_point(); }); } else if (!itr->is_oblidged) { producers_table.modify(itr, eosio::same_payer, [&](auto& p) { @@ -336,4 +334,21 @@ void govern::promote_producers(producers& producers_table) { } } +void govern::remove_old_producers(producers& producers_table) { + static constexpr auto token_code = system_token.code(); + auto last_time_idx = producers_table.get_index<"bytime"_n>(); + auto itr = last_time_idx.begin(); + if (last_time_idx.end() == itr) { + return; + } + if (!cyber::stake::candidate_exists(itr->account, token_code)) { + burn_reward(itr->account, itr->unconfirmed_amount + itr->amount); + last_time_idx.erase(itr); + } else { + last_time_idx.modify(itr, eosio::same_payer, [&](auto& p){ + p.last_time = eosio::current_time_point(); + }); + } +} + } From 8865488b87412f75473544d656262b31bdd708d8 Mon Sep 17 00:00:00 2001 From: Semen Medvedev Date: Mon, 16 Mar 2020 19:29:36 +0700 Subject: [PATCH 06/21] Verificationf of changes in ABI-files generated during build system.contracts #323 --- .gitmodules | 3 + CMakeLists.txt | 13 + cyber.bios/CMakeLists.txt | 2 +- cyber.bios/cyber.bios.abi | 292 ++++++++++++++++++++++ cyber.domain/CMakeLists.txt | 2 +- cyber.domain/cyber.domain.abi | 144 +++++++++++ cyber.govern/CMakeLists.txt | 2 +- cyber.govern/cyber.govern.abi | 144 +++++++++++ cyber.incomereject/CMakeLists.txt | 2 +- cyber.incomereject/cyber.incomereject.abi | 10 + cyber.msig/CMakeLists.txt | 2 +- cyber.msig/cyber.msig.abi | 177 +++++++++++++ cyber.rejector/CMakeLists.txt | 2 +- cyber.rejector/cyber.rejector.abi | 10 + cyber.stake/CMakeLists.txt | 2 +- cyber.stake/cyber.stake.abi | 274 ++++++++++++++++++++ cyber.token/CMakeLists.txt | 2 +- cyber.token/cyber.token.abi | 137 ++++++++++ scripts/deployutils | 1 + 19 files changed, 1213 insertions(+), 8 deletions(-) create mode 100644 .gitmodules create mode 100644 cyber.bios/cyber.bios.abi create mode 100644 cyber.domain/cyber.domain.abi create mode 100644 cyber.govern/cyber.govern.abi create mode 100644 cyber.incomereject/cyber.incomereject.abi create mode 100644 cyber.msig/cyber.msig.abi create mode 100644 cyber.rejector/cyber.rejector.abi create mode 100644 cyber.stake/cyber.stake.abi create mode 100644 cyber.token/cyber.token.abi create mode 160000 scripts/deployutils diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..0f239654b --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "scripts/deployutils"] + path = scripts/deployutils + url = https://github.com/cyberway/deployutils.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 46251c045..e6d3ad423 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,12 +35,25 @@ macro(install_contract TARGET) install (FILES ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}.abi DESTINATION ${CMAKE_INSTALL_PREFIX}/${TARGET}/) endmacro() +macro(add_contract_with_checked_abi CONTRACT_NAME TARGET ABIFILE) + add_contract(${CONTRACT_NAME} ${TARGET} ${ARGN}) + get_target_property(BINOUTPUT ${TARGET}.wasm BINARY_DIR) + add_custom_command(TARGET ${TARGET}.wasm POST_BUILD + COMMAND ${PROJECT_SOURCE_DIR}/scripts/deployutils/abiprinter.py <${BINOUTPUT}/${TARGET}.abi >${BINOUTPUT}/${TARGET}.abi.pretty) + add_custom_target(${TARGET}.abicheck ALL + COMMAND ${CYBERWAY_ABIDIFF} ${CMAKE_CURRENT_SOURCE_DIR}/${ABIFILE} ${BINOUTPUT}/${TARGET}.abi + DEPENDS ${TARGET}.wasm ${ABIFILE} + ) +endmacro() + macro(add_contract_with_abi TARGET ABIFILE) add_executable( ${TARGET}.wasm ${ARGN} ) configure_file(${ABIFILE} ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}.abi COPYONLY) install_contract(TARGET) endmacro() +set(CYBERWAY_ABIDIFF ${CYBERWAY_CDT_ROOT}/bin/cyberway-abidiff) + add_subdirectory(cyber.bios) add_subdirectory(cyber.msig) add_subdirectory(cyber.token) diff --git a/cyber.bios/CMakeLists.txt b/cyber.bios/CMakeLists.txt index 0a7d720df..38da58f36 100644 --- a/cyber.bios/CMakeLists.txt +++ b/cyber.bios/CMakeLists.txt @@ -1,4 +1,4 @@ -add_contract(cyber.bios cyber.bios ${CMAKE_CURRENT_SOURCE_DIR}/src/cyber.bios.cpp) +add_contract_with_checked_abi(cyber.bios cyber.bios cyber.bios.abi ${CMAKE_CURRENT_SOURCE_DIR}/src/cyber.bios.cpp) install_contract(cyber.bios) target_include_directories(cyber.bios.wasm diff --git a/cyber.bios/cyber.bios.abi b/cyber.bios/cyber.bios.abi new file mode 100644 index 000000000..a8c1984ee --- /dev/null +++ b/cyber.bios/cyber.bios.abi @@ -0,0 +1,292 @@ +{ + "____comment": "This file was generated with eosio-abigen. DO NOT EDIT ", + "version": "cyberway::abi/1.1", + "types": [], + "structs": [{ + "name": "authority", "base": "", + "fields": [ + {"name": "threshold", "type": "uint32"}, + {"name": "keys", "type": "key_weight[]"}, + {"name": "accounts", "type": "permission_level_weight[]"}, + {"name": "waits", "type": "wait_weight[]"} + ] + }, { + "name": "auto_recall", "base": "", + "fields": [ + {"name": "id", "type": "uint64"}, + {"name": "token_code", "type": "symbol_code"}, + {"name": "account", "type": "name"}, + {"name": "break_fee_enabled", "type": "bool"}, + {"name": "break_min_stake_enabled", "type": "bool"} + ] + }, { + "name": "bid_refund", "base": "", + "fields": [ + {"name": "bidder", "type": "name"}, + {"name": "amount", "type": "asset"} + ] + }, { + "name": "bidname", "base": "", + "fields": [ + {"name": "bidder", "type": "name"}, + {"name": "newname", "type": "name"}, + {"name": "bid", "type": "asset"} + ] + }, { + "name": "bidrefund", "base": "", + "fields": [ + {"name": "bidder", "type": "name"} + ] + }, { + "name": "block_header", "base": "", + "fields": [ + {"name": "timestamp", "type": "uint32"}, + {"name": "producer", "type": "name"}, + {"name": "confirmed", "type": "uint16"}, + {"name": "previous", "type": "checksum256"}, + {"name": "transaction_mroot", "type": "checksum256"}, + {"name": "action_mroot", "type": "checksum256"}, + {"name": "schedule_version", "type": "uint32"}, + {"name": "new_producers", "type": "producer_schedule?"} + ] + }, { + "name": "blockchain_parameters", "base": "", + "fields": [ + {"name": "base_per_transaction_net_usage", "type": "uint32"}, + {"name": "context_free_discount_net_usage_num", "type": "uint32"}, + {"name": "context_free_discount_net_usage_den", "type": "uint32"}, + {"name": "min_transaction_cpu_usage", "type": "uint32"}, + {"name": "min_transaction_ram_usage", "type": "uint64"}, + {"name": "max_transaction_lifetime", "type": "uint32"}, + {"name": "deferred_trx_expiration_window", "type": "uint32"}, + {"name": "max_transaction_delay", "type": "uint32"}, + {"name": "max_inline_action_size", "type": "uint32"}, + {"name": "max_inline_action_depth", "type": "uint16"}, + {"name": "max_authority_depth", "type": "uint16"}, + {"name": "ram_size", "type": "uint64"}, + {"name": "reserved_ram_size", "type": "uint64"}, + {"name": "max_block_usage", "type": "uint64[]"}, + {"name": "max_transaction_usage", "type": "uint64[]"}, + {"name": "target_virtual_limits", "type": "uint64[]"}, + {"name": "min_virtual_limits", "type": "uint64[]"}, + {"name": "max_virtual_limits", "type": "uint64[]"}, + {"name": "usage_windows", "type": "uint32[]"}, + {"name": "virtual_limit_decrease_pct", "type": "uint16[]"}, + {"name": "virtual_limit_increase_pct", "type": "uint16[]"}, + {"name": "account_usage_windows", "type": "uint32[]"} + ] + }, { + "name": "canceldelay", "base": "", + "fields": [ + {"name": "canceling_auth", "type": "permission_level"}, + {"name": "trx_id", "type": "checksum256"} + ] + }, { + "name": "checkwin", "base": "", + "fields": [] + }, { + "name": "deleteauth", "base": "", + "fields": [ + {"name": "account", "type": "name"}, + {"name": "permission", "type": "name"} + ] + }, { + "name": "key_weight", "base": "", + "fields": [ + {"name": "key", "type": "public_key"}, + {"name": "weight", "type": "uint16"} + ] + }, { + "name": "linkauth", "base": "", + "fields": [ + {"name": "account", "type": "name"}, + {"name": "code", "type": "name"}, + {"name": "type", "type": "name"}, + {"name": "requirement", "type": "name"} + ] + }, { + "name": "name_bid", "base": "", + "fields": [ + {"name": "newname", "type": "name"}, + {"name": "high_bidder", "type": "name"}, + {"name": "high_bid", "type": "int64"}, + {"name": "last_bid_time", "type": "time_point_sec"} + ] + }, { + "name": "newaccount", "base": "", + "fields": [ + {"name": "creator", "type": "name"}, + {"name": "name", "type": "name"}, + {"name": "owner", "type": "authority"}, + {"name": "active", "type": "authority"} + ] + }, { + "name": "onblock", "base": "", + "fields": [ + {"name": "header", "type": "block_header"} + ] + }, { + "name": "onerror", "base": "", + "fields": [ + {"name": "sender_id", "type": "uint128"}, + {"name": "sent_trx", "type": "bytes"} + ] + }, { + "name": "permission_level", "base": "", + "fields": [ + {"name": "actor", "type": "name"}, + {"name": "permission", "type": "name"} + ] + }, { + "name": "permission_level_weight", "base": "", + "fields": [ + {"name": "permission", "type": "permission_level"}, + {"name": "weight", "type": "uint16"} + ] + }, { + "name": "producer_key", "base": "", + "fields": [ + {"name": "producer_name", "type": "name"}, + {"name": "block_signing_key", "type": "public_key"} + ] + }, { + "name": "producer_schedule", "base": "", + "fields": [ + {"name": "version", "type": "uint32"}, + {"name": "producers", "type": "producer_key[]"} + ] + }, { + "name": "providebw", "base": "", + "fields": [ + {"name": "provider", "type": "name"}, + {"name": "account", "type": "name"} + ] + }, { + "name": "reqauth", "base": "", + "fields": [ + {"name": "from", "type": "name"} + ] + }, { + "name": "setabi", "base": "", + "fields": [ + {"name": "account", "type": "name"}, + {"name": "abi", "type": "bytes"} + ] + }, { + "name": "setcode", "base": "", + "fields": [ + {"name": "account", "type": "name"}, + {"name": "vmtype", "type": "uint8"}, + {"name": "vmversion", "type": "uint8"}, + {"name": "code", "type": "bytes"} + ] + }, { + "name": "setparams", "base": "", + "fields": [ + {"name": "params", "type": "blockchain_parameters"} + ] + }, { + "name": "setprods", "base": "", + "fields": [ + {"name": "schedule", "type": "producer_key[]"} + ] + }, { + "name": "state_info", "base": "", + "fields": [ + {"name": "id", "type": "uint64"}, + {"name": "last_close_bid", "type": "time_point_sec"} + ] + }, { + "name": "unlinkauth", "base": "", + "fields": [ + {"name": "account", "type": "name"}, + {"name": "code", "type": "name"}, + {"name": "type", "type": "name"} + ] + }, { + "name": "updateauth", "base": "", + "fields": [ + {"name": "account", "type": "name"}, + {"name": "permission", "type": "name"}, + {"name": "parent", "type": "name"}, + {"name": "auth", "type": "authority"} + ] + }, { + "name": "wait_weight", "base": "", + "fields": [ + {"name": "wait_sec", "type": "uint32"}, + {"name": "weight", "type": "uint16"} + ] + } + ], + "actions": [ + {"name": "bidname", "type": "bidname"}, + {"name": "bidrefund", "type": "bidrefund"}, + {"name": "canceldelay", "type": "canceldelay"}, + {"name": "checkwin", "type": "checkwin"}, + {"name": "deleteauth", "type": "deleteauth"}, + {"name": "linkauth", "type": "linkauth"}, + {"name": "newaccount", "type": "newaccount"}, + {"name": "onblock", "type": "onblock"}, + {"name": "onerror", "type": "onerror"}, + {"name": "providebw", "type": "providebw"}, + {"name": "reqauth", "type": "reqauth"}, + {"name": "setabi", "type": "setabi"}, + {"name": "setcode", "type": "setcode"}, + {"name": "setparams", "type": "setparams"}, + {"name": "setprods", "type": "setprods"}, + {"name": "unlinkauth", "type": "unlinkauth"}, + {"name": "updateauth", "type": "updateauth"} + ], + "events": [], + "tables": [{ + "name": "bidrefunds", "type": "bid_refund", + "indexes": [{ + "name": "primary", "unique": true, + "orders": [ + {"field": "bidder", "order": "asc"} + ] + } + ] + }, { + "name": "biosstate", "type": "state_info", + "indexes": [{ + "name": "primary", "unique": true, + "orders": [ + {"field": "id", "order": "asc"} + ] + } + ] + }, { + "name": "namebids", "type": "name_bid", + "indexes": [{ + "name": "primary", "unique": true, + "orders": [ + {"field": "newname", "order": "asc"} + ] + }, { + "name": "highbid", "unique": false, + "orders": [ + {"field": "high_bid", "order": "desc"} + ] + } + ] + }, { + "name": "stake.autorc", "type": "auto_recall", + "indexes": [{ + "name": "primary", "unique": true, + "orders": [ + {"field": "id", "order": "asc"} + ] + }, { + "name": "bykey", "unique": true, + "orders": [ + {"field": "token_code", "order": "asc"}, + {"field": "account", "order": "asc"} + ] + } + ] + } + ], + "variants": [] +} diff --git a/cyber.domain/CMakeLists.txt b/cyber.domain/CMakeLists.txt index 7148dc1a2..ea04facbe 100644 --- a/cyber.domain/CMakeLists.txt +++ b/cyber.domain/CMakeLists.txt @@ -1,4 +1,4 @@ -add_contract(cyber.domain cyber.domain ${CMAKE_CURRENT_SOURCE_DIR}/cyber.domain.cpp) +add_contract_with_checked_abi(cyber.domain cyber.domain cyber.domain.abi ${CMAKE_CURRENT_SOURCE_DIR}/cyber.domain.cpp) install_contract(cyber.domain) target_include_directories(cyber.domain.wasm diff --git a/cyber.domain/cyber.domain.abi b/cyber.domain/cyber.domain.abi new file mode 100644 index 000000000..25ff1e41d --- /dev/null +++ b/cyber.domain/cyber.domain.abi @@ -0,0 +1,144 @@ +{ + "____comment": "This file was generated with eosio-abigen. DO NOT EDIT ", + "version": "cyberway::abi/1.1", + "types": [ + {"new_type_name": "domain_name", "type": "string"}, + {"new_type_name": "username", "type": "string"} + ], + "structs": [{ + "name": "biddmrefund", "base": "", + "fields": [ + {"name": "bidder", "type": "name"} + ] + }, { + "name": "biddomain", "base": "", + "fields": [ + {"name": "bidder", "type": "name"}, + {"name": "name", "type": "domain_name"}, + {"name": "bid", "type": "asset"} + ] + }, { + "name": "checkwin", "base": "", + "fields": [] + }, { + "name": "declarenames", "base": "", + "fields": [ + {"name": "domains", "type": "name_info[]"} + ] + }, { + "name": "domain_bid", "base": "", + "fields": [ + {"name": "id", "type": "uint64"}, + {"name": "name", "type": "domain_name"}, + {"name": "high_bidder", "type": "name"}, + {"name": "high_bid", "type": "int64"}, + {"name": "last_bid_time", "type": "time_point_sec"} + ] + }, { + "name": "domain_bid_refund", "base": "", + "fields": [ + {"name": "bidder", "type": "name"}, + {"name": "amount", "type": "asset"} + ] + }, { + "name": "domain_bid_state", "base": "", + "fields": [ + {"name": "id", "type": "uint64"}, + {"name": "last_win", "type": "time_point_sec"} + ] + }, { + "name": "linkdomain", "base": "", + "fields": [ + {"name": "owner", "type": "name"}, + {"name": "to", "type": "name"}, + {"name": "name", "type": "string"} + ] + }, { + "name": "name_info", "base": "", + "fields": [ + {"name": "domain", "type": "domain_name"}, + {"name": "account", "type": "name"}, + {"name": "users", "type": "username[]"} + ] + }, { + "name": "newdomain", "base": "", + "fields": [ + {"name": "creator", "type": "name"}, + {"name": "name", "type": "domain_name"} + ] + }, { + "name": "newusername", "base": "", + "fields": [ + {"name": "creator", "type": "name"}, + {"name": "owner", "type": "name"}, + {"name": "name", "type": "string"} + ] + }, { + "name": "passdomain", "base": "", + "fields": [ + {"name": "from", "type": "name"}, + {"name": "to", "type": "name"}, + {"name": "name", "type": "string"} + ] + }, { + "name": "unlinkdomain", "base": "", + "fields": [ + {"name": "owner", "type": "name"}, + {"name": "name", "type": "string"} + ] + } + ], + "actions": [ + {"name": "biddmrefund", "type": "biddmrefund"}, + {"name": "biddomain", "type": "biddomain"}, + {"name": "checkwin", "type": "checkwin"}, + {"name": "declarenames", "type": "declarenames"}, + {"name": "linkdomain", "type": "linkdomain"}, + {"name": "newdomain", "type": "newdomain"}, + {"name": "newusername", "type": "newusername"}, + {"name": "passdomain", "type": "passdomain"}, + {"name": "unlinkdomain", "type": "unlinkdomain"} + ], + "events": [], + "tables": [{ + "name": "dbidrefund", "type": "domain_bid_refund", + "indexes": [{ + "name": "primary", "unique": true, + "orders": [ + {"field": "bidder", "order": "asc"} + ] + } + ] + }, { + "name": "dbidstate", "type": "domain_bid_state", + "indexes": [{ + "name": "primary", "unique": true, + "orders": [ + {"field": "id", "order": "asc"} + ] + } + ] + }, { + "name": "domainbid", "type": "domain_bid", + "indexes": [{ + "name": "primary", "unique": true, + "orders": [ + {"field": "id", "order": "asc"} + ] + }, { + "name": "domain", "unique": true, + "orders": [ + {"field": "name", "order": "asc"} + ] + }, { + "name": "highbid", "unique": true, + "orders": [ + {"field": "high_bid", "order": "desc"}, + {"field": "name", "order": "asc"} + ] + } + ] + } + ], + "variants": [] +} diff --git a/cyber.govern/CMakeLists.txt b/cyber.govern/CMakeLists.txt index 08ff9604a..93af249e2 100644 --- a/cyber.govern/CMakeLists.txt +++ b/cyber.govern/CMakeLists.txt @@ -1,4 +1,4 @@ -add_contract(cyber.govern cyber.govern ${CMAKE_CURRENT_SOURCE_DIR}/src/cyber.govern.cpp) +add_contract_with_checked_abi(cyber.govern cyber.govern cyber.govern.abi ${CMAKE_CURRENT_SOURCE_DIR}/src/cyber.govern.cpp) install_contract(cyber.govern) target_include_directories(cyber.govern.wasm diff --git a/cyber.govern/cyber.govern.abi b/cyber.govern/cyber.govern.abi new file mode 100644 index 000000000..d4be7d128 --- /dev/null +++ b/cyber.govern/cyber.govern.abi @@ -0,0 +1,144 @@ +{ + "____comment": "This file was generated with eosio-abigen. DO NOT EDIT ", + "version": "cyberway::abi/1.1", + "types": [], + "structs": [{ + "name": "balance_struct", "base": "", + "fields": [ + {"name": "account", "type": "name"}, + {"name": "amount", "type": "int64"} + ] + }, { + "name": "omission_struct", "base": "", + "fields": [ + {"name": "account", "type": "name"}, + {"name": "count", "type": "uint16"}, + {"name": "resets", "type": "uint16"} + ] + }, { + "name": "onblock", "base": "", + "fields": [ + {"name": "producer", "type": "name"} + ] + }, { + "name": "pending_producers_info", "base": "", + "fields": [ + {"name": "id", "type": "uint64"}, + {"name": "accounts", "type": "name[]"} + ] + }, { + "name": "producer_struct", "base": "", + "fields": [ + {"name": "account", "type": "name"} + ] + }, { + "name": "schedule_resize_info", "base": "", + "fields": [ + {"name": "id", "type": "uint64"}, + {"name": "last_step", "type": "time_point_sec"}, + {"name": "shift", "type": "int8"} + ] + }, { + "name": "setactprods", "base": "", + "fields": [ + {"name": "pending_active_producers", "type": "name[]"} + ] + }, { + "name": "setshift", "base": "", + "fields": [ + {"name": "shift", "type": "int8"} + ] + }, { + "name": "state_info", "base": "", + "fields": [ + {"name": "id", "type": "uint64"}, + {"name": "last_schedule_increase", "type": "time_point_sec"}, + {"name": "block_num", "type": "uint32"}, + {"name": "target_emission_per_block", "type": "int64"}, + {"name": "funds", "type": "int64"}, + {"name": "last_propose_block_num", "type": "uint32"}, + {"name": "required_producers_num", "type": "uint16"}, + {"name": "last_producers_num", "type": "uint16"} + ] + } + ], + "actions": [ + {"name": "onblock", "type": "onblock"}, + {"name": "setactprods", "type": "setactprods"}, + {"name": "setshift", "type": "setshift"} + ], + "events": [ + {"name": "burnreward", "type": "balance_struct"} + ], + "tables": [{ + "name": "balance", "type": "balance_struct", + "indexes": [{ + "name": "primary", "unique": true, + "orders": [ + {"field": "account", "order": "asc"} + ] + } + ] + }, { + "name": "governstate", "type": "state_info", + "indexes": [{ + "name": "primary", "unique": true, + "orders": [ + {"field": "id", "order": "asc"} + ] + } + ] + }, { + "name": "obligedprod", "type": "producer_struct", + "indexes": [{ + "name": "primary", "unique": true, + "orders": [ + {"field": "account", "order": "asc"} + ] + } + ] + }, { + "name": "omission", "type": "omission_struct", + "indexes": [{ + "name": "primary", "unique": true, + "orders": [ + {"field": "account", "order": "asc"} + ] + }, { + "name": "bycount", "unique": false, + "orders": [ + {"field": "count", "order": "desc"} + ] + } + ] + }, { + "name": "pendingprods", "type": "pending_producers_info", + "indexes": [{ + "name": "primary", "unique": true, + "orders": [ + {"field": "id", "order": "asc"} + ] + } + ] + }, { + "name": "schedresize", "type": "schedule_resize_info", + "indexes": [{ + "name": "primary", "unique": true, + "orders": [ + {"field": "id", "order": "asc"} + ] + } + ] + }, { + "name": "uncbalance", "type": "balance_struct", + "indexes": [{ + "name": "primary", "unique": true, + "orders": [ + {"field": "account", "order": "asc"} + ] + } + ] + } + ], + "variants": [] +} diff --git a/cyber.incomereject/CMakeLists.txt b/cyber.incomereject/CMakeLists.txt index e0fe428e2..d590775c8 100644 --- a/cyber.incomereject/CMakeLists.txt +++ b/cyber.incomereject/CMakeLists.txt @@ -1,4 +1,4 @@ -add_contract(cyber.incomereject cyber.incomereject ${CMAKE_CURRENT_SOURCE_DIR}/src/cyber.incomereject.cpp) +add_contract_with_checked_abi(cyber.incomereject cyber.incomereject cyber.incomereject.abi ${CMAKE_CURRENT_SOURCE_DIR}/src/cyber.incomereject.cpp) install_contract(cyber.incomereject) target_include_directories(cyber.incomereject.wasm diff --git a/cyber.incomereject/cyber.incomereject.abi b/cyber.incomereject/cyber.incomereject.abi new file mode 100644 index 000000000..77b0aabbb --- /dev/null +++ b/cyber.incomereject/cyber.incomereject.abi @@ -0,0 +1,10 @@ +{ + "____comment": "This file was generated with eosio-abigen. DO NOT EDIT ", + "version": "cyberway::abi/1.1", + "types": [], + "structs": [], + "actions": [], + "events": [], + "tables": [], + "variants": [] +} diff --git a/cyber.msig/CMakeLists.txt b/cyber.msig/CMakeLists.txt index 6fcbb2a3e..0b98856e7 100644 --- a/cyber.msig/CMakeLists.txt +++ b/cyber.msig/CMakeLists.txt @@ -1,4 +1,4 @@ -add_contract(cyber.msig cyber.msig ${CMAKE_CURRENT_SOURCE_DIR}/src/cyber.msig.cpp) +add_contract_with_checked_abi(cyber.msig cyber.msig cyber.msig.abi ${CMAKE_CURRENT_SOURCE_DIR}/src/cyber.msig.cpp) install_contract(cyber.msig) target_include_directories(cyber.msig.wasm diff --git a/cyber.msig/cyber.msig.abi b/cyber.msig/cyber.msig.abi new file mode 100644 index 000000000..9364314e8 --- /dev/null +++ b/cyber.msig/cyber.msig.abi @@ -0,0 +1,177 @@ +{ + "____comment": "This file was generated with eosio-abigen. DO NOT EDIT ", + "version": "cyberway::abi/1.1", + "types": [], + "structs": [{ + "name": "action", "base": "", + "fields": [ + {"name": "account", "type": "name"}, + {"name": "name", "type": "name"}, + {"name": "authorization", "type": "permission_level[]"}, + {"name": "data", "type": "bytes"} + ] + }, { + "name": "approval", "base": "", + "fields": [ + {"name": "level", "type": "permission_level"}, + {"name": "time", "type": "time_point"} + ] + }, { + "name": "approvals_info", "base": "", + "fields": [ + {"name": "version", "type": "uint8"}, + {"name": "proposal_name", "type": "name"}, + {"name": "requested_approvals", "type": "approval[]"}, + {"name": "provided_approvals", "type": "approval[]"} + ] + }, { + "name": "approve", "base": "", + "fields": [ + {"name": "proposer", "type": "name"}, + {"name": "proposal_name", "type": "name"}, + {"name": "level", "type": "permission_level"}, + {"name": "proposal_hash", "type": "checksum256$"} + ] + }, { + "name": "cancel", "base": "", + "fields": [ + {"name": "proposer", "type": "name"}, + {"name": "proposal_name", "type": "name"}, + {"name": "canceler", "type": "name"} + ] + }, { + "name": "exec", "base": "", + "fields": [ + {"name": "proposer", "type": "name"}, + {"name": "proposal_name", "type": "name"}, + {"name": "executer", "type": "name"} + ] + }, { + "name": "extension", "base": "", + "fields": [ + {"name": "type", "type": "uint16"}, + {"name": "data", "type": "bytes"} + ] + }, { + "name": "invalidate", "base": "", + "fields": [ + {"name": "account", "type": "name"} + ] + }, { + "name": "invalidation", "base": "", + "fields": [ + {"name": "account", "type": "name"}, + {"name": "last_invalidation_time", "type": "time_point"} + ] + }, { + "name": "permission_level", "base": "", + "fields": [ + {"name": "actor", "type": "name"}, + {"name": "permission", "type": "name"} + ] + }, { + "name": "proposal", "base": "", + "fields": [ + {"name": "proposal_name", "type": "name"}, + {"name": "packed_transaction", "type": "bytes"} + ] + }, { + "name": "proposal_wait", "base": "", + "fields": [ + {"name": "proposal_name", "type": "name"}, + {"name": "started", "type": "time_point_sec"} + ] + }, { + "name": "propose", "base": "", + "fields": [ + {"name": "proposer", "type": "name"}, + {"name": "proposal_name", "type": "name"}, + {"name": "requested", "type": "permission_level[]"}, + {"name": "trx", "type": "transaction"}, + {"name": "description", "type": "string$"} + ] + }, { + "name": "schedule", "base": "", + "fields": [ + {"name": "proposer", "type": "name"}, + {"name": "proposal_name", "type": "name"}, + {"name": "actor", "type": "name"} + ] + }, { + "name": "transaction", "base": "transaction_header", + "fields": [ + {"name": "context_free_actions", "type": "action[]"}, + {"name": "actions", "type": "action[]"}, + {"name": "transaction_extensions", "type": "extension[]"} + ] + }, { + "name": "transaction_header", "base": "", + "fields": [ + {"name": "expiration", "type": "time_point_sec"}, + {"name": "ref_block_num", "type": "uint16"}, + {"name": "ref_block_prefix", "type": "uint32"}, + {"name": "max_net_usage_words", "type": "varuint32"}, + {"name": "max_cpu_usage_ms", "type": "uint8"}, + {"name": "max_ram_kbytes", "type": "varuint32"}, + {"name": "max_storage_kbytes", "type": "varuint32"}, + {"name": "delay_sec", "type": "varuint32"} + ] + }, { + "name": "unapprove", "base": "", + "fields": [ + {"name": "proposer", "type": "name"}, + {"name": "proposal_name", "type": "name"}, + {"name": "level", "type": "permission_level"} + ] + } + ], + "actions": [ + {"name": "approve", "type": "approve"}, + {"name": "cancel", "type": "cancel"}, + {"name": "exec", "type": "exec"}, + {"name": "invalidate", "type": "invalidate"}, + {"name": "propose", "type": "propose"}, + {"name": "schedule", "type": "schedule"}, + {"name": "unapprove", "type": "unapprove"} + ], + "events": [], + "tables": [{ + "name": "approvals2", "type": "approvals_info", + "indexes": [{ + "name": "primary", "unique": true, + "orders": [ + {"field": "proposal_name", "order": "asc"} + ] + } + ] + }, { + "name": "invals", "type": "invalidation", + "indexes": [{ + "name": "primary", "unique": true, + "orders": [ + {"field": "account", "order": "asc"} + ] + } + ] + }, { + "name": "proposal", "type": "proposal", + "indexes": [{ + "name": "primary", "unique": true, + "orders": [ + {"field": "proposal_name", "order": "asc"} + ] + } + ] + }, { + "name": "waits", "type": "proposal_wait", + "indexes": [{ + "name": "primary", "unique": true, + "orders": [ + {"field": "proposal_name", "order": "asc"} + ] + } + ] + } + ], + "variants": [] +} diff --git a/cyber.rejector/CMakeLists.txt b/cyber.rejector/CMakeLists.txt index ee12491f1..6effa2930 100644 --- a/cyber.rejector/CMakeLists.txt +++ b/cyber.rejector/CMakeLists.txt @@ -1,4 +1,4 @@ -add_contract(cyber.rejector cyber.rejector ${CMAKE_CURRENT_SOURCE_DIR}/src/cyber.rejector.cpp) +add_contract_with_checked_abi(cyber.rejector cyber.rejector cyber.rejector.abi ${CMAKE_CURRENT_SOURCE_DIR}/src/cyber.rejector.cpp) install_contract(cyber.rejector) target_include_directories(cyber.rejector.wasm diff --git a/cyber.rejector/cyber.rejector.abi b/cyber.rejector/cyber.rejector.abi new file mode 100644 index 000000000..77b0aabbb --- /dev/null +++ b/cyber.rejector/cyber.rejector.abi @@ -0,0 +1,10 @@ +{ + "____comment": "This file was generated with eosio-abigen. DO NOT EDIT ", + "version": "cyberway::abi/1.1", + "types": [], + "structs": [], + "actions": [], + "events": [], + "tables": [], + "variants": [] +} diff --git a/cyber.stake/CMakeLists.txt b/cyber.stake/CMakeLists.txt index ef43bb8ab..0b02e4fee 100644 --- a/cyber.stake/CMakeLists.txt +++ b/cyber.stake/CMakeLists.txt @@ -1,4 +1,4 @@ -add_contract(cyber.stake cyber.stake ${CMAKE_CURRENT_SOURCE_DIR}/src/cyber.stake.cpp) +add_contract_with_checked_abi(cyber.stake cyber.stake cyber.stake.abi ${CMAKE_CURRENT_SOURCE_DIR}/src/cyber.stake.cpp) install_contract(cyber.stake) target_include_directories(cyber.stake.wasm diff --git a/cyber.stake/cyber.stake.abi b/cyber.stake/cyber.stake.abi new file mode 100644 index 000000000..ad3008685 --- /dev/null +++ b/cyber.stake/cyber.stake.abi @@ -0,0 +1,274 @@ +{ + "____comment": "This file was generated with eosio-abigen. DO NOT EDIT ", + "version": "cyberway::abi/1.1", + "types": [], + "structs": [{ + "name": "claim", "base": "", + "fields": [ + {"name": "grantor_name", "type": "name"}, + {"name": "recipient_name", "type": "name"}, + {"name": "token_code", "type": "symbol_code"} + ] + }, { + "name": "create", "base": "", + "fields": [ + {"name": "token_symbol", "type": "symbol"}, + {"name": "max_proxies", "type": "uint8[]"}, + {"name": "depriving_window", "type": "int64"}, + {"name": "min_own_staked_for_election", "type": "int64"} + ] + }, { + "name": "delegateuse", "base": "", + "fields": [ + {"name": "grantor_name", "type": "name"}, + {"name": "recipient_name", "type": "name"}, + {"name": "quantity", "type": "asset"} + ] + }, { + "name": "delegatevote", "base": "", + "fields": [ + {"name": "grantor_name", "type": "name"}, + {"name": "recipient_name", "type": "name"}, + {"name": "quantity", "type": "asset"} + ] + }, { + "name": "enable", "base": "", + "fields": [ + {"name": "token_code", "type": "symbol_code"} + ] + }, { + "name": "losses", "base": "", + "fields": [ + {"name": "id", "type": "uint64"}, + {"name": "time", "type": "time_point_sec"}, + {"name": "token_code", "type": "symbol_code"}, + {"name": "total", "type": "int64"} + ] + }, { + "name": "open", "base": "", + "fields": [ + {"name": "owner", "type": "name"}, + {"name": "token_code", "type": "symbol_code"}, + {"name": "ram_payer", "type": "name?"} + ] + }, { + "name": "pair_name_int64", "base": "", + "fields": [ + {"name": "first", "type": "name"}, + {"name": "second", "type": "int64"} + ] + }, { + "name": "pick", "base": "", + "fields": [ + {"name": "token_code", "type": "symbol_code"}, + {"name": "accounts", "type": "name[]"} + ] + }, { + "name": "prov_payout_struct", "base": "", + "fields": [ + {"name": "id", "type": "uint64"}, + {"name": "token_code", "type": "symbol_code"}, + {"name": "grantor_name", "type": "name"}, + {"name": "recipient_name", "type": "name"}, + {"name": "amount", "type": "int64"}, + {"name": "date", "type": "time_point_sec"} + ] + }, { + "name": "provision_struct", "base": "", + "fields": [ + {"name": "id", "type": "uint64"}, + {"name": "token_code", "type": "symbol_code"}, + {"name": "grantor_name", "type": "name"}, + {"name": "recipient_name", "type": "name"}, + {"name": "amount", "type": "int64"} + ] + }, { + "name": "recalluse", "base": "", + "fields": [ + {"name": "grantor_name", "type": "name"}, + {"name": "recipient_name", "type": "name"}, + {"name": "quantity", "type": "asset"} + ] + }, { + "name": "recallvote", "base": "", + "fields": [ + {"name": "grantor_name", "type": "name"}, + {"name": "recipient_name", "type": "name"}, + {"name": "token_code", "type": "symbol_code"}, + {"name": "pct", "type": "int16"} + ] + }, { + "name": "returnlosses", "base": "", + "fields": [] + }, { + "name": "reward", "base": "", + "fields": [ + {"name": "rewards", "type": "pair_name_int64[]"}, + {"name": "sym", "type": "symbol"} + ] + }, { + "name": "setautorc", "base": "", + "fields": [ + {"name": "account", "type": "name"}, + {"name": "token_code", "type": "symbol_code"}, + {"name": "break_fee_enabled", "type": "bool"}, + {"name": "break_min_stake_enabled", "type": "bool"} + ] + }, { + "name": "setautorcmode", "base": "", + "fields": [ + {"name": "token_code", "type": "symbol_code"}, + {"name": "enabled", "type": "bool"} + ] + }, { + "name": "setgrntterms", "base": "", + "fields": [ + {"name": "grantor_name", "type": "name"}, + {"name": "recipient_name", "type": "name"}, + {"name": "token_code", "type": "symbol_code"}, + {"name": "pct", "type": "int16"}, + {"name": "break_fee", "type": "int16"}, + {"name": "break_min_own_staked", "type": "int64"} + ] + }, { + "name": "setkey", "base": "", + "fields": [ + {"name": "account", "type": "name"}, + {"name": "token_code", "type": "symbol_code"}, + {"name": "signing_key", "type": "public_key?"} + ] + }, { + "name": "setminstaked", "base": "", + "fields": [ + {"name": "account", "type": "name"}, + {"name": "token_code", "type": "symbol_code"}, + {"name": "min_own_staked", "type": "int64"} + ] + }, { + "name": "setproxyfee", "base": "", + "fields": [ + {"name": "account", "type": "name"}, + {"name": "token_code", "type": "symbol_code"}, + {"name": "fee", "type": "int16"} + ] + }, { + "name": "setproxylvl", "base": "", + "fields": [ + {"name": "account", "type": "name"}, + {"name": "token_code", "type": "symbol_code"}, + {"name": "level", "type": "uint8"} + ] + }, { + "name": "suspendcand", "base": "", + "fields": [ + {"name": "account", "type": "name"}, + {"name": "token_code", "type": "symbol_code"} + ] + }, { + "name": "suspense", "base": "", + "fields": [ + {"name": "id", "type": "uint64"}, + {"name": "token_code", "type": "symbol_code"}, + {"name": "account", "type": "name"}, + {"name": "action_name", "type": "name"}, + {"name": "expiration_time", "type": "time_point_sec"} + ] + }, { + "name": "updatefunds", "base": "", + "fields": [ + {"name": "account", "type": "name"}, + {"name": "token_code", "type": "symbol_code"} + ] + }, { + "name": "withdraw", "base": "", + "fields": [ + {"name": "account", "type": "name"}, + {"name": "quantity", "type": "asset"} + ] + } + ], + "actions": [ + {"name": "claim", "type": "claim"}, + {"name": "create", "type": "create"}, + {"name": "delegateuse", "type": "delegateuse"}, + {"name": "delegatevote", "type": "delegatevote"}, + {"name": "enable", "type": "enable"}, + {"name": "open", "type": "open"}, + {"name": "pick", "type": "pick"}, + {"name": "recalluse", "type": "recalluse"}, + {"name": "recallvote", "type": "recallvote"}, + {"name": "returnlosses", "type": "returnlosses"}, + {"name": "reward", "type": "reward"}, + {"name": "setautorc", "type": "setautorc"}, + {"name": "setautorcmode", "type": "setautorcmode"}, + {"name": "setgrntterms", "type": "setgrntterms"}, + {"name": "setkey", "type": "setkey"}, + {"name": "setminstaked", "type": "setminstaked"}, + {"name": "setproxyfee", "type": "setproxyfee"}, + {"name": "setproxylvl", "type": "setproxylvl"}, + {"name": "suspendcand", "type": "suspendcand"}, + {"name": "updatefunds", "type": "updatefunds"}, + {"name": "withdraw", "type": "withdraw"} + ], + "events": [], + "tables": [{ + "name": "losses", "type": "losses", + "indexes": [{ + "name": "primary", "unique": true, + "orders": [ + {"field": "id", "order": "asc"} + ] + } + ] + }, { + "name": "provision", "type": "provision_struct", + "indexes": [{ + "name": "primary", "unique": true, + "orders": [ + {"field": "id", "order": "asc"} + ] + }, { + "name": "bykey", "unique": true, + "orders": [ + {"field": "token_code", "order": "asc"}, + {"field": "grantor_name", "order": "asc"}, + {"field": "recipient_name", "order": "asc"} + ] + } + ] + }, { + "name": "provpayout", "type": "prov_payout_struct", + "indexes": [{ + "name": "primary", "unique": true, + "orders": [ + {"field": "id", "order": "asc"} + ] + }, { + "name": "bykey", "unique": true, + "orders": [ + {"field": "token_code", "order": "asc"}, + {"field": "grantor_name", "order": "asc"}, + {"field": "recipient_name", "order": "asc"} + ] + } + ] + }, { + "name": "suspense", "type": "suspense", + "indexes": [{ + "name": "primary", "unique": true, + "orders": [ + {"field": "id", "order": "asc"} + ] + }, { + "name": "bykey", "unique": true, + "orders": [ + {"field": "token_code", "order": "asc"}, + {"field": "account", "order": "asc"}, + {"field": "action_name", "order": "asc"} + ] + } + ] + } + ], + "variants": [] +} diff --git a/cyber.token/CMakeLists.txt b/cyber.token/CMakeLists.txt index f11ae17af..4ecd55feb 100644 --- a/cyber.token/CMakeLists.txt +++ b/cyber.token/CMakeLists.txt @@ -1,4 +1,4 @@ -add_contract(cyber.token cyber.token ${CMAKE_CURRENT_SOURCE_DIR}/src/cyber.token.cpp) +add_contract_with_checked_abi(cyber.token cyber.token cyber.token.abi ${CMAKE_CURRENT_SOURCE_DIR}/src/cyber.token.cpp) install_contract(cyber.token) target_include_directories(cyber.token.wasm diff --git a/cyber.token/cyber.token.abi b/cyber.token/cyber.token.abi new file mode 100644 index 000000000..24d1e48f3 --- /dev/null +++ b/cyber.token/cyber.token.abi @@ -0,0 +1,137 @@ +{ + "____comment": "This file was generated with eosio-abigen. DO NOT EDIT ", + "version": "cyberway::abi/1.1", + "types": [], + "structs": [{ + "name": "account", "base": "", + "fields": [ + {"name": "balance", "type": "asset"}, + {"name": "payments", "type": "asset"} + ] + }, { + "name": "balance_event", "base": "", + "fields": [ + {"name": "account", "type": "name"}, + {"name": "balance", "type": "asset"}, + {"name": "payments", "type": "asset"} + ] + }, { + "name": "bulkpayment", "base": "", + "fields": [ + {"name": "from", "type": "name"}, + {"name": "recipients", "type": "recipient[]"} + ] + }, { + "name": "bulktransfer", "base": "", + "fields": [ + {"name": "from", "type": "name"}, + {"name": "recipients", "type": "recipient[]"} + ] + }, { + "name": "claim", "base": "", + "fields": [ + {"name": "owner", "type": "name"}, + {"name": "quantity", "type": "asset"} + ] + }, { + "name": "close", "base": "", + "fields": [ + {"name": "owner", "type": "name"}, + {"name": "symbol", "type": "symbol"} + ] + }, { + "name": "create", "base": "", + "fields": [ + {"name": "issuer", "type": "name"}, + {"name": "maximum_supply", "type": "asset"} + ] + }, { + "name": "currency_stats", "base": "", + "fields": [ + {"name": "supply", "type": "asset"}, + {"name": "max_supply", "type": "asset"}, + {"name": "issuer", "type": "name"} + ] + }, { + "name": "issue", "base": "", + "fields": [ + {"name": "to", "type": "name"}, + {"name": "quantity", "type": "asset"}, + {"name": "memo", "type": "string"} + ] + }, { + "name": "open", "base": "", + "fields": [ + {"name": "owner", "type": "name"}, + {"name": "symbol", "type": "symbol"}, + {"name": "ram_payer", "type": "name"} + ] + }, { + "name": "payment", "base": "", + "fields": [ + {"name": "from", "type": "name"}, + {"name": "to", "type": "name"}, + {"name": "quantity", "type": "asset"}, + {"name": "memo", "type": "string"} + ] + }, { + "name": "recipient", "base": "", + "fields": [ + {"name": "to", "type": "name"}, + {"name": "quantity", "type": "asset"}, + {"name": "memo", "type": "string"} + ] + }, { + "name": "retire", "base": "", + "fields": [ + {"name": "quantity", "type": "asset"}, + {"name": "memo", "type": "string"} + ] + }, { + "name": "transfer", "base": "", + "fields": [ + {"name": "from", "type": "name"}, + {"name": "to", "type": "name"}, + {"name": "quantity", "type": "asset"}, + {"name": "memo", "type": "string"} + ] + } + ], + "actions": [ + {"name": "bulkpayment", "type": "bulkpayment"}, + {"name": "bulktransfer", "type": "bulktransfer"}, + {"name": "claim", "type": "claim"}, + {"name": "close", "type": "close"}, + {"name": "create", "type": "create"}, + {"name": "issue", "type": "issue"}, + {"name": "open", "type": "open"}, + {"name": "payment", "type": "payment"}, + {"name": "retire", "type": "retire"}, + {"name": "transfer", "type": "transfer"} + ], + "events": [ + {"name": "balance", "type": "balance_event"}, + {"name": "currency", "type": "currency_stats"} + ], + "tables": [{ + "name": "accounts", "type": "account", + "indexes": [{ + "name": "primary", "unique": true, + "orders": [ + {"field": "balance._sym", "order": "asc"} + ] + } + ] + }, { + "name": "stat", "type": "currency_stats", "scope_type": "symbol_code", + "indexes": [{ + "name": "primary", "unique": true, + "orders": [ + {"field": "supply._sym", "order": "asc"} + ] + } + ] + } + ], + "variants": [] +} diff --git a/scripts/deployutils b/scripts/deployutils new file mode 160000 index 000000000..70ea6a9c8 --- /dev/null +++ b/scripts/deployutils @@ -0,0 +1 @@ +Subproject commit 70ea6a9c8a6f7c6c179a28cfd68f86f47b4737d1 From d7d8959598d25e62afa92ae0278ab121c247dd31 Mon Sep 17 00:00:00 2001 From: Andrew Falaleev Date: Thu, 19 Mar 2020 20:46:03 +0700 Subject: [PATCH 07/21] Refactor binary extension in cyber.govern after updates in CDT. --- .../include/cyber.govern/cyber.govern.hpp | 33 +++---------------- cyber.govern/src/cyber.govern.cpp | 4 +-- 2 files changed, 6 insertions(+), 31 deletions(-) diff --git a/cyber.govern/include/cyber.govern/cyber.govern.hpp b/cyber.govern/include/cyber.govern/cyber.govern.hpp index a8e799718..038ce258b 100644 --- a/cyber.govern/include/cyber.govern/cyber.govern.hpp +++ b/cyber.govern/include/cyber.govern/cyber.govern.hpp @@ -25,34 +25,9 @@ struct structures { uint16_t last_producers_num = 1; // using of binary_extension is a temporary decision only for upgrade phase - eosio::binary_extension last_resize_step; - eosio::binary_extension resize_shift; - eosio::binary_extension schedule_version; - - // this operator is required to set binary extension fields - void operator = (const state_info& s) { - last_schedule_increase = s.last_schedule_increase; - block_num = s.block_num; - target_emission_per_block = s.target_emission_per_block; - funds = s.funds; - last_propose_block_num = s.last_propose_block_num; - required_producers_num = s.required_producers_num; - last_producers_num = s.last_producers_num; - - set_extension_field(last_resize_step, s.last_resize_step); - set_extension_field(resize_shift, s.resize_shift); - set_extension_field(schedule_version, s.schedule_version); - } - - template - void set_extension_field(eosio::binary_extension& dst, const eosio::binary_extension& src) const { - // temporary decision only for upgrade phase - if (src.has_value()) { - dst.emplace(src.value()); - } else { - dst.reset(); - } - } + eosio::binary_extension last_resize_step; + eosio::binary_extension resize_shift; + eosio::binary_extension schedule_version; }; struct [[using eosio: event("burnreward"), contract("cyber.govern")]] balance_struct { @@ -95,7 +70,7 @@ struct structures { public: using contract::contract; - [[eosio::action]] void onblock(name producer, eosio::binary_extension schedule_version); + [[eosio::action]] void onblock(name producer, const eosio::binary_extension& schedule_version); [[eosio::action]] void setshift(int8_t shift); }; diff --git a/cyber.govern/src/cyber.govern.cpp b/cyber.govern/src/cyber.govern.cpp index 35b2542db..c6c410f5a 100644 --- a/cyber.govern/src/cyber.govern.cpp +++ b/cyber.govern/src/cyber.govern.cpp @@ -11,7 +11,7 @@ using namespace cyber::config; namespace cyber { -void govern::onblock(name producer, eosio::binary_extension schedule_version) { +void govern::onblock(name producer, const eosio::binary_extension& schedule_version) { require_auth(_self); auto state = state_singleton(_self, _self.value); @@ -74,7 +74,7 @@ void govern::onblock(name producer, eosio::binary_extension schedule_v s.schedule_version.emplace(schedule_version.value()); } - state.set(s, _self); + state.set(std::move(s), _self); } void govern::reward_workers(structures::state_info& s) { From b1ecb780d2b88c52e155621efe99254b9afc2885 Mon Sep 17 00:00:00 2001 From: Andrew Falaleev Date: Thu, 19 Mar 2020 21:14:13 +0700 Subject: [PATCH 08/21] Update cyber.govern.abi. --- cyber.govern/cyber.govern.abi | 86 +++++++++-------------------------- 1 file changed, 22 insertions(+), 64 deletions(-) diff --git a/cyber.govern/cyber.govern.abi b/cyber.govern/cyber.govern.abi index d4be7d128..b10ff0583 100644 --- a/cyber.govern/cyber.govern.abi +++ b/cyber.govern/cyber.govern.abi @@ -8,40 +8,22 @@ {"name": "account", "type": "name"}, {"name": "amount", "type": "int64"} ] - }, { - "name": "omission_struct", "base": "", - "fields": [ - {"name": "account", "type": "name"}, - {"name": "count", "type": "uint16"}, - {"name": "resets", "type": "uint16"} - ] }, { "name": "onblock", "base": "", "fields": [ - {"name": "producer", "type": "name"} - ] - }, { - "name": "pending_producers_info", "base": "", - "fields": [ - {"name": "id", "type": "uint64"}, - {"name": "accounts", "type": "name[]"} + {"name": "producer", "type": "name"}, + {"name": "schedule_version", "type": "uint32$"} ] }, { "name": "producer_struct", "base": "", "fields": [ - {"name": "account", "type": "name"} - ] - }, { - "name": "schedule_resize_info", "base": "", - "fields": [ - {"name": "id", "type": "uint64"}, - {"name": "last_step", "type": "time_point_sec"}, - {"name": "shift", "type": "int8"} - ] - }, { - "name": "setactprods", "base": "", - "fields": [ - {"name": "pending_active_producers", "type": "name[]"} + {"name": "account", "type": "name"}, + {"name": "is_oblidged", "type": "bool"}, + {"name": "amount", "type": "int64"}, + {"name": "unconfirmed_amount", "type": "int64"}, + {"name": "omission_count", "type": "uint16"}, + {"name": "omission_resets", "type": "uint16"}, + {"name": "last_time", "type": "time_point_sec"} ] }, { "name": "setshift", "base": "", @@ -58,13 +40,15 @@ {"name": "funds", "type": "int64"}, {"name": "last_propose_block_num", "type": "uint32"}, {"name": "required_producers_num", "type": "uint16"}, - {"name": "last_producers_num", "type": "uint16"} + {"name": "last_producers_num", "type": "uint16"}, + {"name": "last_resize_step", "type": "time_point_sec$"}, + {"name": "resize_shift", "type": "int8$"}, + {"name": "schedule_version", "type": "uint32$"} ] } ], "actions": [ {"name": "onblock", "type": "onblock"}, - {"name": "setactprods", "type": "setactprods"}, {"name": "setshift", "type": "setshift"} ], "events": [ @@ -89,52 +73,26 @@ } ] }, { - "name": "obligedprod", "type": "producer_struct", - "indexes": [{ - "name": "primary", "unique": true, - "orders": [ - {"field": "account", "order": "asc"} - ] - } - ] - }, { - "name": "omission", "type": "omission_struct", + "name": "producer", "type": "producer_struct", "indexes": [{ "name": "primary", "unique": true, "orders": [ {"field": "account", "order": "asc"} ] }, { - "name": "bycount", "unique": false, - "orders": [ - {"field": "count", "order": "desc"} - ] - } - ] - }, { - "name": "pendingprods", "type": "pending_producers_info", - "indexes": [{ - "name": "primary", "unique": true, + "name": "byoblidged", "unique": false, "orders": [ - {"field": "id", "order": "asc"} + {"field": "is_oblidged", "order": "desc"} ] - } - ] - }, { - "name": "schedresize", "type": "schedule_resize_info", - "indexes": [{ - "name": "primary", "unique": true, + }, { + "name": "bybalance", "unique": false, "orders": [ - {"field": "id", "order": "asc"} + {"field": "amount", "order": "desc"} ] - } - ] - }, { - "name": "uncbalance", "type": "balance_struct", - "indexes": [{ - "name": "primary", "unique": true, + }, { + "name": "bytime", "unique": false, "orders": [ - {"field": "account", "order": "asc"} + {"field": "last_time", "order": "asc"} ] } ] From 9bfc56b922ffb9eca13e324d7177268f1523a132 Mon Sep 17 00:00:00 2001 From: Andrew Falaleev Date: Fri, 20 Mar 2020 01:15:12 +0700 Subject: [PATCH 09/21] Change order of fields in the cyber.govern::state --- cyber.govern/cyber.govern.abi | 4 ++-- cyber.govern/include/cyber.govern/cyber.govern.hpp | 6 +++--- cyber.govern/src/cyber.govern.cpp | 6 ++++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/cyber.govern/cyber.govern.abi b/cyber.govern/cyber.govern.abi index b10ff0583..1b305c0a5 100644 --- a/cyber.govern/cyber.govern.abi +++ b/cyber.govern/cyber.govern.abi @@ -41,9 +41,9 @@ {"name": "last_propose_block_num", "type": "uint32"}, {"name": "required_producers_num", "type": "uint16"}, {"name": "last_producers_num", "type": "uint16"}, - {"name": "last_resize_step", "type": "time_point_sec$"}, + {"name": "schedule_version", "type": "uint32$"}, {"name": "resize_shift", "type": "int8$"}, - {"name": "schedule_version", "type": "uint32$"} + {"name": "last_resize_step", "type": "time_point_sec$"} ] } ], diff --git a/cyber.govern/include/cyber.govern/cyber.govern.hpp b/cyber.govern/include/cyber.govern/cyber.govern.hpp index 038ce258b..65b64b28c 100644 --- a/cyber.govern/include/cyber.govern/cyber.govern.hpp +++ b/cyber.govern/include/cyber.govern/cyber.govern.hpp @@ -25,9 +25,9 @@ struct structures { uint16_t last_producers_num = 1; // using of binary_extension is a temporary decision only for upgrade phase - eosio::binary_extension last_resize_step; - eosio::binary_extension resize_shift; eosio::binary_extension schedule_version; + eosio::binary_extension resize_shift; + eosio::binary_extension last_resize_step; }; struct [[using eosio: event("burnreward"), contract("cyber.govern")]] balance_struct { @@ -70,7 +70,7 @@ struct structures { public: using contract::contract; - [[eosio::action]] void onblock(name producer, const eosio::binary_extension& schedule_version); + [[eosio::action]] void onblock(name producer, eosio::binary_extension schedule_version); [[eosio::action]] void setshift(int8_t shift); }; diff --git a/cyber.govern/src/cyber.govern.cpp b/cyber.govern/src/cyber.govern.cpp index c6c410f5a..25aaef25c 100644 --- a/cyber.govern/src/cyber.govern.cpp +++ b/cyber.govern/src/cyber.govern.cpp @@ -11,7 +11,7 @@ using namespace cyber::config; namespace cyber { -void govern::onblock(name producer, const eosio::binary_extension& schedule_version) { +void govern::onblock(name producer, eosio::binary_extension schedule_version) { require_auth(_self); auto state = state_singleton(_self, _self.value); @@ -72,6 +72,8 @@ void govern::onblock(name producer, const eosio::binary_extension& sch remove_old_producers(producers_table); } s.schedule_version.emplace(schedule_version.value()); + } else { + s.schedule_version.emplace(0); } state.set(std::move(s), _self); @@ -263,7 +265,7 @@ void govern::setshift(int8_t shift) { auto s = state.get(); // no default values, because it was created on the first block, errors can happens only in tests eosio::check(shift != s.resize_shift.value(), "the shift has not changed"); s.resize_shift.emplace(shift); - state.set(s, _self); + state.set(std::move(s), _self); } void govern::promote_producers(producers& producers_table) { From a297480a269bb56cbe410e8cc3a6d9085b0df0db Mon Sep 17 00:00:00 2001 From: zxcat <550974+zxcat@users.noreply.github.com> Date: Fri, 13 Mar 2020 18:21:13 +0300 Subject: [PATCH 10/21] Add safe (also updates tester) #321 --- common/config.hpp | 5 +- .../include/cyber.token/cyber.token.hpp | 207 ++++- cyber.token/src/cyber.token.cpp | 201 +++++ tests/cyber.token_safe_tests.cpp | 760 ++++++++++++++++++ tests/cyber.token_test_api.hpp | 182 ++++- tests/golos_tester.cpp | 13 +- tests/golos_tester.hpp | 4 +- tests/test_api_helper.hpp | 19 +- 8 files changed, 1373 insertions(+), 18 deletions(-) create mode 100644 tests/cyber.token_safe_tests.cpp diff --git a/common/config.hpp b/common/config.hpp index 88114f1a4..42aa57698 100644 --- a/common/config.hpp +++ b/common/config.hpp @@ -10,10 +10,13 @@ #ifdef UNIT_TEST_ENV # include +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu-string-literal-operator-template" template inline eosio::chain::name operator ""_n() { - return eosio::chain::name({Str...}); + return eosio::chain::name({Str...}); } +#pragma clang diagnostic pop #endif #define CYBER_TOKEN "cyber.token" diff --git a/cyber.token/include/cyber.token/cyber.token.hpp b/cyber.token/include/cyber.token/cyber.token.hpp index 8308aa93c..d44c50a90 100644 --- a/cyber.token/include/cyber.token/cyber.token.hpp +++ b/cyber.token/include/cyber.token/cyber.token.hpp @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -66,13 +67,131 @@ namespace eosio { [[eosio::action]] void close( name owner, const symbol& symbol ); + /** + \brief The \ref enablesafe action enables a safe on given balance and sets its initial parameters. + + \param owner account name of safe owner + \param unlock amount of tokens to initially unlock in the safe. This parameter must be greater than or equal to "0" and have correct token symbol + \param delay duration of locked period (in seconds). This parameter must be greater than "0" + \param trusted name of trusted account; empty value means no trusted account set, otherwise account must exist and its name can't be equal to \a owner + + The balance owner calls this action to enable a safe for tokens with symbol provided in asset and set its initial parameters. The safe must not exist when this action is called. The action locks tokens instantly. + + When enabled, the safe locks all tokens except \a unlock amount. Locked tokens can still be used, but they cannot be transferred. When \a owner transfers tokens, the unlocked amount is reduced by a corresponding value. The balance owner can not transfer more tokens than he unlocked. Tokens obtained from incoming transfer/issue are automatically locked. + + \signreq + — the \a owner account. + */ + [[eosio::action]] void enablesafe(name owner, asset unlock, uint32_t delay, name trusted); + + /** + \brief The \ref disablesafe action disables a safe on given balance and sets its initial parameters. + + \param owner account name of safe owner + \param sym_code symbol code of the token for which the safe is disabling + \param mod_id named identifier of the current safe change. It is required to provide the same value to find delayed disable when apply or cancel it. The name must be empty ("") if disable instantly + + The balance owner calls this action to disable his safe for \a sym_code tokens. The safe must be enabled when calling this action. The action disables the safe with a delay set previously. If a trusted account has been set and action contains his authorization, the safe is disabled instantly. + + \signreq + — the \a owner account (required) + — the trusted account from the currently active safe parameters (optional). + */ + [[eosio::action]] void disablesafe(name owner, symbol_code sym_code, name mod_id); + + /** + \brief The \ref unlocksafe action unlocks some funds in a safe. + + \param owner account name of safe owner + \param unlock amount of tokens to unlock in the safe. This parameter must be greater than "0", it may exceed balance + \param mod_id identifier of the current change. It is used to find delayed unlock when apply or cancel it. The name must be empty ("") if unlocking instantly + + The balance owner calls this action to increase amount of unlocked tokens in the safe. Tokens are unlocked with a delay set previously. If a trusted account has been set and the action contains his authorization, then the tokens are unlocked instantly, otherwise unlock should be applied after a delay using the \ref applysafemod action. + + \signreq + — the \a owner account (required) + — the trusted account from the currently active safe parameters (optional). + */ + [[eosio::action]] void unlocksafe(name owner, asset unlock, name mod_id); + + /** + \brief The \ref locksafe action locks previously unlocked tokens. + + \param owner account name of safe owner + \param lock amount of unlocked tokens to lock in the safe. To lock all tokens, it is required to specify "0" (and correct token symbol), otherwise this parameter must be greater than "0" and not greater than amount of currently unlocked tokens + + The balance owner calls this action to lock tokens previously unlocked with \ref enablesafe or \ref unlocksafe (followed by \ref applysafemod). The action instantly reduces amount of unlocked tokens by value provided in \a lock. + + \signreq + — the \a owner account. + */ + [[eosio::action]] void locksafe(name owner, asset lock); + + /** + \brief The \ref modifysafe action changes delay and/or trusted account of a safe. + + \param owner account name of safe owner + \param sym_code symbol code of tokens for which safe parameters are to be changed + \param mod_id identifier of the current change. It is used to find delayed change when apply or cancel it. The name must be empty ("") if changing instantly + \param delay new duration of locked period (optional, in seconds). This parameter must be greater than "0". It must be specified if no \a trusted account set. The parameter value must be different from the current one if set in instant change + \param trusted name of a trusted account (optional). To remove trusted account it needs to be an empty name. The name must be specify if no \a delay set. The parameter must differ from the current value if set in instant change + + The balance owner calls this action to change some safe parameters. The safe must be enabled earlier. The change is delayed by the current value of the \a delay parameter. If a trusted account has been set and the action contains his authorization, the new parameters are applied instantly. Otherwise they should be applied after a delay using \ref applysafemod action. + + \signreq + — the \a owner account (required) + — the trusted account from the currently active parameters (optional). + */ + [[eosio::action]] void modifysafe(name owner, symbol_code sym_code, name mod_id, + std::optional delay, std::optional trusted); + + /** + \brief The \ref applysafemod action applies delayed change of a safe (tokens unlock or new parameters or disable a safe). + + \param owner account name of safe owner + \param mod_id identifier of the change to apply + + The balance owner calls this action to apply delayed tokens unlock or change of the safe parameters. If current safe parameters have trusted account and the action contains his authorization, then new parameters are applied instantly. Otherwise they can be applied only after a delay. + + \signreq + — the \a owner account (required) + — the trusted account from the currently active parameters (optional). + */ + [[eosio::action]] void applysafemod(name owner, name mod_id); + + /** + \brief The \ref cancelsafemod action cancels delayed change of a safe (tokens unlock or new parameters, or disable a safe). + + \param owner account name of safe owner + \param mod_id identifier of the change to cancel + + The balance owner calls this action to instantly cancel delayed tokens unlock or change of the safe parameters. + + \signreq + — the \a owner account. + */ + [[eosio::action]] void cancelsafemod(name owner, name mod_id); + + /** + \brief The \ref globallock action locks all tokens and delayed mods to given period of time. + + \param owner account name of tokens owner + \param period lock duration (in seconds). This parameter can not reduce existing global lock duration. Its value must be greater than "0" + + The balance owner (or authorized recovery contract) calls this action to instantly lock all of his tokens and prevent delayed mods execution for a specified period of time. Global lock has higher priority than safe unlocked tokens. + + \signreq + — the \a owner account. + */ + [[eosio::action]] void globallock(name owner, uint32_t period); // TODO: Can also add "deletelock" to free storage + static asset get_supply( name token_contract_account, symbol_code sym_code ) { stats statstable( token_contract_account, sym_code.raw() ); const auto& st = statstable.get( sym_code.raw() ); return st.supply; } - + static asset get_max_supply( name token_contract_account, symbol_code sym_code ) { stats statstable( token_contract_account, sym_code.raw() ); @@ -100,6 +219,26 @@ namespace eosio { return accountstable.find( sym_code.raw() ) != accountstable.end(); } + static inline void validate_symbol(name token_contract, const asset a) { + validate_symbol(token_contract, a.symbol); + } + + static inline void validate_symbol(name token_contract, const symbol sym) { + auto supply = get_supply(token_contract, sym.code()); + check(supply.symbol == sym, "symbol precision mismatch"); + } + + static inline time_point_sec get_global_lock_time(name token_contract, name account) { + lock_singleton lock(token_contract, account.value); + return lock.get_or_default().unlocks; + } + + static inline bool get_global_lock_state(name token_contract, name account, uint32_t period=0) { + auto unlock_time = get_global_lock_time(token_contract, account); + time_point_sec time{eosio::current_time_point() + eosio::seconds(period)}; + return time <= unlock_time; + } + private: struct account { asset balance; @@ -122,11 +261,64 @@ namespace eosio { asset payments; }; + /** + \brief DB record containing information about safe for tokens of a certain symbol on the account balance + \ingroup token_tables + */ + // DOCS_TABLE: safe + struct safe { + asset unlocked; //!< Number of unlocked tokens in the safe + name trusted; //!< Trusted account, disabled if empty + uint32_t delay; //!< Delay in seconds of unlock/modify period + + uint64_t primary_key() const { return unlocked.symbol.code().raw(); } + }; + + /** + \brief DB record containing information about a delayed safe modify + \ingroup token_tables + */ + // DOCS_TABLE: safemod + struct safemod { + name id; //!< modification id of the safe + symbol_code sym_code; //!< symbol code of the safe (tokens symbol code) + time_point_sec date; //!< time when delayed changes become ready to apply + int64_t unlock; //!< number of tokens to unlock or 0 (share_type) + std::optional delay; //!< new delay to set + std::optional trusted; //!< new trusted account to set + + uint64_t primary_key() const { return id.value; } + using key_t = std::tuple; + key_t by_symbol_code() const { return std::make_tuple(sym_code, id); } + +#ifndef UNIT_TEST_ENV + EOSLIB_SERIALIZE(safemod, (id)(sym_code)(date)(unlock)(delay)(trusted)) +#endif + }; + + /** + \brief DB record containing information about a global lock; singleton; scope = safe owner + \ingroup token_tables + */ + // DOCS_TABLE: lock + struct lock { + time_point_sec unlocks; //!< time when lock becomes ineffective + }; + using accounts [[eosio::order("balance._sym")]] = eosio::multi_index<"accounts"_n, account>; using stats [[using eosio: order("supply._sym"), scope_type("symbol_code")]] = eosio::multi_index<"stat"_n, currency_stats>; + using safe_tbl [[eosio::order("unlocked._sym","asc")]] = + eosio::multi_index<"safe"_n, safe>; + using safemod_sym_idx [[using eosio: order("sym_code","asc"), order("id","asc")]] = + eosio::indexed_by<"bysymbolcode"_n, eosio::const_mem_fun>; + using safemod_tbl [[eosio::order("id","asc")]] = + eosio::multi_index<"safemod"_n, safemod, safemod_sym_idx>; + using lock_singleton [[eosio::order("id","asc")]] = + eosio::singleton<"lock"_n, lock>; + void sub_balance( name owner, asset value ); void add_balance( name owner, asset value, name ram_payer ); void add_payment( name owner, asset value, name ram_payer ); @@ -139,5 +331,18 @@ namespace eosio { const asset& quantity, const string& memo, bool payment = false); + + void delay_safe_change( + name owner, asset unlock, name mod_id, std::optional delay, std::optional trusted, + bool check_params = true, bool check_sym = true); + void delay_safe_change( + name owner, symbol_code sym_code, name mod_id, std::optional delay, std::optional trusted, + bool check_params = true); + + static inline bool is_locked(name token_contract, name owner) { + lock_singleton lock(token_contract, owner.value); + return lock.exists() && lock.get().unlocks > eosio::current_time_point(); + } + }; } /// namespace eosio diff --git a/cyber.token/src/cyber.token.cpp b/cyber.token/src/cyber.token.cpp index 6fe75e6c9..fca18fb9f 100644 --- a/cyber.token/src/cyber.token.cpp +++ b/cyber.token/src/cyber.token.cpp @@ -12,6 +12,8 @@ namespace eosio { namespace config { static constexpr size_t max_memo_size = 384; static constexpr char memo_error[] = "memo has more than 384 bytes"; + const uint32_t seconds_per_day = 24 * 60 * 60; // TODO: move to some global consts + static constexpr uint32_t safe_max_delay = 30 * seconds_per_day; // max delay and max lock period } void token::send_currency_event(const currency_stats& stat) { @@ -161,6 +163,16 @@ void token::sub_balance( name owner, asset value ) { a.balance -= value; send_balance_event(owner, a); }); + + check(!is_locked(_self, owner), "balance locked in safe"); + safe_tbl safes(_self, owner.value); + const auto& safe = safes.find(value.symbol.code().raw()); + if (safe != safes.end()) { + safes.modify(safe, owner, [&](auto& s) { + s.unlocked -= value; + check(s.unlocked.amount >= 0, "overdrawn safe unlocked balance"); + }); + } } void token::add_balance( name owner, asset value, name ram_payer ) @@ -280,4 +292,193 @@ void token::bulkpayment(name from, vector recipients) } } +//////////////////////////////////////////////////////////////// +// safe related actions +using std::optional; + +void check_safe_params(name owner, optional delay, optional trusted) { + if (delay) { + check(*delay > 0, "delay must be > 0"); + check(*delay <= config::safe_max_delay, "delay must be <= " + std::to_string(config::safe_max_delay)); + } + if (trusted && *trusted != name()) { + check(owner != *trusted, "trusted and owner must be different accounts"); + check(is_account(*trusted), "trusted account does not exist"); + } +} + +void token::enablesafe(name owner, asset unlock, uint32_t delay, name trusted) { + require_auth(owner); + + check(unlock.amount >= 0, "unlock amount must be >= 0"); + check_safe_params(owner, delay, trusted); + if (unlock.symbol != symbol{}) { + validate_symbol(_self, unlock); + } + + safe_tbl safes(_self, owner.value); + const auto scode = unlock.symbol.code(); + auto safe = safes.find(scode.raw()); + check(safe == safes.end(), "Safe already enabled"); + + // Do not allow to have delayed changes when enable the safe, they came from the previously enabled safe + // and should be cancelled to make clean safe setup. + safemod_tbl mods(_self, owner.value); + auto idx = mods.get_index<"bysymbolcode"_n>(); + auto itr = idx.lower_bound(scode); + check(itr == idx.end() || itr->sym_code != scode, "Can't enable safe with existing delayed mods"); + + safes.emplace(owner, [&](auto& s) { + s.unlocked = unlock; + s.delay = delay; + s.trusted = trusted; + }); +} + +template +void instant_safe_change(Tbl& safes, S& safe, + name owner, int64_t unlock, optional delay, optional trusted, bool ensure_change +) { + if (delay && *delay == 0) { + check(!unlock && !trusted, "SYS: incorrect disabling safe mod"); + safes.erase(safe); + } else { + bool changed = !ensure_change; + safes.modify(safe, owner, [&](auto& s) { + if (unlock) { + s.unlocked.amount += unlock; + check(s.unlocked.is_amount_within_range(), "unlocked overflow"); + changed = true; + } + if (delay && *delay != s.delay) { + s.delay = *delay; + changed = true; + } + if (trusted && *trusted != s.trusted) { + s.trusted = *trusted; + changed = true; + } + check(changed, "Change has no effect and can be cancelled"); + }); + } +} + +// helper for actions which do not change `unlocked` and have incomplete asset symbol +void token::delay_safe_change( + name owner, symbol_code scode, name mod_id, optional delay, optional trusted, + bool check_params/*=true*/ +) { + const asset fake_asset{0, symbol{scode, 0}}; + delay_safe_change(owner, fake_asset, mod_id, delay, trusted, check_params, false); +} + +void token::delay_safe_change( + name owner, asset unlock, name mod_id, optional delay, optional trusted, + bool check_params/*=true*/, bool check_sym/*=true*/ +) { + if (check_params) { + check_safe_params(owner, delay, trusted); + } + if (check_sym) { + validate_symbol(_self, unlock); + } + + const auto scode = unlock.symbol.code(); + safe_tbl safes(_self, owner.value); + const auto& safe = safes.get(scode.raw(), "Safe disabled"); + + const bool have_id = mod_id != name(); + const auto trusted_acc = safe.trusted; + if (trusted_acc != name() && has_auth(trusted_acc)) { + check(!have_id, "mod_id must be empty for trusted action"); + check(!delay || *delay != safe.delay, "Can't set same delay"); + check(!trusted || *trusted != trusted_acc, "Can't set same trusted"); + instant_safe_change(safes, safe, owner, unlock.amount, delay, trusted, false); + } else { + check(have_id, "mod_id must not be empty"); + safemod_tbl mods(_self, owner.value); + check(mods.find(mod_id.value) == mods.end(), "Safe mod with the same id is already exists"); + mods.emplace(owner, [&](auto& d) { + d.id = mod_id; + d.sym_code = scode; + d.date = eosio::current_time_point() + eosio::seconds(safe.delay); + d.unlock = unlock.amount; + d.delay = delay; + d.trusted = trusted; + }); + } +} + +void token::disablesafe(name owner, symbol_code sym_code, name mod_id) { + require_auth(owner); + delay_safe_change(owner, sym_code, mod_id, 0, {}, false); +} + +void token::unlocksafe(name owner, asset unlock, name mod_id) { + require_auth(owner); + check(unlock.amount > 0, "unlock amount must be > 0"); + delay_safe_change(owner, unlock, mod_id, {}, {}); +} + +void token::locksafe(name owner, asset lock) { + require_auth(owner); + check(lock.amount >= 0, "lock amount must be >= 0"); + validate_symbol(_self, lock); // checked within "<= unlocked", but have confusing message, so check here + + const auto scode = lock.symbol.code(); + safe_tbl safes(_self, owner.value); + const auto& safe = safes.get(scode.raw(), "Safe disabled"); + check(safe.unlocked.amount > 0, "nothing to lock"); + check(lock <= safe.unlocked, "lock must be <= unlocked"); + + bool lock_all = lock.amount == 0; + safes.modify(safe, owner, [&](auto& s) { + s.unlocked -= lock_all ? s.unlocked : lock; + }); +} + +void token::modifysafe( + name owner, symbol_code sym_code, name mod_id, optional delay, optional trusted +) { + require_auth(owner); + check(delay || trusted, "delay and/or trusted must be set"); + delay_safe_change(owner, sym_code, mod_id, delay, trusted); +} + +void token::applysafemod(name owner, name mod_id) { + require_auth(owner); + safemod_tbl mods(_self, owner.value); + const auto& mod = mods.get(mod_id.value, "Safe mod not found"); + + safe_tbl safes(_self, owner.value); + const auto& safe = safes.get(mod.sym_code.raw(), "Safe disabled"); + + bool trusted_apply = safe.trusted != name() && has_auth(safe.trusted); + if (!trusted_apply) { + check(mod.date <= eosio::current_time_point(), "Safe change is time locked"); + check(!is_locked(_self, owner), "Safe locked globally"); + } + instant_safe_change(safes, safe, owner, mod.unlock, mod.delay, mod.trusted, true); + mods.erase(mod); +} + +void token::cancelsafemod(name owner, name mod_id) { + require_auth(owner); + safemod_tbl mods(_self, owner.value); + const auto& mod = mods.get(mod_id.value, "Safe mod not found"); + mods.erase(mod); +} + +void token::globallock(name owner, uint32_t period) { + require_auth(owner); + check(period > 0, "period must be > 0"); + check(period <= config::safe_max_delay, "period must be <= " + std::to_string(config::safe_max_delay)); + + time_point_sec unlocks{eosio::current_time_point() + eosio::seconds(period)}; + lock_singleton lock(_self, owner.value); + check(unlocks > lock.get_or_default().unlocks, "new unlock time must be greater than current"); + + lock.set({unlocks}, owner); +} + } /// namespace eosio diff --git a/tests/cyber.token_safe_tests.cpp b/tests/cyber.token_safe_tests.cpp new file mode 100644 index 000000000..c0e54bb14 --- /dev/null +++ b/tests/cyber.token_safe_tests.cpp @@ -0,0 +1,760 @@ +#include "golos_tester.hpp" +#include "cyber.token_test_api.hpp" +#include "common/config.hpp" +#include "contracts.hpp" + + +using namespace cyber; +using namespace eosio::testing; +using namespace eosio::chain; +using namespace fc; +static const auto _token = symbol(3, "GLS"); +static const auto _token2 = symbol(3, "TEST"); + +namespace cfg { + const account_name token_name = N(cyber.token); + constexpr uint32_t safe_max_delay = 30*24*60*60; +} + + +class cyber_token_safe_tester : public golos_tester { +protected: + cyber_token_api token; + cyber_token_api token2; + +public: + cyber_token_safe_tester() + : golos_tester(cfg::token_name) + , token({this, _code, _token}) + , token2({this, _code, _token2}) { + create_accounts({_issuer, _alice, _bob, _carol, cfg::token_name}); + produce_block(); + install_contract(_code, contracts::token_wasm(), contracts::token_abi()); + } + +protected: + const uint32_t _delay = 60; + const double _total = 400; + +public: + void init(name issuer = {}) { + if (issuer.empty()) issuer = _issuer; + const auto init_tokens = [&](cyber_token_api& token) { + BOOST_CHECK_EQUAL(success(), token.create(issuer, token.make_asset(_total * 5))); + BOOST_CHECK_EQUAL(success(), token.issue(issuer, issuer, token.make_asset(_total))); + }; + init_tokens(token); + init_tokens(token2); + } + + const account_name _issuer = N(issuer); + const account_name _alice = N(alice); + const account_name _bob = N(bob); + const account_name _carol = N(carol); + const account_name _nobody = N(nobody); // not existing account + + struct errors: contract_error_messages { + const string symbol_precision = amsg("symbol precision mismatch"); + const string unlock_lt0 = amsg("unlock amount must be >= 0"); + const string unlock_lte0 = amsg("unlock amount must be > 0"); + const string lock_lt0 = amsg("lock amount must be >= 0"); + const string unlocked_eq0 = amsg("nothing to lock"); + const string delay_lte0 = amsg("delay must be > 0"); + const string delay_gt_max = amsg("delay must be <= " + std::to_string(cfg::safe_max_delay)); + const string trusted_eq_owner = amsg("trusted and owner must be different accounts"); + const string trusted_not_exists = amsg("trusted account does not exist"); + const string already_enabled = amsg("Safe already enabled"); + const string have_mods = amsg("Can't enable safe with existing delayed mods"); + const string disabled = amsg("Safe disabled"); + const string same_delay = amsg("Can't set same delay"); + const string same_trusted = amsg("Can't set same trusted"); + const string empty_mod_id = amsg("mod_id must not be empty"); + const string have_mod_id = amsg("mod_id must be empty for trusted action"); + const string same_mod_id = amsg("Safe mod with the same id is already exists"); + const string lock_gt_unlocked = amsg("lock must be <= unlocked"); + const string nothing_set = amsg("delay and/or trusted must be set"); + const string mod_not_exists = amsg("Safe mod not found"); + const string still_locked = amsg("Safe change is time locked"); + const string nothing_to_apply = amsg("Change has no effect and can be cancelled"); + const string global_lock = amsg("balance locked in safe"); + const string mod_global_lock = amsg("Safe locked globally"); + const string balance_lock = amsg("overdrawn safe unlocked balance"); + const string balance_over = amsg("overdrawn balance"); + const string unlocked_over = amsg("unlocked overflow"); + const string period_le0 = amsg("period must be > 0"); + const string period_gt_max = amsg("period must be <= " + std::to_string(cfg::safe_max_delay)); + const string period_le_cur = amsg("new unlock time must be greater than current"); + } err; +}; + +BOOST_AUTO_TEST_SUITE(cyber_token_safe) + +BOOST_FIXTURE_TEST_CASE(enable, cyber_token_safe_tester) try { + BOOST_TEST_MESSAGE("Enable the safe"); + init(); + BOOST_CHECK(token.get_safe(_bob).is_null()); + BOOST_TEST_MESSAGE("--- fail on bad params"); + BOOST_CHECK_EQUAL(err.unlock_lt0, token.enable_safe(_bob, -1, 0)); + BOOST_CHECK_EQUAL(err.unlock_lt0, token.enable_safe(_bob, -token.satoshi(), 0)); + BOOST_CHECK_EQUAL(err.delay_lte0, token.enable_safe(_bob, 0, 0)); + BOOST_CHECK_EQUAL(err.delay_gt_max, token.enable_safe(_bob, 0, cfg::safe_max_delay + 1)); + BOOST_CHECK_EQUAL(err.trusted_eq_owner, token.enable_safe(_bob, 0, _delay, _bob)); + BOOST_CHECK_EQUAL(err.trusted_not_exists, token.enable_safe(_bob, 0, _delay, _nobody)); + const asset bad_asset{0, token.bad_sym()}; + BOOST_CHECK_EQUAL(err.symbol_precision, token.enable_safe(_bob, bad_asset, _delay)); + + BOOST_TEST_MESSAGE("--- success on valid params"); + BOOST_CHECK_EQUAL(success(), token.enable_safe(_bob, 0, _delay)); + CHECK_MATCHING_OBJECT(token.get_safe(_bob), token.make_safe(0, _delay)); + produce_block(); + + BOOST_TEST_MESSAGE("--- fail if already created"); + BOOST_CHECK_EQUAL(err.already_enabled, token.enable_safe(_bob, 0, _delay)); + BOOST_CHECK_EQUAL(err.already_enabled, token.enable_safe(_bob, 100500, cfg::safe_max_delay)); + + BOOST_TEST_MESSAGE("--- success when create for different tokens with over-limit and max delay"); + BOOST_CHECK_EQUAL(success(), token2.enable_safe(_bob, 100500, cfg::safe_max_delay)); + CHECK_MATCHING_OBJECT(token2.get_safe(_bob), token2.make_safe(100500, cfg::safe_max_delay)); + + // enable_safe with existing delayed mods checked in "disable safe test" +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE(disable, cyber_token_safe_tester) try { + BOOST_TEST_MESSAGE("Disable the safe"); + const auto mod_id = N(mod); + init(); + + BOOST_TEST_MESSAGE("--- fail if not enabled"); + BOOST_CHECK_EQUAL(err.disabled, token.disable_safe(_bob, {})); + BOOST_CHECK_EQUAL(err.disabled, token.disable_safe(_bob, mod_id)); + + BOOST_TEST_MESSAGE("--- enable for " << token.name()); + BOOST_CHECK_EQUAL(success(), token.enable_safe(_bob, 0, _delay)); + CHECK_MATCHING_OBJECT(token.get_safe(_bob), token.make_safe(0, _delay)); + produce_block(); + + BOOST_TEST_MESSAGE("--- still fail for " << token2.name()); + BOOST_CHECK_EQUAL(err.disabled, token2.disable_safe(_bob, {})); + BOOST_CHECK_EQUAL(err.disabled, token2.disable_safe(_bob, mod_id)); + + BOOST_TEST_MESSAGE("--- fail on bad params"); + BOOST_CHECK_EQUAL(err.empty_mod_id, token.disable_safe(_bob, {})); + + BOOST_TEST_MESSAGE("--- success on valid params"); + BOOST_CHECK_EQUAL(success(), token.disable_safe(_bob, mod_id)); + CHECK_MATCHING_OBJECT(token.get_safe_mod(_bob, mod_id), token.make_safe_mod(mod_id, 0, 0)); + BOOST_CHECK_EQUAL(err.still_locked, token.apply_safe_mod(_bob, mod_id)); + produce_block(); + + BOOST_TEST_MESSAGE("--- fail if same mod_id and success if other"); + const auto mod2_id = N(mod2); + BOOST_CHECK_EQUAL(success(), token.disable_safe(_bob, mod2_id)); + produce_block(); + BOOST_CHECK_EQUAL(err.same_mod_id, token.disable_safe(_bob, mod_id)); + BOOST_CHECK_EQUAL(err.same_mod_id, token.disable_safe(_bob, mod2_id)); + + BOOST_TEST_MESSAGE("--- wait and disable"); + const auto blocks = seconds_to_blocks(_delay); + produce_blocks(blocks - 1 - 2); // 2 for two produce_block() calls + BOOST_CHECK_EQUAL(err.still_locked, token.apply_safe_mod(_bob, mod_id)); + produce_block(); + BOOST_CHECK_EQUAL(success(), token.apply_safe_mod(_bob, mod_id)); + BOOST_CHECK_EQUAL(err.disabled, token.disable_safe(_bob, mod_id)); + BOOST_CHECK(token.get_safe(_bob).is_null()); + BOOST_CHECK(token.get_safe_mod(_bob, mod_id).is_null()); + + BOOST_TEST_MESSAGE("--- fail on re-enabling with existing mod"); + BOOST_CHECK_EQUAL(err.have_mods, token.enable_safe(_bob, 0, _delay)); + + BOOST_TEST_MESSAGE("--- success when existing mod is for other symbol"); + BOOST_CHECK_EQUAL(success(), token2.enable_safe(_bob, 0, _delay)); + + BOOST_TEST_MESSAGE("--- success when no more mods"); + BOOST_CHECK_EQUAL(success(), token.cancel_safe_mod(_bob, mod2_id)); + BOOST_CHECK_EQUAL(success(), token.enable_safe(_bob, 0, _delay)); + CHECK_MATCHING_OBJECT(token.get_safe(_bob), token.make_safe(0, _delay)); +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE(lock_unlock, cyber_token_safe_tester) try { + BOOST_TEST_MESSAGE("Unlock and lock the safe"); + init(); + + BOOST_TEST_MESSAGE("--- fail if not enabled"); + const double one = 1; + const auto mod_id = N(mod); + BOOST_CHECK_EQUAL(err.disabled, token.unlock_safe(_bob, {}, one)); + BOOST_CHECK_EQUAL(err.disabled, token.unlock_safe(_bob, mod_id, one)); + BOOST_CHECK_EQUAL(err.disabled, token.lock_safe(_bob, one)); + + BOOST_TEST_MESSAGE("--- fail if negative amount"); + BOOST_CHECK_EQUAL(err.unlock_lte0, token.unlock_safe(_bob, {}, -1)); + BOOST_CHECK_EQUAL(err.unlock_lte0, token.unlock_safe(_bob, mod_id, -1)); + BOOST_CHECK_EQUAL(err.lock_lt0, token.lock_safe(_bob, -1)); + BOOST_TEST_MESSAGE("--- fail if unlock with wrong precision"); + const asset bad{1, token.bad_sym()}; + BOOST_CHECK_EQUAL(err.symbol_precision, token.unlock_safe(_bob, {}, bad)); + BOOST_CHECK_EQUAL(err.symbol_precision, token.unlock_safe(_bob, mod_id, bad)); + BOOST_CHECK_EQUAL(err.symbol_precision, token.lock_safe(_bob, bad)); + BOOST_CHECK_EQUAL(err.symbol_precision, token.lock_safe(_bob, asset{1, token.bad_sym(-1)})); + + BOOST_TEST_MESSAGE("--- enable 1 token unlocked for " << token.name()); + BOOST_CHECK_EQUAL(success(), token.enable_safe(_bob, one, _delay)); + CHECK_MATCHING_OBJECT(token.get_safe(_bob), token.make_safe(one, _delay)); + BOOST_TEST_MESSAGE("--- enable full locked for " << token2.name()); + BOOST_CHECK_EQUAL(success(), token2.enable_safe(_bob, 0, _delay)); + produce_block(); + + BOOST_TEST_MESSAGE("--- fail if unlock with empty id"); + BOOST_CHECK_EQUAL(err.empty_mod_id, token.unlock_safe(_bob, {}, one)); + BOOST_TEST_MESSAGE("--- fail if lock too much"); + const auto satoshi = token.satoshi(); + BOOST_CHECK_EQUAL(err.lock_gt_unlocked, token.lock_safe(_bob, one + satoshi)); + BOOST_CHECK_EQUAL(err.unlocked_eq0, token2.lock_safe(_bob, token2.satoshi())); + + BOOST_TEST_MESSAGE("--- success if lock satoshi"); + BOOST_CHECK_EQUAL(success(), token.lock_safe(_bob, satoshi)); + BOOST_TEST_MESSAGE("--- success if lock half"); + const auto half = one / 2; + BOOST_CHECK_EQUAL(success(), token.lock_safe(_bob, half)); + CHECK_MATCHING_OBJECT(token.get_safe(_bob), token.make_safe(one - half - satoshi, _delay)); + produce_block(); + BOOST_CHECK_EQUAL(err.lock_gt_unlocked, token.lock_safe(_bob, half)); + BOOST_TEST_MESSAGE("--- success if lock remainig"); + BOOST_CHECK_EQUAL(success(), token.lock_safe(_bob, half - satoshi)); + CHECK_MATCHING_OBJECT(token.get_safe(_bob), token.make_safe(0, _delay)); + BOOST_TEST_MESSAGE("--- fail if lock one more satoshi"); + BOOST_CHECK_EQUAL(err.unlocked_eq0, token.lock_safe(_bob)); + BOOST_CHECK_EQUAL(err.unlocked_eq0, token.lock_safe(_bob, satoshi)); + BOOST_CHECK_EQUAL(err.unlocked_eq0, token2.lock_safe(_bob)); + + BOOST_TEST_MESSAGE("--- success if schedule valid unlock"); + const auto mod2_id = N(mod2); + BOOST_CHECK(token.get_safe_mod(_bob, mod_id).is_null()); + BOOST_CHECK(token.get_safe_mod(_bob, mod2_id).is_null()); + BOOST_CHECK_EQUAL(success(), token.unlock_safe(_bob, mod_id, one)); + BOOST_CHECK_EQUAL(success(), token.unlock_safe(_bob, mod2_id, one)); + CHECK_MATCHING_OBJECT(token.get_safe_mod(_bob, mod_id), token.make_safe_mod(mod_id, one)); + CHECK_MATCHING_OBJECT(token.get_safe_mod(_bob, mod2_id), token.make_safe_mod(mod2_id, one)); + BOOST_CHECK_EQUAL(err.same_mod_id, token.unlock_safe(_bob, mod_id, half)); + + BOOST_TEST_MESSAGE("--- fail to apply"); + BOOST_CHECK_EQUAL(err.still_locked, token.apply_safe_mod(_bob, mod_id)); + BOOST_TEST_MESSAGE("--- wait half time and add one more unlock"); + const auto blocks = seconds_to_blocks(_delay); + const auto half_blocks = blocks/2; + produce_blocks(half_blocks); + BOOST_CHECK_EQUAL(err.still_locked, token.apply_safe_mod(_bob, mod_id)); + const auto fat_id = N(fatmod); + BOOST_CHECK_EQUAL(success(), token.unlock_safe(_bob, fat_id, 100500)); + + BOOST_TEST_MESSAGE("--- wait to 1 block before 1st unlocks end ensure it's locked"); + const auto remaining_blocks = blocks - half_blocks; + produce_blocks(remaining_blocks - 1); + BOOST_CHECK_EQUAL(err.still_locked, token.apply_safe_mod(_bob, mod_id)); + BOOST_TEST_MESSAGE("--- wait 1 more block and unlock"); + produce_block(); + BOOST_CHECK_EQUAL(success(), token.apply_safe_mod(_bob, mod_id)); + BOOST_CHECK_EQUAL(err.still_locked, token.apply_safe_mod(_bob, fat_id)); + BOOST_CHECK(token.get_safe_mod(_bob, mod_id).is_null()); + CHECK_MATCHING_OBJECT(token.get_safe(_bob), token.make_safe(one, _delay)); + + BOOST_TEST_MESSAGE("--- wait fat unlock time and check both fat and mod2 applied"); + produce_blocks(blocks - remaining_blocks - 1); + BOOST_CHECK_EQUAL(err.still_locked, token.apply_safe_mod(_bob, fat_id)); + produce_block(); + BOOST_CHECK_EQUAL(success(), token.apply_safe_mod(_bob, fat_id)); + BOOST_CHECK_EQUAL(success(), token.apply_safe_mod(_bob, mod2_id)); + BOOST_CHECK(token.get_safe_mod(_bob, fat_id).is_null()); + BOOST_CHECK(token.get_safe_mod(_bob, mod2_id).is_null()); + CHECK_MATCHING_OBJECT(token.get_safe(_bob), token.make_safe(one+one+100500, _delay)); + + BOOST_TEST_MESSAGE("--- lock all"); + BOOST_CHECK_EQUAL(success(), token.lock_safe(_bob)); + CHECK_MATCHING_OBJECT(token.get_safe(_bob), token.make_safe(0, _delay)); + produce_block(); + BOOST_CHECK_EQUAL(err.unlocked_eq0, token.lock_safe(_bob)); +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE(modify, cyber_token_safe_tester) try { + BOOST_TEST_MESSAGE("Modify the safe"); + init(); + const auto mod_id = N(mod); + const auto delay1 = _delay / 2; + const auto delay2 = _delay * 2; + + BOOST_TEST_MESSAGE("--- fail on bad params"); + BOOST_CHECK_EQUAL(err.nothing_set, token.modify_safe(_bob, mod_id)); + BOOST_CHECK_EQUAL(err.delay_lte0, token.modify_safe(_bob, mod_id, 0)); + BOOST_CHECK_EQUAL(err.delay_gt_max, token.modify_safe(_bob, mod_id, cfg::safe_max_delay + 1)); + BOOST_CHECK_EQUAL(err.trusted_eq_owner, token.modify_safe(_bob, mod_id, {}, _bob)); + BOOST_CHECK_EQUAL(err.trusted_not_exists, token.modify_safe(_bob, mod_id, {}, _nobody)); + + BOOST_TEST_MESSAGE("--- fail if not enabled"); + BOOST_CHECK_EQUAL(err.disabled, token.modify_safe(_bob, mod_id, delay1)); + BOOST_CHECK_EQUAL(err.disabled, token.modify_safe(_bob, mod_id, delay1, _alice)); + BOOST_CHECK_EQUAL(err.disabled, token.modify_safe(_bob, mod_id, {}, _alice)); + + BOOST_TEST_MESSAGE("--- enable full locked for " << token.name()); + BOOST_CHECK_EQUAL(success(), token.enable_safe(_bob, 0, _delay, _alice)); + CHECK_MATCHING_OBJECT(token.get_safe(_bob), token.make_safe(0, _delay, _alice)); + produce_block(); + + BOOST_TEST_MESSAGE("--- success with enabled and correct params"); + BOOST_CHECK_EQUAL(success(), token.modify_safe(_bob, mod_id, _delay, _alice)); + CHECK_MATCHING_OBJECT(token.get_safe_mod(_bob, mod_id), token.make_safe_mod(mod_id, 0, _delay, _alice)); + + BOOST_TEST_MESSAGE("--- prepare mods to check all change cases"); + BOOST_TEST_MESSAGE("------ 1 param: same delay, same trusted, other delay, other trusted"); + const auto mod_delay0 = N(mod.delay); + const auto mod_delay1 = N(mod.delay1); + const auto mod_delay2 = N(mod.delay2); + const auto mod_trust1 = N(mod.trust1); + const auto mod_trust2 = N(mod.trust2); + BOOST_CHECK_EQUAL(success(), token.modify_safe(_bob, mod_delay0, _delay)); + BOOST_CHECK_EQUAL(success(), token.modify_safe(_bob, mod_delay1, delay1)); + BOOST_CHECK_EQUAL(success(), token.modify_safe(_bob, mod_delay2, delay2)); + BOOST_CHECK_EQUAL(success(), token.modify_safe(_bob, mod_trust1, {}, _alice)); + BOOST_CHECK_EQUAL(success(), token.modify_safe(_bob, mod_trust2, {}, _carol)); + CHECK_MATCHING_OBJECT(token.get_safe_mod(_bob, mod_delay0), token.make_safe_mod(mod_delay0, 0, _delay)); + CHECK_MATCHING_OBJECT(token.get_safe_mod(_bob, mod_delay1), token.make_safe_mod(mod_delay1, 0, delay1)); + CHECK_MATCHING_OBJECT(token.get_safe_mod(_bob, mod_delay2), token.make_safe_mod(mod_delay2, 0, delay2)); + CHECK_MATCHING_OBJECT(token.get_safe_mod(_bob, mod_trust1), token.make_safe_mod(mod_trust1, 0, {}, _alice)); + CHECK_MATCHING_OBJECT(token.get_safe_mod(_bob, mod_trust2), token.make_safe_mod(mod_trust2, 0, {}, _carol)); + BOOST_TEST_MESSAGE("------ 2 params: same both, same delay, same trusted, other both"); + const auto mod_d2t2 = N(mod.d2t2); + const auto mod_d1t2 = N(mod.d1t2); + const auto mod_d1t1 = N(mod.d1t1); + const auto mod_dmt2 = N(mod.dmt2); + BOOST_CHECK_EQUAL(success(), token.modify_safe(_bob, mod_d2t2, delay2, _carol)); + BOOST_CHECK_EQUAL(success(), token.modify_safe(_bob, mod_d1t2, _delay, _carol)); + BOOST_CHECK_EQUAL(success(), token.modify_safe(_bob, mod_d1t1, _delay, _alice)); + BOOST_CHECK_EQUAL(success(), token.modify_safe(_bob, mod_dmt2, cfg::safe_max_delay, _carol)); + CHECK_MATCHING_OBJECT(token.get_safe_mod(_bob, mod_d2t2), token.make_safe_mod(mod_d2t2, 0, delay2, _carol)); + CHECK_MATCHING_OBJECT(token.get_safe_mod(_bob, mod_dmt2), token.make_safe_mod(mod_dmt2, 0, cfg::safe_max_delay, _carol)); + + BOOST_TEST_MESSAGE("--- Wait 1 block before unlock time"); + auto blocks = seconds_to_blocks(_delay); + produce_blocks(blocks - 1); + BOOST_CHECK_EQUAL(err.still_locked, token.apply_safe_mod(_bob, mod_id)); + BOOST_CHECK_EQUAL(err.same_mod_id, token.modify_safe(_bob, mod_id, _delay)); + + BOOST_TEST_MESSAGE("--- Wait 1 more block and check changes"); + produce_block(); + BOOST_TEST_MESSAGE("------ fail if 2 params are the same as in current safe"); + BOOST_CHECK_EQUAL(err.nothing_to_apply, token.apply_safe_mod(_bob, mod_id)); + BOOST_TEST_MESSAGE("------ fail if 1 param and same delay"); + BOOST_CHECK_EQUAL(err.nothing_to_apply, token.apply_safe_mod(_bob, mod_delay0)); + BOOST_TEST_MESSAGE("------ fail if 1 param asd same trusted"); + BOOST_CHECK_EQUAL(err.nothing_to_apply, token.apply_safe_mod(_bob, mod_trust1)); + BOOST_TEST_MESSAGE("------ success if 1 param differs from value in current safe"); + BOOST_CHECK_EQUAL(success(), token.apply_safe_mod(_bob, mod_delay2)); + CHECK_MATCHING_OBJECT(token.get_safe(_bob), token.make_safe(0, delay2, _alice)); + BOOST_CHECK_EQUAL(success(), token.apply_safe_mod(_bob, mod_trust2)); + CHECK_MATCHING_OBJECT(token.get_safe(_bob), token.make_safe(0, delay2, _carol)); + BOOST_CHECK(token.get_safe_mod(_bob, mod_delay2).is_null()); + BOOST_CHECK(token.get_safe_mod(_bob, mod_trust2).is_null()); + BOOST_TEST_MESSAGE("------ fail if 2 params are the same as in current safe"); + BOOST_CHECK_EQUAL(err.nothing_to_apply, token.apply_safe_mod(_bob, mod_d2t2)); + BOOST_TEST_MESSAGE("------ success if 1 of 2 params differs from value in current safe"); + BOOST_CHECK_EQUAL(success(), token.apply_safe_mod(_bob, mod_d1t2)); + CHECK_MATCHING_OBJECT(token.get_safe(_bob), token.make_safe(0, _delay, _carol)); + BOOST_CHECK_EQUAL(success(), token.apply_safe_mod(_bob, mod_d1t1)); + CHECK_MATCHING_OBJECT(token.get_safe(_bob), token.make_safe(0, _delay, _alice)); + BOOST_TEST_MESSAGE("------ success if both params differ"); + BOOST_CHECK_EQUAL(success(), token.apply_safe_mod(_bob, mod_dmt2)); + CHECK_MATCHING_OBJECT(token.get_safe(_bob), token.make_safe(0, cfg::safe_max_delay, _carol)); + BOOST_CHECK(token.get_safe_mod(_bob, mod_dmt2).is_null()); + + BOOST_TEST_MESSAGE("--- reduce delay"); + BOOST_CHECK_EQUAL(success(), token.apply_safe_mod(_bob, mod_delay1)); + BOOST_TEST_MESSAGE("--- reuse old mod id to create new mod and ensure it's applied at time"); + BOOST_CHECK_EQUAL(success(), token.modify_safe(_bob, mod_delay1, delay2)); + const auto blocks2 = seconds_to_blocks(delay1); + produce_blocks(blocks2 - 1); + BOOST_CHECK_EQUAL(err.still_locked, token.apply_safe_mod(_bob, mod_delay1)); + produce_block(); + BOOST_CHECK_EQUAL(success(), token.apply_safe_mod(_bob, mod_delay1)); + + BOOST_TEST_MESSAGE("--- apply mod with increased delay and check it's applied at time"); + BOOST_CHECK_EQUAL(success(), token.modify_safe(_bob, mod_delay1, _delay, name{})); + const auto blocks3 = seconds_to_blocks(delay2); + produce_blocks(blocks3 - 1); + BOOST_CHECK_EQUAL(err.still_locked, token.apply_safe_mod(_bob, mod_delay1)); + produce_block(); + BOOST_CHECK_EQUAL(success(), token.apply_safe_mod(_bob, mod_delay1)); + CHECK_MATCHING_OBJECT(token.get_safe(_bob), token.make_safe(0, _delay)); +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE(apply_cancel, cyber_token_safe_tester) try { + BOOST_TEST_MESSAGE("Apply/cancel safe mod"); + init(); + const auto modify_mod = N(mod); + const auto unlock_mod = N(unlock); + const auto killit_mod = N(.k..i...ll); // test exotic name + + const auto create_mods = [&]() { + // create all mod types + BOOST_CHECK_EQUAL(success(), token.unlock_safe(_bob, unlock_mod, 1)); + BOOST_CHECK_EQUAL(success(), token.modify_safe(_bob, modify_mod, _delay, _carol)); + BOOST_CHECK_EQUAL(success(), token.disable_safe(_bob, killit_mod)); + CHECK_MATCHING_OBJECT(token.get_safe_mod(_bob, unlock_mod), token.make_safe_mod(unlock_mod, 1)); + CHECK_MATCHING_OBJECT(token.get_safe_mod(_bob, modify_mod), token.make_safe_mod(modify_mod, 0, _delay, _carol)); + CHECK_MATCHING_OBJECT(token.get_safe_mod(_bob, killit_mod), token.make_safe_mod(killit_mod, 0, 0)); + }; + + BOOST_TEST_MESSAGE("--- fail on non-existing mods"); + BOOST_CHECK_EQUAL(err.mod_not_exists, token.apply_safe_mod(_bob, _bob)); + BOOST_CHECK_EQUAL(err.mod_not_exists, token.cancel_safe_mod(_bob, _bob)); + + BOOST_CHECK_EQUAL(success(), token.enable_safe(_bob, 0, _delay, _alice)); + create_mods(); + BOOST_CHECK_EQUAL(err.mod_not_exists, token.apply_safe_mod(_bob, _bob)); + BOOST_CHECK_EQUAL(err.mod_not_exists, token.cancel_safe_mod(_bob, _bob)); + + BOOST_TEST_MESSAGE("--- success on cancel existing"); + BOOST_CHECK_EQUAL(success(), token.cancel_safe_mod(_bob, unlock_mod)); + BOOST_CHECK_EQUAL(success(), token.cancel_safe_mod(_bob, modify_mod)); + BOOST_CHECK_EQUAL(success(), token.cancel_safe_mod(_bob, killit_mod)); + BOOST_CHECK(token.get_safe_mod(_bob, unlock_mod).is_null()); + BOOST_CHECK(token.get_safe_mod(_bob, modify_mod).is_null()); + BOOST_CHECK(token.get_safe_mod(_bob, killit_mod).is_null()); + produce_block(); + + BOOST_TEST_MESSAGE("--- success on trusted apply"); + create_mods(); + BOOST_CHECK_EQUAL(success(), token.apply_safe_mod2(_bob, unlock_mod, _alice)); + BOOST_CHECK_EQUAL(success(), token.apply_safe_mod2(_bob, modify_mod, _alice)); + BOOST_CHECK_EQUAL(err.still_locked, token.apply_safe_mod2(_bob, killit_mod, _alice)); + BOOST_CHECK_EQUAL(success(), token.apply_safe_mod2(_bob, killit_mod, _carol)); + BOOST_CHECK(token.get_safe_mod(_bob, unlock_mod).is_null()); + BOOST_CHECK(token.get_safe_mod(_bob, modify_mod).is_null()); + BOOST_CHECK(token.get_safe_mod(_bob, killit_mod).is_null()); + produce_block(); + BOOST_CHECK_EQUAL(success(), token.enable_safe(_bob, 0, _delay)); + + BOOST_TEST_MESSAGE("--- success on delayed apply"); + create_mods(); + const auto blocks = seconds_to_blocks(_delay); + produce_blocks(blocks - 1); + BOOST_CHECK_EQUAL(err.still_locked, token.apply_safe_mod(_bob, unlock_mod)); + BOOST_CHECK_EQUAL(err.still_locked, token.apply_safe_mod(_bob, modify_mod)); + BOOST_CHECK_EQUAL(err.still_locked, token.apply_safe_mod(_bob, killit_mod)); + produce_block(); + BOOST_CHECK_EQUAL(success(), token.apply_safe_mod(_bob, unlock_mod)); + BOOST_CHECK_EQUAL(success(), token.apply_safe_mod(_bob, modify_mod)); + BOOST_CHECK_EQUAL(success(), token.apply_safe_mod(_bob, killit_mod)); + BOOST_CHECK(token.get_safe_mod(_bob, unlock_mod).is_null()); + BOOST_CHECK(token.get_safe_mod(_bob, modify_mod).is_null()); + BOOST_CHECK(token.get_safe_mod(_bob, killit_mod).is_null()); + produce_block(); + + BOOST_TEST_MESSAGE("--- fail apply on disabled safe"); + BOOST_CHECK_EQUAL(success(), token.enable_safe(_bob, 0, _delay, _alice)); + produce_block(); + create_mods(); + BOOST_CHECK_EQUAL(success(), token.disable_safe2(_bob, _alice)); + const auto try_apply_all = [&]() { + BOOST_CHECK_EQUAL(err.disabled, token.apply_safe_mod(_bob, unlock_mod)); + BOOST_CHECK_EQUAL(err.disabled, token.apply_safe_mod(_bob, modify_mod)); + BOOST_CHECK_EQUAL(err.disabled, token.apply_safe_mod(_bob, killit_mod)); + BOOST_CHECK_EQUAL(err.disabled, token.apply_safe_mod2(_bob, unlock_mod, _alice)); + BOOST_CHECK_EQUAL(err.disabled, token.apply_safe_mod2(_bob, modify_mod, _alice)); + BOOST_CHECK_EQUAL(err.disabled, token.apply_safe_mod2(_bob, killit_mod, _alice)); + }; + try_apply_all(); + BOOST_TEST_MESSAGE("--- fail apply on disabled safe after delay"); + produce_blocks(blocks); + try_apply_all(); + BOOST_TEST_MESSAGE("--- success cancel on disabled safe"); + BOOST_CHECK_EQUAL(success(), token.cancel_safe_mod(_bob, unlock_mod)); + BOOST_CHECK_EQUAL(success(), token.cancel_safe_mod(_bob, modify_mod)); + BOOST_CHECK_EQUAL(success(), token.cancel_safe_mod(_bob, killit_mod)); + BOOST_CHECK(token.get_safe_mod(_bob, unlock_mod).is_null()); + BOOST_CHECK(token.get_safe_mod(_bob, modify_mod).is_null()); + BOOST_CHECK(token.get_safe_mod(_bob, killit_mod).is_null()); + + // change params requirements on apply already tested in "modify" +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE(trusted, cyber_token_safe_tester) try { + BOOST_TEST_MESSAGE("Instant actions with trusted account"); + init(); + const auto mod_id = N(mod); + + BOOST_CHECK_EQUAL(success(), token.enable_safe(_bob, 0, _delay, _alice)); + CHECK_MATCHING_OBJECT(token.get_safe(_bob), token.make_safe(0, _delay, _alice)); + BOOST_TEST_MESSAGE("--- fail if have mod_id"); + BOOST_CHECK_EQUAL(err.have_mod_id, token.unlock_safe2(_bob, 1, _alice, mod_id)); + BOOST_CHECK_EQUAL(err.have_mod_id, token.modify_safe2(_bob, _delay + 1, {}, _alice, mod_id)); + BOOST_CHECK_EQUAL(err.have_mod_id, token.disable_safe2(_bob, _alice, mod_id)); + BOOST_TEST_MESSAGE("--- fail if bad unlock"); + BOOST_CHECK_EQUAL(err.symbol_precision, token.unlock_safe2(_bob, asset(1, token.bad_sym()), _alice)); + BOOST_CHECK_EQUAL(err.unlock_lte0, token.unlock_safe2(_bob, 0, _alice)); + BOOST_CHECK_EQUAL(err.unlock_lte0, token.unlock_safe2(_bob, -1, _alice)); + BOOST_TEST_MESSAGE("--- fail if not changed modify"); + BOOST_CHECK_EQUAL(err.nothing_set, token.modify_safe2(_bob, {}, {}, _alice)); + BOOST_CHECK_EQUAL(err.same_trusted, token.modify_safe2(_bob, {}, _alice, _alice)); + BOOST_CHECK_EQUAL(err.same_delay, token.modify_safe2(_bob, _delay, {}, _alice)); + BOOST_CHECK_EQUAL(err.same_delay, token.modify_safe2(_bob, _delay, _alice, _alice)); + BOOST_TEST_MESSAGE("--- fail if bad delay"); + BOOST_CHECK_EQUAL(err.delay_lte0, token.modify_safe2(_bob, 0, {}, _alice)); + BOOST_CHECK_EQUAL(err.delay_lte0, token.modify_safe2(_bob, 0, _carol, _alice)); + BOOST_CHECK_EQUAL(err.delay_gt_max, token.modify_safe2(_bob, cfg::safe_max_delay+1, {}, _alice)); + BOOST_CHECK_EQUAL(err.delay_gt_max, token.modify_safe2(_bob, cfg::safe_max_delay+1, _carol, _alice)); + BOOST_TEST_MESSAGE("--- fail if bad trusted"); + BOOST_CHECK_EQUAL(err.trusted_eq_owner, token.modify_safe2(_bob, {}, _bob, _alice)); + BOOST_CHECK_EQUAL(err.trusted_eq_owner, token.modify_safe2(_bob, 60, _bob, _alice)); + BOOST_CHECK_EQUAL(err.trusted_not_exists, token.modify_safe2(_bob, {}, _nobody, _alice)); + BOOST_CHECK_EQUAL(err.trusted_not_exists, token.modify_safe2(_bob, 60, _nobody, _alice)); + BOOST_TEST_MESSAGE("--- fail if bad delay and trusted"); + BOOST_CHECK_EQUAL(err.delay_lte0, token.modify_safe2(_bob, 0, _bob, _alice)); + BOOST_CHECK_EQUAL(err.delay_lte0, token.modify_safe2(_bob, 0, _nobody, _alice)); + BOOST_CHECK_EQUAL(err.delay_gt_max, token.modify_safe2(_bob, cfg::safe_max_delay+1, _bob, _alice)); + BOOST_CHECK_EQUAL(err.delay_gt_max, token.modify_safe2(_bob, cfg::safe_max_delay+1, _nobody, _alice)); + + BOOST_TEST_MESSAGE("--- success on unlock"); + BOOST_CHECK_EQUAL(success(), token.unlock_safe2(_bob, 1, _alice)); + double unlocked = 1; + CHECK_MATCHING_OBJECT(token.get_safe(_bob), token.make_safe(unlocked, _delay, _alice)); + BOOST_CHECK_EQUAL(success(), token.unlock_safe2(_bob, 100500, _alice)); + unlocked += 100500; + CHECK_MATCHING_OBJECT(token.get_safe(_bob), token.make_safe(unlocked, _delay, _alice)); + BOOST_TEST_MESSAGE("--- success on modify"); + BOOST_CHECK_EQUAL(success(), token.modify_safe2(_bob, _delay*2, {}, _alice)); + CHECK_MATCHING_OBJECT(token.get_safe(_bob), token.make_safe(unlocked, _delay*2, _alice)); + BOOST_CHECK_EQUAL(success(), token.modify_safe2(_bob, cfg::safe_max_delay, {}, _alice)); + CHECK_MATCHING_OBJECT(token.get_safe(_bob), token.make_safe(unlocked, cfg::safe_max_delay, _alice)); + BOOST_CHECK_EQUAL(success(), token.modify_safe2(_bob, {}, _carol, _alice)); + BOOST_CHECK_EQUAL(err.empty_mod_id, token.modify_safe2(_bob, _delay, _alice, _alice)); + BOOST_CHECK_EQUAL(success(), token.modify_safe2(_bob, _delay, _alice, _carol)); + CHECK_MATCHING_OBJECT(token.get_safe(_bob), token.make_safe(unlocked, _delay, _alice)); + BOOST_TEST_MESSAGE("--- success on disable"); + BOOST_CHECK_EQUAL(success(), token.disable_safe2(_bob, _alice)); + BOOST_CHECK(token.get_safe(_bob).is_null()); + + BOOST_TEST_MESSAGE("--- remove trusted"); + produce_block(); + BOOST_CHECK_EQUAL(success(), token.enable_safe(_bob, 0, _delay, _alice)); + CHECK_MATCHING_OBJECT(token.get_safe(_bob), token.make_safe(0, _delay, _alice)); + BOOST_CHECK_EQUAL(success(), token.modify_safe2(_bob, {}, name{}, _alice)); + CHECK_MATCHING_OBJECT(token.get_safe(_bob), token.make_safe(0, _delay)); + BOOST_CHECK_EQUAL(err.empty_mod_id, token.modify_safe2(_bob, {}, _alice, _alice)); + BOOST_CHECK_EQUAL(err.empty_mod_id, token.unlock_safe2(_bob, 1, _alice)); + BOOST_CHECK_EQUAL(err.empty_mod_id, token.disable_safe2(_bob, _alice)); +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE(safe, cyber_token_safe_tester) try { + BOOST_TEST_MESSAGE("Limit transfers with enabled safe"); + init(_alice); // only issuer can retire so issue by alice + const auto mod_id = N(mod); + const double money = 100; + BOOST_CHECK_EQUAL(success(), token.open(_bob)); + BOOST_CHECK_EQUAL(success(), token.open(_carol)); + BOOST_CHECK_EQUAL(success(), token2.open(_bob)); + BOOST_CHECK_EQUAL(success(), token2.open(_carol)); + BOOST_CHECK_EQUAL(success(), token.transfer(_alice, _bob, money)); + BOOST_CHECK_EQUAL(success(), token2.transfer(_alice, _bob, money)); + BOOST_CHECK_EQUAL(success(), token.transfer(_alice, _carol, _total - 2*money)); + BOOST_CHECK_EQUAL(success(), token2.transfer(_alice, _carol, _total - 2*money)); + auto alices = money; + + const auto showBalances = [&]() { + BOOST_TEST_MESSAGE("------ alices safe: " << + fc::json::to_string(token.get_safe(_alice)) << " / " << fc::json::to_string(token2.get_safe(_alice))); + BOOST_TEST_MESSAGE("------ alices balance: " << + fc::json::to_string(token.get_account(_alice)) << " / " << fc::json::to_string(token2.get_account(_alice))); + BOOST_TEST_MESSAGE("------ bobs balance: " << + fc::json::to_string(token.get_account(_bob)) << " / " << fc::json::to_string(token2.get_account(_bob))); + }; + showBalances(); + + BOOST_TEST_MESSAGE("--- transfer without safe"); + const double half = money / 2; + alices -= half; + BOOST_CHECK_EQUAL(success(), token.transfer(_alice, _bob, half)); + BOOST_CHECK_EQUAL(success(), token2.transfer(_alice, _bob, half)); + showBalances(); + + BOOST_TEST_MESSAGE("--- issuer enables safe for " << token.name()); + BOOST_CHECK_EQUAL(success(), token.enable_safe(_alice, 0, _delay, _bob)); + + BOOST_TEST_MESSAGE("--- transfer out blocked by the safe"); + const double tenth = money / 10; + const auto satoshi = token.satoshi(); + BOOST_CHECK_EQUAL(err.balance_lock, token.transfer(_alice, _bob, tenth)); + BOOST_CHECK_EQUAL(err.balance_lock, token.transfer(_alice, _bob, satoshi)); + BOOST_CHECK_EQUAL(err.balance_lock, token.retire(_alice, tenth)); + BOOST_CHECK_EQUAL(err.balance_lock, token.retire(_alice, satoshi)); + BOOST_CHECK_EQUAL(success(), token2.transfer(_alice, _bob, tenth)); + showBalances(); + + BOOST_TEST_MESSAGE("--- transfer in succeed"); + alices += tenth; + BOOST_CHECK_EQUAL(success(), token.transfer(_bob, _alice, tenth)); + BOOST_CHECK_EQUAL(success(), token2.transfer(_bob, _alice, tenth)); + showBalances(); + + BOOST_TEST_MESSAGE("--- prepare unlocked tokens"); + BOOST_CHECK_EQUAL(success(), token.unlock_safe2(_alice, tenth, _bob)); + BOOST_CHECK_EQUAL(success(), token2.enable_safe(_alice, tenth, _delay, _bob)); + produce_block(); + + showBalances(); + BOOST_TEST_MESSAGE("--- success when transfer unlocked"); + alices -= tenth; + BOOST_CHECK_EQUAL(success(), token.transfer(_alice, _bob, tenth)); + const auto one = tenth/10; + BOOST_CHECK_EQUAL(success(), token2.transfer(_alice, _bob, one)); + BOOST_CHECK_EQUAL(success(), token2.transfer(_alice, _bob, tenth - one)); + showBalances(); + BOOST_TEST_MESSAGE("--- fail when out of unlocked"); + BOOST_CHECK_EQUAL(err.balance_lock, token.transfer(_alice, _bob, satoshi)); + BOOST_CHECK_EQUAL(err.balance_lock, token2.transfer(_alice, _bob, satoshi)); + BOOST_CHECK_EQUAL(err.balance_lock, token.retire(_alice, satoshi)); + BOOST_CHECK_EQUAL(err.balance_lock, token2.retire(_alice, satoshi)); + + BOOST_TEST_MESSAGE("--- fail when out of balance with enough unlocked"); + BOOST_CHECK_EQUAL(success(), token.unlock_safe2(_alice, money*100, _bob)); + BOOST_CHECK_EQUAL(err.balance_over, token.transfer(_alice, _bob, money)); + BOOST_CHECK_EQUAL(err.balance_over, token.transfer(_alice, _bob, alices + satoshi)); + BOOST_CHECK_EQUAL(err.balance_over, token.retire(_alice, alices + satoshi)); + BOOST_CHECK_EQUAL(success(), token.transfer(_alice, _bob, alices)); + showBalances(); + produce_block(); + + BOOST_TEST_MESSAGE("--- unlock and then lock"); + BOOST_CHECK_EQUAL(success(), token.transfer(_bob, _alice, half)); + BOOST_CHECK_EQUAL(success(), token.lock_safe(_alice)); + BOOST_CHECK_EQUAL(success(), token.unlock_safe2(_alice, half, _bob)); + BOOST_CHECK_EQUAL(success(), token.transfer(_alice, _bob, tenth)); + BOOST_CHECK_EQUAL(success(), token.lock_safe(_alice, tenth)); + BOOST_CHECK_EQUAL(err.balance_lock, token.transfer(_alice, _bob, half - tenth)); + const auto max = half - 2 * tenth; + BOOST_CHECK_EQUAL(err.balance_lock, token.transfer(_alice, _bob, max + satoshi)); + BOOST_CHECK_EQUAL(err.balance_lock, token.retire(_alice, max + satoshi)); + BOOST_CHECK_EQUAL(success(), token.transfer(_alice, _bob, max)); + showBalances(); +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE(global_lock, cyber_token_safe_tester) try { + BOOST_TEST_MESSAGE("Globally locked balance"); + init(_alice); // only issuer can retire so issue by alice + BOOST_TEST_MESSAGE("--- issue tokens"); + const double money = 100; + BOOST_CHECK_EQUAL(success(), token.transfer(_alice, _bob, money)); + BOOST_CHECK_EQUAL(success(), token2.transfer(_alice, _bob, money)); + BOOST_CHECK_EQUAL(success(), token.transfer(_alice, _carol, _total - 2*money)); + BOOST_CHECK_EQUAL(success(), token2.transfer(_alice, _carol, _total - 2*money)); + BOOST_TEST_MESSAGE("--- enable safe for " << token.name() << "; no safe for " << token2.name()); + BOOST_CHECK_EQUAL(success(), token.enable_safe(_alice, money / 2, _delay, _bob)); + BOOST_TEST_MESSAGE("--- transfer/retire allowed"); + const double tenth = money / 10; + const auto satoshi = token.satoshi(); + BOOST_CHECK_EQUAL(success(), token.transfer(_alice, _bob, tenth)); + BOOST_CHECK_EQUAL(success(), token2.transfer(_alice, _bob, tenth)); + BOOST_CHECK_EQUAL(success(), token.retire(_alice, tenth)); + BOOST_CHECK_EQUAL(success(), token2.retire(_alice, tenth)); + + BOOST_TEST_MESSAGE("--- global lock fails if bad period"); + BOOST_CHECK_EQUAL(err.period_le0, token.global_lock(_alice, 0)); + BOOST_CHECK_EQUAL(err.period_gt_max, token.global_lock(_alice, cfg::safe_max_delay + 1)); + BOOST_TEST_MESSAGE("--- succeeds if increase lock period"); + auto locked = 30; + BOOST_CHECK_EQUAL(success(), token.global_lock(_alice, 1)); + BOOST_CHECK_EQUAL(success(), token.global_lock(_alice, 3)); + BOOST_CHECK_EQUAL(success(), token.global_lock(_alice, locked)); + produce_block(); locked -= 3; + BOOST_TEST_MESSAGE("--- fails if decrease lock period"); + BOOST_CHECK_EQUAL(err.period_le_cur, token.global_lock(_alice, 1)); + BOOST_CHECK_EQUAL(err.period_le_cur, token.global_lock(_alice, locked)); + locked++; + BOOST_CHECK_EQUAL(success(), token.global_lock(_alice, locked)); + locked = _delay*2; + BOOST_CHECK_EQUAL(success(), token.global_lock(_alice, locked)); + BOOST_TEST_MESSAGE("--- transfers disallowed on both " << token.name() << " & " << token2.name()); + BOOST_CHECK_EQUAL(err.global_lock, token.transfer(_alice, _bob, tenth)); + BOOST_CHECK_EQUAL(err.global_lock, token2.transfer(_alice, _bob, tenth)); + BOOST_CHECK_EQUAL(err.global_lock, token.retire(_alice, tenth)); + BOOST_CHECK_EQUAL(err.global_lock, token2.retire(_alice, tenth)); + BOOST_TEST_MESSAGE("--- delayed mods disallowed when locked globally"); + BOOST_CHECK_EQUAL(success(), token.unlock_safe(_alice, N(unlock), tenth)); + BOOST_CHECK_EQUAL(success(), token.modify_safe(_alice, N(mod), _delay/2)); + BOOST_CHECK_EQUAL(success(), token.disable_safe(_alice, N(off))); + BOOST_CHECK_EQUAL(success(), token.unlock_safe(_alice, N(u2), 1)); + BOOST_CHECK_EQUAL(success(), token.modify_safe(_alice, N(m2), _delay*2)); + auto blocks = seconds_to_blocks(_delay); + produce_blocks(blocks); + locked -= blocks*3; + BOOST_CHECK_EQUAL(err.global_lock, token.transfer(_alice, _bob, tenth)); + BOOST_CHECK_EQUAL(err.global_lock, token2.transfer(_alice, _bob, tenth)); + BOOST_CHECK_EQUAL(err.global_lock, token.retire(_alice, tenth)); + BOOST_CHECK_EQUAL(err.global_lock, token2.retire(_alice, tenth)); + BOOST_CHECK_EQUAL(err.mod_global_lock, token.apply_safe_mod(_alice, N(unlock))); + BOOST_CHECK_EQUAL(err.mod_global_lock, token.apply_safe_mod(_alice, N(mod))); + BOOST_CHECK_EQUAL(err.mod_global_lock, token.apply_safe_mod(_alice, N(off))); + BOOST_TEST_MESSAGE("--- but still allowed with trusted"); + BOOST_CHECK_EQUAL(success(), token.apply_safe_mod2(_alice, N(unlock), _bob)); + BOOST_CHECK_EQUAL(success(), token.apply_safe_mod2(_alice, N(mod), _bob)); + BOOST_CHECK_EQUAL(success(), token.unlock_safe2(_alice, tenth, _bob)); + BOOST_CHECK_EQUAL(success(), token.modify_safe2(_alice, _delay, {}, _bob)); + BOOST_TEST_MESSAGE("--- wait 1 block before global unlock and ensure it's still locked"); + blocks = seconds_to_blocks(locked); + produce_blocks(blocks - 1); + BOOST_CHECK_EQUAL(err.global_lock, token.transfer(_alice, _bob, tenth)); + BOOST_CHECK_EQUAL(err.global_lock, token2.transfer(_alice, _bob, tenth)); + BOOST_CHECK_EQUAL(err.global_lock, token.retire(_alice, tenth)); + BOOST_CHECK_EQUAL(err.global_lock, token2.retire(_alice, tenth)); + BOOST_CHECK_EQUAL(err.mod_global_lock, token.apply_safe_mod(_alice, N(u2))); + BOOST_CHECK_EQUAL(err.mod_global_lock, token.apply_safe_mod(_alice, N(m2))); + BOOST_CHECK_EQUAL(err.mod_global_lock, token.apply_safe_mod(_alice, N(off))); + BOOST_TEST_MESSAGE("--- delayed mods and transfers allowed after global unlock"); + produce_block(); + BOOST_CHECK_EQUAL(success(), token.transfer(_alice, _bob, tenth)); + BOOST_CHECK_EQUAL(success(), token2.transfer(_alice, _bob, tenth)); + BOOST_CHECK_EQUAL(success(), token.retire(_alice, tenth)); + BOOST_CHECK_EQUAL(success(), token2.retire(_alice, tenth)); + BOOST_CHECK_EQUAL(success(), token.apply_safe_mod(_alice, N(u2))); + BOOST_CHECK_EQUAL(success(), token.apply_safe_mod(_alice, N(m2))); + BOOST_CHECK_EQUAL(success(), token.apply_safe_mod(_alice, N(off))); +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE(unlock_overflow, cyber_token_safe_tester) try { + BOOST_TEST_MESSAGE("Unlocked overflow"); + init(); + BOOST_TEST_MESSAGE("--- enable safes"); + const asset max{asset::max_amount, token._symbol}; + BOOST_CHECK_EQUAL(success(), token.enable_safe(_bob, 0, _delay, _alice)); + BOOST_CHECK_EQUAL(success(), token.enable_safe(_alice, max, _delay, _bob)); + BOOST_TEST_MESSAGE("--- bob unlocks until limit"); + const asset qtr{(asset::max_amount + 1) / 4, token._symbol}; + BOOST_CHECK_EQUAL(success(), token.unlock_safe2(_bob, qtr, _alice)); + produce_block(); + BOOST_CHECK_EQUAL(success(), token.unlock_safe2(_bob, qtr, _alice)); + produce_block(); + BOOST_CHECK_EQUAL(success(), token.unlock_safe2(_bob, qtr, _alice)); + produce_block(); + BOOST_CHECK_EQUAL(err.unlocked_over, token.unlock_safe2(_bob, qtr, _alice)); + const auto satoshi = token.satoshi_asset(); + BOOST_CHECK_EQUAL(success(), token.unlock_safe2(_bob, qtr - satoshi, _alice)); + + BOOST_TEST_MESSAGE("--- alice fails to unlock because already unlocked max"); + BOOST_CHECK_EQUAL(err.unlocked_over, token.unlock_safe2(_alice, max, _bob)); + BOOST_CHECK_EQUAL(err.unlocked_over, token.unlock_safe2(_alice, qtr, _bob)); + BOOST_CHECK_EQUAL(err.unlocked_over, token.unlock_safe2(_alice, 1, _bob)); + BOOST_CHECK_EQUAL(err.unlocked_over, token.unlock_safe2(_alice, satoshi, _bob)); + BOOST_TEST_MESSAGE("------ delayed fails too"); + BOOST_CHECK_EQUAL(success(), token.unlock_safe(_alice, N(max), max)); + BOOST_CHECK_EQUAL(success(), token.unlock_safe(_alice, N(one), 1)); + BOOST_CHECK_EQUAL(success(), token.unlock_safe(_alice, N(sat), satoshi)); + const auto blocks = seconds_to_blocks(_delay); + produce_blocks(blocks); + BOOST_CHECK_EQUAL(err.unlocked_over, token.apply_safe_mod(_alice, N(max))); + BOOST_CHECK_EQUAL(err.unlocked_over, token.apply_safe_mod(_alice, N(one))); + BOOST_CHECK_EQUAL(err.unlocked_over, token.apply_safe_mod(_alice, N(sat))); + BOOST_TEST_MESSAGE("--- success if lock some amount"); + BOOST_CHECK_EQUAL(success(), token.lock_safe(_alice, 1)); + BOOST_CHECK_EQUAL(success(), token.apply_safe_mod(_alice, N(one))); + BOOST_CHECK_EQUAL(err.unlocked_over, token.apply_safe_mod(_alice, N(sat))); +} FC_LOG_AND_RETHROW() + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/cyber.token_test_api.hpp b/tests/cyber.token_test_api.hpp index 1bb9ceb3a..e4514a654 100644 --- a/tests/cyber.token_test_api.hpp +++ b/tests/cyber.token_test_api.hpp @@ -13,9 +13,11 @@ struct recipient { struct cyber_token_api: base_contract_api { cyber_token_api(golos_tester* tester, name code, symbol sym) : base_contract_api(tester, code) - , _symbol(sym) {} + , _symbol(sym) + , _symbol_code(sym.to_symbol_code()) {} symbol _symbol; + symbol_code _symbol_code; //// token actions action_result create(account_name issuer, asset maximum_supply) { @@ -25,7 +27,10 @@ struct cyber_token_api: base_contract_api { ); } - action_result issue(account_name issuer, account_name to, asset quantity, string memo) { + action_result issue(account_name issuer, account_name to, double quantity, string memo = "") { + return issue(issuer, to, make_asset(quantity), memo); + } + action_result issue(account_name issuer, account_name to, asset quantity, string memo = "") { return push(N(issue), issuer, args() ("to", to) ("quantity", quantity) @@ -33,6 +38,10 @@ struct cyber_token_api: base_contract_api { ); } + action_result open(account_name owner, account_name payer = {}) { + if (payer.empty()) payer = owner; + return open(owner, _symbol, payer); + } action_result open(account_name owner, symbol symbol, account_name payer) { return push(N(open), owner, args() ("owner", owner) @@ -40,7 +49,7 @@ struct cyber_token_api: base_contract_api { ("ram_payer", payer) ); } - + action_result close(account_name owner, symbol symbol) { return push(N(close), owner, args() ("owner", owner) @@ -48,6 +57,9 @@ struct cyber_token_api: base_contract_api { ); } + action_result transfer(account_name from, account_name to, double quantity, string memo = "") { + return transfer(from, to, make_asset(quantity), memo); + } action_result transfer(account_name from, account_name to, asset quantity, string memo = "") { return push(N(transfer), from, args() ("from", from) @@ -58,12 +70,129 @@ struct cyber_token_api: base_contract_api { } action_result bulk_transfer( account_name from, std::vector recipients ) { - return push( N(bulktransfer), from, args() + return push( N(bulktransfer), from, args() ( "from", from) ( "recipients", recipients) - ); + ); + } + + action_result retire(name signer, double quantity, string memo = "") { + return retire(signer, make_asset(quantity), memo); + } + action_result retire(name signer, asset quantity, string memo = "") { + return push(N(retire), signer, args() + ("quantity", quantity) + ("memo", memo) + ); + } + + // safe + action_result enable_safe(name owner, asset unlock, uint32_t delay, name trusted = {}) { + return push(N(enablesafe), owner, args() + ("owner", owner) + ("unlock", unlock) + ("delay", delay) + ("trusted", trusted) + ); + } + + action_result _disable_safe(name owner, name mod_id, signers_t signers) { + return push_msig(N(disablesafe), signers, args() + ("owner", owner) + ("sym_code", _symbol_code) + ("mod_id", mod_id) + ); } + action_result _unlock_safe(name owner, asset unlock, name mod_id, signers_t signers) { + return push_msig(N(unlocksafe), signers, args() + ("owner", owner) + ("unlock", unlock) + ("mod_id", mod_id) + ); + } + + action_result lock_safe(name owner, asset lock) { + return push(N(locksafe), owner, args() + ("owner", owner) + ("lock", lock) + ); + } + + action_result _modify_safe( + name owner, optional delay, optional trusted, name mod_id, signers_t signers + ) { + return push_msig(N(modifysafe), signers, args() + ("owner", owner) + ("sym_code", _symbol_code) + ("mod_id", mod_id) + ("delay", delay) + ("trusted", trusted) + ); + } + + action_result _apply_safe_mod(name owner, name mod_id, signers_t signers) { + return push_msig(N(applysafemod), signers, args() + ("owner", owner) + ("mod_id", mod_id) + ); + } + + action_result cancel_safe_mod(name owner, name mod_id) { + return push(N(cancelsafemod), owner, args() + ("owner", owner) + ("mod_id", mod_id) + ); + } + + action_result global_lock(name owner, uint32_t period) { + return push(N(globallock), owner, args() + ("owner", owner) + ("period", period) + ); + } + + // safe shortcuts + action_result enable_safe(name owner, double unlock, uint32_t delay, name trusted = {}) { + return enable_safe(owner, make_asset(unlock), delay, trusted); + } + action_result disable_safe(name owner, name mod_id) { + return _disable_safe(owner, mod_id, {owner}); + } + action_result disable_safe2(name owner, name signer, name force_mod_id = {}) { + return _disable_safe(owner, force_mod_id, {owner, signer}); + } + action_result unlock_safe(name owner, name mod_id, double unlock) { + return unlock_safe(owner, mod_id, make_asset(unlock)); + } + action_result unlock_safe(name owner, name mod_id, asset unlock) { + return _unlock_safe(owner, unlock, mod_id, {owner}); + } + action_result unlock_safe2(name owner, double unlock, name signer, name force_mod_id = {}) { + return unlock_safe2(owner, make_asset(unlock), signer, force_mod_id); + } + action_result unlock_safe2(name owner, asset unlock, name signer, name force_mod_id = {}) { + return _unlock_safe(owner, unlock, force_mod_id, {owner, signer}); + } + action_result lock_safe(name owner, double lock = 0) { + return lock_safe(owner, make_asset(lock)); + } + action_result modify_safe(name owner, name mod_id, optional delay = {}, optional trusted = {}) { + return _modify_safe(owner, delay, trusted, mod_id, {owner}); + } + action_result modify_safe2( + name owner, optional delay, optional trusted, name signer, name force_mod_id = {} + ) { + return _modify_safe(owner, delay, trusted, force_mod_id, {owner, signer}); + } + action_result apply_safe_mod(name owner, name mod_id) { + return _apply_safe_mod(owner, mod_id, {owner}); + } + action_result apply_safe_mod2(name owner, name mod_id, name signer) { + return _apply_safe_mod(owner, mod_id, {owner, signer}); + } + + //// token tables variant get_stats() { auto sname = _symbol.to_symbol_code().value; @@ -91,9 +220,47 @@ struct cyber_token_api: base_contract_api { return _tester->get_all_chaindb_rows(_code, user, N(accounts), false); } + variant get_safe(name acc) { + auto v = get_struct(acc, N(safe), _symbol_code.value, ""); + if (v.is_object() && v["unlocked"].is_object()) { + auto o = mvo(v); + return o("unlocked", o["unlocked"].as()); + } + return v; + } + + variant get_safe_mod(name acc, name mod_id) { + return get_struct(acc, N(safemod), mod_id.value, ""); + } + + variant get_global_lock(name acc) { + return get_singleton(acc, N(lock), ""); + } + + // generated objects + variant make_safe(double unlocked, uint32_t delay, name trusted = {}) { + return mvo()("unlocked", make_asset(unlocked).to_string())("delay", delay)("trusted", trusted); + } + variant make_safe_mod(name id, double unlock, optional delay = {}, optional trusted = {}) { + auto mod = mvo()("id", id)("sym_code", _symbol_code)("unlock", to_shares(unlock)); + if (delay) mod = mod("delay", *delay); + if (trusted) mod = mod("trusted", *trusted); + // date + return mod; + } + //// helpers + string name() const { + return _symbol.name(); + } + double satoshi() const { + return 1. / _symbol.precision(); + } + asset satoshi_asset() { + return make_asset(satoshi()); + } int64_t to_shares(double x) const { - return x * _symbol.precision(); + return x * _symbol.precision() + .5 * (x < 0 ? 0 : 1); } asset make_asset(double x = 0) const { return asset(to_shares(x), _symbol); @@ -104,6 +271,9 @@ struct cyber_token_api: base_contract_api { string asset_str(double x = 0) { return make_asset(x).to_string(); } + symbol bad_sym(int diff = 1) const { + return symbol{static_cast(_symbol.decimals() + diff), name().c_str()}; + } }; diff --git a/tests/golos_tester.cpp b/tests/golos_tester.cpp index 0498a0d19..6d061028d 100644 --- a/tests/golos_tester.cpp +++ b/tests/golos_tester.cpp @@ -79,7 +79,8 @@ base_tester::action_result golos_tester::push_action_msig_tx( action_name name, vector perms, vector signers, - const variant_object& data + const variant_object& data, + bool produce/*=true*/ ) { auto& abi = _abis[code]; action act; @@ -96,10 +97,10 @@ base_tester::action_result golos_tester::push_action_msig_tx( for (const auto& a : signers) { tx.sign(get_private_key(a, "active"), control->get_chain_id()); } - return push_tx(std::move(tx)); + return push_tx(std::move(tx), produce); } -base_tester::action_result golos_tester::push_tx(signed_transaction&& tx) { +base_tester::action_result golos_tester::push_tx(signed_transaction&& tx, bool produce_and_check) { try { push_transaction(tx); } catch (const fc::exception& ex) { @@ -107,8 +108,10 @@ base_tester::action_result golos_tester::push_tx(signed_transaction&& tx) { return error(ex.top_message()); // top_message() is assumed by many tests; otherwise they fail //return error(ex.to_detail_string()); } - produce_block(); - BOOST_REQUIRE_EQUAL(true, chain_has_transaction(tx.id())); + if (produce_and_check) { + produce_block(); + BOOST_REQUIRE_EQUAL(true, chain_has_transaction(tx.id())); + } return success(); } diff --git a/tests/golos_tester.hpp b/tests/golos_tester.hpp index 0cfb4a40e..a07d905a0 100644 --- a/tests/golos_tester.hpp +++ b/tests/golos_tester.hpp @@ -64,8 +64,8 @@ class golos_tester : public tester { action_result push_and_check_action(account_name, action_name, account_name, const variant_object&); action_result push_action(account_name code, action_name name, account_name signer, const variant_object& data); action_result push_action_msig_tx(account_name code, action_name name, - std::vector perms, std::vector signers, const variant_object& data); - action_result push_tx(signed_transaction&& tx); + std::vector perms, std::vector signers, const variant_object& data, bool produce=true); + action_result push_tx(signed_transaction&& tx, bool produce_and_check=true); void delegate_authority(account_name from, std::vector to, account_name code, action_name type, permission_name req, permission_name parent = N(active), permission_name prov = config::eosio_code_name); diff --git a/tests/test_api_helper.hpp b/tests/test_api_helper.hpp index fe3526abb..123152171 100644 --- a/tests/test_api_helper.hpp +++ b/tests/test_api_helper.hpp @@ -19,10 +19,11 @@ inline account_name user_name(size_t n) { ret_str += std::string(1, 'a' + n % q); n /= q; } - return string_to_name(ret_str.c_str()); -}; + return string_to_name(ret_str.c_str()); +}; struct base_contract_api { + using signers_t = std::vector; protected: uint32_t billed_cpu_time_us = base_tester::DEFAULT_BILLED_CPU_TIME_US; uint64_t billed_ram_bytes = base_tester::DEFAULT_BILLED_RAM_BYTES; @@ -32,7 +33,15 @@ struct base_contract_api { base_contract_api(golos_tester* tester, name code): _tester(tester), _code(code) {} - base_tester::action_result push_msig(action_name name, std::vector perms, std::vector signers, + base_tester::action_result push_msig(action_name name, signers_t signers, const variant_object& data) { + std::vector perms{signers.size()}; + std::transform(signers.begin(), signers.end(), perms.begin(), [](const auto& s){ + return permission_level{s, config::active_name}; + }); + return _tester->push_action_msig_tx(_code, name, perms, signers, data, false); // NOTE: doesn't produce block + } + + base_tester::action_result push_msig(action_name name, std::vector perms, signers_t signers, const variant_object& data ) { return _tester->push_action_msig_tx(_code, name, perms, signers, data); @@ -42,6 +51,10 @@ struct base_contract_api { return _tester->get_chaindb_struct(_code, scope, tbl, id, name); } + variant get_singleton(uint64_t scope, name tbl, const string& name) const { + return _tester->get_chaindb_singleton(_code, scope, tbl, name); + } + virtual mvo args() { return mvo(); } From 1402704629737779e17ecf91746bc4538174cc27 Mon Sep 17 00:00:00 2001 From: zxcat <550974+zxcat@users.noreply.github.com> Date: Mon, 16 Mar 2020 16:11:53 +0300 Subject: [PATCH 11/21] Use binary extension for safe #321 --- .../include/cyber.token/cyber.token.hpp | 24 ++-- cyber.token/src/cyber.token.cpp | 111 ++++++++++-------- tests/cyber.token_safe_tests.cpp | 64 +++++++--- tests/cyber.token_test_api.hpp | 14 +-- 4 files changed, 125 insertions(+), 88 deletions(-) diff --git a/cyber.token/include/cyber.token/cyber.token.hpp b/cyber.token/include/cyber.token/cyber.token.hpp index d44c50a90..edecf667a 100644 --- a/cyber.token/include/cyber.token/cyber.token.hpp +++ b/cyber.token/include/cyber.token/cyber.token.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -240,10 +241,18 @@ namespace eosio { } private: + struct safe_t { + int64_t unlocked; //!< Amount of unlocked tokens in the safe, share_type + uint32_t delay; //!< Delay in seconds of unlock/modify period + name trusted; //!< Trusted account, empty name means no trusted account set + }; + struct account { asset balance; asset payments; + eosio::binary_extension safe; + uint64_t primary_key()const { return balance.symbol.code().raw(); } }; @@ -261,19 +270,6 @@ namespace eosio { asset payments; }; - /** - \brief DB record containing information about safe for tokens of a certain symbol on the account balance - \ingroup token_tables - */ - // DOCS_TABLE: safe - struct safe { - asset unlocked; //!< Number of unlocked tokens in the safe - name trusted; //!< Trusted account, disabled if empty - uint32_t delay; //!< Delay in seconds of unlock/modify period - - uint64_t primary_key() const { return unlocked.symbol.code().raw(); } - }; - /** \brief DB record containing information about a delayed safe modify \ingroup token_tables @@ -310,8 +306,6 @@ namespace eosio { using stats [[using eosio: order("supply._sym"), scope_type("symbol_code")]] = eosio::multi_index<"stat"_n, currency_stats>; - using safe_tbl [[eosio::order("unlocked._sym","asc")]] = - eosio::multi_index<"safe"_n, safe>; using safemod_sym_idx [[using eosio: order("sym_code","asc"), order("id","asc")]] = eosio::indexed_by<"bysymbolcode"_n, eosio::const_mem_fun>; using safemod_tbl [[eosio::order("id","asc")]] = diff --git a/cyber.token/src/cyber.token.cpp b/cyber.token/src/cyber.token.cpp index fca18fb9f..30d15121a 100644 --- a/cyber.token/src/cyber.token.cpp +++ b/cyber.token/src/cyber.token.cpp @@ -165,12 +165,12 @@ void token::sub_balance( name owner, asset value ) { }); check(!is_locked(_self, owner), "balance locked in safe"); - safe_tbl safes(_self, owner.value); - const auto& safe = safes.find(value.symbol.code().raw()); - if (safe != safes.end()) { - safes.modify(safe, owner, [&](auto& s) { - s.unlocked -= value; - check(s.unlocked.amount >= 0, "overdrawn safe unlocked balance"); + if (from.safe.has_value()) { + from_acnts.modify(from, owner, [&](auto& a) { + auto safe = a.safe.value(); + safe.unlocked -= value.amount; + check(safe.unlocked >= 0, "overdrawn safe unlocked balance"); + a.safe.emplace(safe); }); } } @@ -240,6 +240,7 @@ void token::close( name owner, const symbol& symbol ) eosio::check( it != acnts.end(), "Balance row already deleted or never existed. Action won't have any effect." ); eosio::check( it->balance.amount == 0, "Cannot close because the balance is not zero." ); eosio::check( it->payments.amount == 0, "Cannot close because account has payments." ); + eosio::check( !it->safe.has_value(), "Cannot close because safe enabled." ); acnts.erase( it ); } @@ -316,49 +317,51 @@ void token::enablesafe(name owner, asset unlock, uint32_t delay, name trusted) { validate_symbol(_self, unlock); } - safe_tbl safes(_self, owner.value); + accounts tbl(_self, owner.value); const auto scode = unlock.symbol.code(); - auto safe = safes.find(scode.raw()); - check(safe == safes.end(), "Safe already enabled"); + const auto& acc = tbl.get(scode.raw(), "no token account object found"); + check(!acc.safe.has_value(), "safe already enabled"); // Do not allow to have delayed changes when enable the safe, they came from the previously enabled safe // and should be cancelled to make clean safe setup. safemod_tbl mods(_self, owner.value); auto idx = mods.get_index<"bysymbolcode"_n>(); auto itr = idx.lower_bound(scode); - check(itr == idx.end() || itr->sym_code != scode, "Can't enable safe with existing delayed mods"); + check(itr == idx.end() || itr->sym_code != scode, "can't enable safe with existing delayed mods"); - safes.emplace(owner, [&](auto& s) { - s.unlocked = unlock; - s.delay = delay; - s.trusted = trusted; + tbl.modify(acc, owner, [&](auto& a) { + a.safe.emplace(safe_t{unlock.amount, delay, trusted}); }); } template -void instant_safe_change(Tbl& safes, S& safe, +void instant_safe_change(Tbl& tbl, S& acc, name owner, int64_t unlock, optional delay, optional trusted, bool ensure_change ) { if (delay && *delay == 0) { check(!unlock && !trusted, "SYS: incorrect disabling safe mod"); - safes.erase(safe); + tbl.modify(acc, owner, [](auto& a){ + a.safe.reset(); + }); } else { bool changed = !ensure_change; - safes.modify(safe, owner, [&](auto& s) { - if (unlock) { - s.unlocked.amount += unlock; - check(s.unlocked.is_amount_within_range(), "unlocked overflow"); - changed = true; - } - if (delay && *delay != s.delay) { - s.delay = *delay; - changed = true; - } - if (trusted && *trusted != s.trusted) { - s.trusted = *trusted; - changed = true; - } - check(changed, "Change has no effect and can be cancelled"); + auto safe = acc.safe.value(); + if (unlock) { + safe.unlocked += unlock; + check(safe.unlocked >= 0 && safe.unlocked <= asset::max_amount, "unlocked overflow"); + changed = true; + } + if (delay && *delay != safe.delay) { + safe.delay = *delay; + changed = true; + } + if (trusted && *trusted != safe.trusted) { + safe.trusted = *trusted; + changed = true; + } + check(changed, "change has no effect and can be cancelled"); + tbl.modify(acc, owner, [&](auto& a) { + a.safe.emplace(safe); }); } } @@ -384,20 +387,22 @@ void token::delay_safe_change( } const auto scode = unlock.symbol.code(); - safe_tbl safes(_self, owner.value); - const auto& safe = safes.get(scode.raw(), "Safe disabled"); + accounts tbl(_self, owner.value); + const auto& acc = tbl.get(scode.raw(), "no token account object found"); + check(acc.safe.has_value(), "safe disabled"); + auto safe = acc.safe.value(); const bool have_id = mod_id != name(); const auto trusted_acc = safe.trusted; if (trusted_acc != name() && has_auth(trusted_acc)) { check(!have_id, "mod_id must be empty for trusted action"); - check(!delay || *delay != safe.delay, "Can't set same delay"); - check(!trusted || *trusted != trusted_acc, "Can't set same trusted"); - instant_safe_change(safes, safe, owner, unlock.amount, delay, trusted, false); + check(!delay || *delay != safe.delay, "can't set same delay"); + check(!trusted || *trusted != trusted_acc, "can't set same trusted"); + instant_safe_change(tbl, acc, owner, unlock.amount, delay, trusted, false); } else { check(have_id, "mod_id must not be empty"); safemod_tbl mods(_self, owner.value); - check(mods.find(mod_id.value) == mods.end(), "Safe mod with the same id is already exists"); + check(mods.find(mod_id.value) == mods.end(), "safe mod with the same id is already exists"); mods.emplace(owner, [&](auto& d) { d.id = mod_id; d.sym_code = scode; @@ -426,14 +431,18 @@ void token::locksafe(name owner, asset lock) { validate_symbol(_self, lock); // checked within "<= unlocked", but have confusing message, so check here const auto scode = lock.symbol.code(); - safe_tbl safes(_self, owner.value); - const auto& safe = safes.get(scode.raw(), "Safe disabled"); - check(safe.unlocked.amount > 0, "nothing to lock"); - check(lock <= safe.unlocked, "lock must be <= unlocked"); + accounts tbl(_self, owner.value); + const auto& acc = tbl.get(scode.raw(), "no token account object found"); + check(acc.safe.has_value(), "safe disabled"); + auto safe = acc.safe.value(); + check(safe.unlocked > 0, "nothing to lock"); + check(safe.unlocked >= lock.amount, "lock must be <= unlocked"); bool lock_all = lock.amount == 0; - safes.modify(safe, owner, [&](auto& s) { - s.unlocked -= lock_all ? s.unlocked : lock; + tbl.modify(acc, owner, [&](auto& a) { + auto safe = a.safe.value(); + safe.unlocked -= lock_all ? safe.unlocked : lock.amount; + a.safe.emplace(safe); }); } @@ -448,24 +457,26 @@ void token::modifysafe( void token::applysafemod(name owner, name mod_id) { require_auth(owner); safemod_tbl mods(_self, owner.value); - const auto& mod = mods.get(mod_id.value, "Safe mod not found"); + const auto& mod = mods.get(mod_id.value, "safe mod not found"); - safe_tbl safes(_self, owner.value); - const auto& safe = safes.get(mod.sym_code.raw(), "Safe disabled"); + accounts tbl(_self, owner.value); + const auto& acc = tbl.get(mod.sym_code.raw(), "no token account object found"); + check(acc.safe.has_value(), "safe disabled"); + const auto& safe = acc.safe.value(); bool trusted_apply = safe.trusted != name() && has_auth(safe.trusted); if (!trusted_apply) { - check(mod.date <= eosio::current_time_point(), "Safe change is time locked"); - check(!is_locked(_self, owner), "Safe locked globally"); + check(mod.date <= eosio::current_time_point(), "safe change is time locked"); + check(!is_locked(_self, owner), "safe locked globally"); } - instant_safe_change(safes, safe, owner, mod.unlock, mod.delay, mod.trusted, true); + instant_safe_change(tbl, acc, owner, mod.unlock, mod.delay, mod.trusted, true); mods.erase(mod); } void token::cancelsafemod(name owner, name mod_id) { require_auth(owner); safemod_tbl mods(_self, owner.value); - const auto& mod = mods.get(mod_id.value, "Safe mod not found"); + const auto& mod = mods.get(mod_id.value, "safe mod not found"); mods.erase(mod); } diff --git a/tests/cyber.token_safe_tests.cpp b/tests/cyber.token_safe_tests.cpp index c0e54bb14..07e8a22d8 100644 --- a/tests/cyber.token_safe_tests.cpp +++ b/tests/cyber.token_safe_tests.cpp @@ -37,14 +37,23 @@ class cyber_token_safe_tester : public golos_tester { const double _total = 400; public: - void init(name issuer = {}) { - if (issuer.empty()) issuer = _issuer; + void init_without_open() { + init(name{}, false); + } + void init(name issuer = {}, bool open = true) { + if (!issuer) issuer = _issuer; const auto init_tokens = [&](cyber_token_api& token) { BOOST_CHECK_EQUAL(success(), token.create(issuer, token.make_asset(_total * 5))); BOOST_CHECK_EQUAL(success(), token.issue(issuer, issuer, token.make_asset(_total))); }; init_tokens(token); init_tokens(token2); + if (open) { + BOOST_CHECK_EQUAL(success(), token.open(_alice)); + BOOST_CHECK_EQUAL(success(), token.open(_bob)); + BOOST_CHECK_EQUAL(success(), token2.open(_alice)); + BOOST_CHECK_EQUAL(success(), token2.open(_bob)); + } } const account_name _issuer = N(issuer); @@ -54,6 +63,7 @@ class cyber_token_safe_tester : public golos_tester { const account_name _nobody = N(nobody); // not existing account struct errors: contract_error_messages { + const string no_token_account = amsg("no token account object found"); const string symbol_precision = amsg("symbol precision mismatch"); const string unlock_lt0 = amsg("unlock amount must be >= 0"); const string unlock_lte0 = amsg("unlock amount must be > 0"); @@ -63,32 +73,58 @@ class cyber_token_safe_tester : public golos_tester { const string delay_gt_max = amsg("delay must be <= " + std::to_string(cfg::safe_max_delay)); const string trusted_eq_owner = amsg("trusted and owner must be different accounts"); const string trusted_not_exists = amsg("trusted account does not exist"); - const string already_enabled = amsg("Safe already enabled"); - const string have_mods = amsg("Can't enable safe with existing delayed mods"); - const string disabled = amsg("Safe disabled"); - const string same_delay = amsg("Can't set same delay"); - const string same_trusted = amsg("Can't set same trusted"); + const string already_enabled = amsg("safe already enabled"); + const string have_mods = amsg("can't enable safe with existing delayed mods"); + const string disabled = amsg("safe disabled"); + const string same_delay = amsg("can't set same delay"); + const string same_trusted = amsg("can't set same trusted"); const string empty_mod_id = amsg("mod_id must not be empty"); const string have_mod_id = amsg("mod_id must be empty for trusted action"); - const string same_mod_id = amsg("Safe mod with the same id is already exists"); + const string same_mod_id = amsg("safe mod with the same id is already exists"); const string lock_gt_unlocked = amsg("lock must be <= unlocked"); const string nothing_set = amsg("delay and/or trusted must be set"); - const string mod_not_exists = amsg("Safe mod not found"); - const string still_locked = amsg("Safe change is time locked"); - const string nothing_to_apply = amsg("Change has no effect and can be cancelled"); + const string mod_not_exists = amsg("safe mod not found"); + const string still_locked = amsg("safe change is time locked"); + const string nothing_to_apply = amsg("change has no effect and can be cancelled"); const string global_lock = amsg("balance locked in safe"); - const string mod_global_lock = amsg("Safe locked globally"); + const string mod_global_lock = amsg("safe locked globally"); const string balance_lock = amsg("overdrawn safe unlocked balance"); const string balance_over = amsg("overdrawn balance"); const string unlocked_over = amsg("unlocked overflow"); const string period_le0 = amsg("period must be > 0"); const string period_gt_max = amsg("period must be <= " + std::to_string(cfg::safe_max_delay)); const string period_le_cur = amsg("new unlock time must be greater than current"); + const string close_with_safe = amsg("Cannot close because safe enabled."); } err; }; BOOST_AUTO_TEST_SUITE(cyber_token_safe) +BOOST_FIXTURE_TEST_CASE(no_balance, cyber_token_safe_tester) try { + BOOST_TEST_MESSAGE("Works only with opened balance"); + init_without_open(); + BOOST_CHECK(token.get_safe(_bob).is_null()); + + BOOST_TEST_MESSAGE("--- fail if account object doesn't exist"); + const name mod_id = N(mod); + BOOST_CHECK_EQUAL(err.no_token_account, token.enable_safe(_bob, 0, _delay)); + BOOST_CHECK_EQUAL(err.no_token_account, token.disable_safe(_bob, mod_id)); + BOOST_CHECK_EQUAL(err.no_token_account, token.unlock_safe(_bob, mod_id, 1)); + BOOST_CHECK_EQUAL(err.no_token_account, token.lock_safe(_bob, 1)); + BOOST_CHECK_EQUAL(err.no_token_account, token.modify_safe(_bob, mod_id, cfg::safe_max_delay/2)); + // Can't test apply_safe_mod with unexistig balance + BOOST_TEST_MESSAGE("--- success after open balance"); + BOOST_CHECK_EQUAL(success(), token.open(_bob)); + BOOST_CHECK_EQUAL(success(), token.enable_safe(_bob, 0, _delay, _alice)); + + BOOST_TEST_MESSAGE("--- fail to close balance with enabled safe"); + BOOST_CHECK_EQUAL(err.close_with_safe, token.close(_bob)); + BOOST_TEST_MESSAGE("--- success after disable safe"); + BOOST_CHECK_EQUAL(success(), token.disable_safe2(_bob, _alice)); + BOOST_CHECK_EQUAL(success(), token.close(_bob)); + +} FC_LOG_AND_RETHROW() + BOOST_FIXTURE_TEST_CASE(enable, cyber_token_safe_tester) try { BOOST_TEST_MESSAGE("Enable the safe"); init(); @@ -548,10 +584,6 @@ BOOST_FIXTURE_TEST_CASE(safe, cyber_token_safe_tester) try { init(_alice); // only issuer can retire so issue by alice const auto mod_id = N(mod); const double money = 100; - BOOST_CHECK_EQUAL(success(), token.open(_bob)); - BOOST_CHECK_EQUAL(success(), token.open(_carol)); - BOOST_CHECK_EQUAL(success(), token2.open(_bob)); - BOOST_CHECK_EQUAL(success(), token2.open(_carol)); BOOST_CHECK_EQUAL(success(), token.transfer(_alice, _bob, money)); BOOST_CHECK_EQUAL(success(), token2.transfer(_alice, _bob, money)); BOOST_CHECK_EQUAL(success(), token.transfer(_alice, _carol, _total - 2*money)); diff --git a/tests/cyber.token_test_api.hpp b/tests/cyber.token_test_api.hpp index e4514a654..f8d9cbd40 100644 --- a/tests/cyber.token_test_api.hpp +++ b/tests/cyber.token_test_api.hpp @@ -50,6 +50,9 @@ struct cyber_token_api: base_contract_api { ); } + action_result close(account_name owner) { + return close(owner, _symbol); + } action_result close(account_name owner, symbol symbol) { return push(N(close), owner, args() ("owner", owner) @@ -211,6 +214,7 @@ struct cyber_token_api: base_contract_api { if (v.is_object()) { auto o = mvo(v); o["balance"] = o["balance"].as().to_string(); + o["payments"] = o["payments"].as().to_string(); v = o; } return v; @@ -221,12 +225,8 @@ struct cyber_token_api: base_contract_api { } variant get_safe(name acc) { - auto v = get_struct(acc, N(safe), _symbol_code.value, ""); - if (v.is_object() && v["unlocked"].is_object()) { - auto o = mvo(v); - return o("unlocked", o["unlocked"].as()); - } - return v; + auto v = get_struct(acc, N(accounts), _symbol_code.value, ""); + return v.is_object() && v.get_object().contains("safe") ? v["safe"] : variant{}; } variant get_safe_mod(name acc, name mod_id) { @@ -239,7 +239,7 @@ struct cyber_token_api: base_contract_api { // generated objects variant make_safe(double unlocked, uint32_t delay, name trusted = {}) { - return mvo()("unlocked", make_asset(unlocked).to_string())("delay", delay)("trusted", trusted); + return mvo()("unlocked", make_asset(unlocked).get_amount())("delay", delay)("trusted", trusted); } variant make_safe_mod(name id, double unlock, optional delay = {}, optional trusted = {}) { auto mod = mvo()("id", id)("sym_code", _symbol_code)("unlock", to_shares(unlock)); From 54363ea99704db486c6c928a8217e409cc74f88f Mon Sep 17 00:00:00 2001 From: zxcat <550974+zxcat@users.noreply.github.com> Date: Fri, 20 Mar 2020 15:34:54 +0300 Subject: [PATCH 12/21] missing abi update #321 --- cyber.token/cyber.token.abi | 115 +++++++++++++++++++++++++++++++++++- 1 file changed, 113 insertions(+), 2 deletions(-) diff --git a/cyber.token/cyber.token.abi b/cyber.token/cyber.token.abi index 24d1e48f3..1891e2c2b 100644 --- a/cyber.token/cyber.token.abi +++ b/cyber.token/cyber.token.abi @@ -6,7 +6,14 @@ "name": "account", "base": "", "fields": [ {"name": "balance", "type": "asset"}, - {"name": "payments", "type": "asset"} + {"name": "payments", "type": "asset"}, + {"name": "safe", "type": "safe_t$"} + ] + }, { + "name": "applysafemod", "base": "", + "fields": [ + {"name": "owner", "type": "name"}, + {"name": "mod_id", "type": "name"} ] }, { "name": "balance_event", "base": "", @@ -27,6 +34,12 @@ {"name": "from", "type": "name"}, {"name": "recipients", "type": "recipient[]"} ] + }, { + "name": "cancelsafemod", "base": "", + "fields": [ + {"name": "owner", "type": "name"}, + {"name": "mod_id", "type": "name"} + ] }, { "name": "claim", "base": "", "fields": [ @@ -52,6 +65,27 @@ {"name": "max_supply", "type": "asset"}, {"name": "issuer", "type": "name"} ] + }, { + "name": "disablesafe", "base": "", + "fields": [ + {"name": "owner", "type": "name"}, + {"name": "sym_code", "type": "symbol_code"}, + {"name": "mod_id", "type": "name"} + ] + }, { + "name": "enablesafe", "base": "", + "fields": [ + {"name": "owner", "type": "name"}, + {"name": "unlock", "type": "asset"}, + {"name": "delay", "type": "uint32"}, + {"name": "trusted", "type": "name"} + ] + }, { + "name": "globallock", "base": "", + "fields": [ + {"name": "owner", "type": "name"}, + {"name": "period", "type": "uint32"} + ] }, { "name": "issue", "base": "", "fields": [ @@ -59,6 +93,27 @@ {"name": "quantity", "type": "asset"}, {"name": "memo", "type": "string"} ] + }, { + "name": "lock", "base": "", + "fields": [ + {"name": "id", "type": "uint64"}, + {"name": "unlocks", "type": "time_point_sec"} + ] + }, { + "name": "locksafe", "base": "", + "fields": [ + {"name": "owner", "type": "name"}, + {"name": "lock", "type": "asset"} + ] + }, { + "name": "modifysafe", "base": "", + "fields": [ + {"name": "owner", "type": "name"}, + {"name": "sym_code", "type": "symbol_code"}, + {"name": "mod_id", "type": "name"}, + {"name": "delay", "type": "uint32?"}, + {"name": "trusted", "type": "name?"} + ] }, { "name": "open", "base": "", "fields": [ @@ -87,6 +142,23 @@ {"name": "quantity", "type": "asset"}, {"name": "memo", "type": "string"} ] + }, { + "name": "safe_t", "base": "", + "fields": [ + {"name": "unlocked", "type": "int64"}, + {"name": "delay", "type": "uint32"}, + {"name": "trusted", "type": "name"} + ] + }, { + "name": "safemod", "base": "", + "fields": [ + {"name": "id", "type": "name"}, + {"name": "sym_code", "type": "symbol_code"}, + {"name": "date", "type": "time_point_sec"}, + {"name": "unlock", "type": "int64"}, + {"name": "delay", "type": "uint32?"}, + {"name": "trusted", "type": "name?"} + ] }, { "name": "transfer", "base": "", "fields": [ @@ -95,19 +167,34 @@ {"name": "quantity", "type": "asset"}, {"name": "memo", "type": "string"} ] + }, { + "name": "unlocksafe", "base": "", + "fields": [ + {"name": "owner", "type": "name"}, + {"name": "unlock", "type": "asset"}, + {"name": "mod_id", "type": "name"} + ] } ], "actions": [ + {"name": "applysafemod", "type": "applysafemod"}, {"name": "bulkpayment", "type": "bulkpayment"}, {"name": "bulktransfer", "type": "bulktransfer"}, + {"name": "cancelsafemod", "type": "cancelsafemod"}, {"name": "claim", "type": "claim"}, {"name": "close", "type": "close"}, {"name": "create", "type": "create"}, + {"name": "disablesafe", "type": "disablesafe"}, + {"name": "enablesafe", "type": "enablesafe"}, + {"name": "globallock", "type": "globallock"}, {"name": "issue", "type": "issue"}, + {"name": "locksafe", "type": "locksafe"}, + {"name": "modifysafe", "type": "modifysafe"}, {"name": "open", "type": "open"}, {"name": "payment", "type": "payment"}, {"name": "retire", "type": "retire"}, - {"name": "transfer", "type": "transfer"} + {"name": "transfer", "type": "transfer"}, + {"name": "unlocksafe", "type": "unlocksafe"} ], "events": [ {"name": "balance", "type": "balance_event"}, @@ -122,6 +209,30 @@ ] } ] + }, { + "name": "lock", "type": "lock", + "indexes": [{ + "name": "primary", "unique": true, + "orders": [ + {"field": "id", "order": "asc"} + ] + } + ] + }, { + "name": "safemod", "type": "safemod", + "indexes": [{ + "name": "primary", "unique": true, + "orders": [ + {"field": "id", "order": "asc"} + ] + }, { + "name": "bysymbolcode", "unique": true, + "orders": [ + {"field": "sym_code", "order": "asc"}, + {"field": "id", "order": "asc"} + ] + } + ] }, { "name": "stat", "type": "currency_stats", "scope_type": "symbol_code", "indexes": [{ From d2804c40b6e54521d147751c48a0b08d8d7dadb4 Mon Sep 17 00:00:00 2001 From: zxcat <550974+zxcat@users.noreply.github.com> Date: Fri, 20 Mar 2020 15:56:43 +0300 Subject: [PATCH 13/21] Update generated abi when build contracts locally #326 also updates deployutils --- .buildkite/steps/build-image.sh | 2 +- CMakeLists.txt | 17 +++++++++++------ Docker/Dockerfile | 3 +++ scripts/deployutils | 2 +- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/.buildkite/steps/build-image.sh b/.buildkite/steps/build-image.sh index 1e341426b..64de30978 100755 --- a/.buildkite/steps/build-image.sh +++ b/.buildkite/steps/build-image.sh @@ -24,4 +24,4 @@ if [[ -z ${BUILDER_TAG+x} ]]; then docker pull cyberway/builder:${BUILDER_TAG} fi -docker build -t cyberway/cyberway.contracts:${REVISION} --build-arg=version=${REVISION} --build-arg=cw_tag=${CW_TAG} --build-arg=cdt_tag=${CDT_TAG} --build-arg=builder_tag=${BUILDER_TAG} -f Docker/Dockerfile . +docker build -t cyberway/cyberway.contracts:${REVISION} --build-arg=version=${REVISION} --build-arg=cw_tag=${CW_TAG} --build-arg=cdt_tag=${CDT_TAG} --build-arg=builder_tag=${BUILDER_TAG} --build-arg=ci_build=${CI} -f Docker/Dockerfile . diff --git a/CMakeLists.txt b/CMakeLists.txt index e6d3ad423..dc7d0a0f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,12 +38,17 @@ endmacro() macro(add_contract_with_checked_abi CONTRACT_NAME TARGET ABIFILE) add_contract(${CONTRACT_NAME} ${TARGET} ${ARGN}) get_target_property(BINOUTPUT ${TARGET}.wasm BINARY_DIR) - add_custom_command(TARGET ${TARGET}.wasm POST_BUILD - COMMAND ${PROJECT_SOURCE_DIR}/scripts/deployutils/abiprinter.py <${BINOUTPUT}/${TARGET}.abi >${BINOUTPUT}/${TARGET}.abi.pretty) - add_custom_target(${TARGET}.abicheck ALL - COMMAND ${CYBERWAY_ABIDIFF} ${CMAKE_CURRENT_SOURCE_DIR}/${ABIFILE} ${BINOUTPUT}/${TARGET}.abi - DEPENDS ${TARGET}.wasm ${ABIFILE} - ) + if(CI_BUILD STREQUAL "true") + add_custom_command(TARGET ${TARGET}.wasm POST_BUILD + COMMAND ${PROJECT_SOURCE_DIR}/scripts/deployutils/abiprinter.py <${BINOUTPUT}/${TARGET}.abi >${BINOUTPUT}/${TARGET}.abi.pretty) + add_custom_target(${TARGET}.abicheck ALL + COMMAND ${CYBERWAY_ABIDIFF} ${CMAKE_CURRENT_SOURCE_DIR}/${ABIFILE} ${BINOUTPUT}/${TARGET}.abi + DEPENDS ${TARGET}.wasm ${ABIFILE} + ) + else() + add_custom_command(TARGET ${TARGET}.wasm POST_BUILD + COMMAND ${PROJECT_SOURCE_DIR}/scripts/deployutils/abiprinter.py <${BINOUTPUT}/${TARGET}.abi >${CMAKE_CURRENT_SOURCE_DIR}/${ABIFILE}) + endif() endmacro() macro(add_contract_with_abi TARGET ABIFILE) diff --git a/Docker/Dockerfile b/Docker/Dockerfile index e2b094f6c..1bc14768b 100644 --- a/Docker/Dockerfile +++ b/Docker/Dockerfile @@ -13,6 +13,8 @@ ENV CYBERWAY /opt/cyberway/ COPY . /cyberway.contracts +ARG ci_build=no + RUN ldconfig && cd cyberway.contracts \ && cmake -H. -B"build" \ -GNinja \ @@ -20,6 +22,7 @@ RUN ldconfig && cd cyberway.contracts \ -DCMAKE_INSTALL_PREFIX=/opt/cyberway.contracts/ \ -Dcyberway.cdt_DIR=/opt/cyberway.cdt/lib/cmake/cyberway.cdt \ -DEOSIO_ROOT=$CYBERWAY \ + -DCI_BUILD=$ci_build \ && cmake --build build --target install FROM ubuntu:18.04 diff --git a/scripts/deployutils b/scripts/deployutils index 70ea6a9c8..5668829b8 160000 --- a/scripts/deployutils +++ b/scripts/deployutils @@ -1 +1 @@ -Subproject commit 70ea6a9c8a6f7c6c179a28cfd68f86f47b4737d1 +Subproject commit 5668829b824d250852f978486b89220701e22fe4 From c3c0d2b7971d95c6c167898be88815a3b8358069 Mon Sep 17 00:00:00 2001 From: zxcat <550974+zxcat@users.noreply.github.com> Date: Fri, 20 Mar 2020 17:38:26 +0300 Subject: [PATCH 14/21] update staking providing tests to work with changed res usage #321 --- tests/cyber.stake_tests.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/cyber.stake_tests.cpp b/tests/cyber.stake_tests.cpp index bac44e612..49b45921b 100644 --- a/tests/cyber.stake_tests.cpp +++ b/tests/cyber.stake_tests.cpp @@ -748,6 +748,7 @@ BOOST_FIXTURE_TEST_CASE(basic_test, cyber_stake_tester) try { install_contract(config::system_account_name, contracts::bios_wasm(), contracts::bios_abi()); BOOST_CHECK_EQUAL(success(), bios.set_min_transaction_cpu_usage(500)); stake.set_billed(1500, 1024); + token.set_billed(500, 1024); bios.set_billed(500, 1); int64_t stake_amount = 50000000000; size_t blocks_before_payout = 4; @@ -780,6 +781,7 @@ BOOST_FIXTURE_TEST_CASE(basic_test, cyber_stake_tester) try { BOOST_TEST_MESSAGE("alice's resource balance = " << bios.get_account_balance(_alice)); BOOST_CHECK_EQUAL(success(), stake.setproxylvl(_alice, token._symbol.to_symbol_code(), 2)); BOOST_TEST_MESSAGE("alice's resource balance = " << bios.get_account_balance(_alice)); + produce_block(); BOOST_CHECK_EQUAL("explicitly_billed_exception", stake.setproxylvl(_alice, token._symbol.to_symbol_code(), 1)); BOOST_TEST_MESSAGE("alice's resource balance = " << bios.get_account_balance(_alice)); From a804123fdebbaec9b46773f6cd19b138d3f6e933 Mon Sep 17 00:00:00 2001 From: zxcat <550974+zxcat@users.noreply.github.com> Date: Fri, 20 Mar 2020 19:14:29 +0300 Subject: [PATCH 15/21] more clear #326 --- CMakeLists.txt | 2 +- Docker/Dockerfile | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dc7d0a0f9..0002bb6d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,7 +38,7 @@ endmacro() macro(add_contract_with_checked_abi CONTRACT_NAME TARGET ABIFILE) add_contract(${CONTRACT_NAME} ${TARGET} ${ARGN}) get_target_property(BINOUTPUT ${TARGET}.wasm BINARY_DIR) - if(CI_BUILD STREQUAL "true") + if(ABICHECK STREQUAL "true") add_custom_command(TARGET ${TARGET}.wasm POST_BUILD COMMAND ${PROJECT_SOURCE_DIR}/scripts/deployutils/abiprinter.py <${BINOUTPUT}/${TARGET}.abi >${BINOUTPUT}/${TARGET}.abi.pretty) add_custom_target(${TARGET}.abicheck ALL diff --git a/Docker/Dockerfile b/Docker/Dockerfile index 1bc14768b..77754b6ec 100644 --- a/Docker/Dockerfile +++ b/Docker/Dockerfile @@ -13,7 +13,7 @@ ENV CYBERWAY /opt/cyberway/ COPY . /cyberway.contracts -ARG ci_build=no +ARG ci_build RUN ldconfig && cd cyberway.contracts \ && cmake -H. -B"build" \ @@ -22,7 +22,7 @@ RUN ldconfig && cd cyberway.contracts \ -DCMAKE_INSTALL_PREFIX=/opt/cyberway.contracts/ \ -Dcyberway.cdt_DIR=/opt/cyberway.cdt/lib/cmake/cyberway.cdt \ -DEOSIO_ROOT=$CYBERWAY \ - -DCI_BUILD=$ci_build \ + -DABICHECK=$ci_build \ && cmake --build build --target install FROM ubuntu:18.04 From 8caa6bfea117f5d054e2b867734055760780c2e2 Mon Sep 17 00:00:00 2001 From: zxcat <550974+zxcat@users.noreply.github.com> Date: Wed, 25 Mar 2020 15:21:33 +0300 Subject: [PATCH 16/21] Add setinfo action to cyber.stake #329 (#331) --- cyber.stake/cyber.stake.abi | 9 + cyber.stake/include/cyber.stake/config.hpp | 2 + .../include/cyber.stake/cyber.stake.hpp | 24 +- cyber.stake/src/cyber.stake.cpp | 48 ++-- tests/cyber.stake_test_api.hpp | 38 ++- tests/cyber.stake_tests.cpp | 272 +++++++++--------- 6 files changed, 220 insertions(+), 173 deletions(-) diff --git a/cyber.stake/cyber.stake.abi b/cyber.stake/cyber.stake.abi index ad3008685..235364166 100644 --- a/cyber.stake/cyber.stake.abi +++ b/cyber.stake/cyber.stake.abi @@ -130,6 +130,14 @@ {"name": "break_fee", "type": "int16"}, {"name": "break_min_own_staked", "type": "int64"} ] + }, { + "name": "setinfo", "base": "", + "fields": [ + {"name": "account", "type": "name"}, + {"name": "token_code", "type": "symbol_code"}, + {"name": "url", "type": "string"}, + {"name": "info", "type": "string$"} + ] }, { "name": "setkey", "base": "", "fields": [ @@ -202,6 +210,7 @@ {"name": "setautorc", "type": "setautorc"}, {"name": "setautorcmode", "type": "setautorcmode"}, {"name": "setgrntterms", "type": "setgrntterms"}, + {"name": "setinfo", "type": "setinfo"}, {"name": "setkey", "type": "setkey"}, {"name": "setminstaked", "type": "setminstaked"}, {"name": "setproxyfee", "type": "setproxyfee"}, diff --git a/cyber.stake/include/cyber.stake/config.hpp b/cyber.stake/include/cyber.stake/config.hpp index cadf7b319..86f946fc6 100644 --- a/cyber.stake/include/cyber.stake/config.hpp +++ b/cyber.stake/include/cyber.stake/config.hpp @@ -8,6 +8,8 @@ static const auto proxylvl_recovery_delay = 60 * 60 * 24 * 7; static const auto max_no_pick_period = 60 * 60 * 24 * 30; static const auto reward_memo = "$reward"; +constexpr size_t max_validator_meta_size = 2048; + } } // cyber::config diff --git a/cyber.stake/include/cyber.stake/cyber.stake.hpp b/cyber.stake/include/cyber.stake/cyber.stake.hpp index 50b0ecb77..0b79b6fe1 100644 --- a/cyber.stake/include/cyber.stake/cyber.stake.hpp +++ b/cyber.stake/include/cyber.stake/cyber.stake.hpp @@ -93,16 +93,16 @@ struct structures { } void check_own_staked(name privileged, int64_t min_own_staked_for_election) const { if (!has_auth(privileged)) { - //there are accounts with inconsistent balance and min_own_staked values, + //there are accounts with inconsistent balance and min_own_staked values, //to test their deactivation we allow _self to set such values - + eosio::check(proxy_level || min_own_staked >= min_own_staked_for_election, "min_own_staked can't be less than min_own_staked_for_election for users with an ultimate level"); eosio::check(get_own_funds() >= min_own_staked, "own staked funds can't be less than min_own_staked"); } } - }; - + }; + struct auto_recall { uint64_t id; symbol_code token_code; @@ -228,7 +228,7 @@ struct structures { using prov_payouts [[eosio::order("id")]] = eosio::multi_index<"provpayout"_n, structures::prov_payout_struct, prov_payout_acc_index>; - using susp_key_index [[using eosio: order("token_code"), order("account"), order("action_name")]] = + using susp_key_index [[using eosio: order("token_code"), order("account"), order("action_name")]] = eosio::indexed_by<"bykey"_n, eosio::const_mem_fun >; using susps [[eosio::order("id")]] = eosio::multi_index<"suspense"_n, structures::suspense, susp_key_index>; @@ -278,7 +278,7 @@ struct structures { void set_votes(symbol_code token_code, const std::map& votes_changes); void update_provided(name grantor_name, name recipient_name, asset quantity); - + void check_suspense(susps& susps_table, susps_idx_t& susps_idx, symbol_code token_code, name account, name action_name); void set_suspense(name ram_payer, susps& susps_table, susps_idx_t& susps_idx, symbol_code token_code, name account, name action_name, int delay); @@ -289,7 +289,7 @@ struct structures { int64_t votes = 0; public_key signing_key = {}; }; - + static inline uint8_t get_max_level(symbol_code token_code) { params params_table(table_owner, table_owner.value); return params_table.get(token_code.raw(), "no staking for token").max_proxies.size(); @@ -369,10 +369,10 @@ struct structures { } return ret; } - + static inline bool candidate_exists(name account, symbol_code token_code) { staking_exists(token_code); - + candidates candidates_table(table_owner, table_owner.value); auto cands_idx = candidates_table.get_index<"bykey"_n>(); return cands_idx.find(std::make_tuple(token_code, account)) != cands_idx.end(); @@ -407,7 +407,7 @@ struct structures { [[eosio::action]] void setproxyfee(name account, symbol_code token_code, int16_t fee); [[eosio::action]] void setminstaked(name account, symbol_code token_code, int64_t min_own_staked); [[eosio::action]] void setkey(name account, symbol_code token_code, std::optional signing_key); - + [[eosio::action]] void updatefunds(name account, symbol_code token_code); [[eosio::action]] void suspendcand(name account, symbol_code token_code); @@ -428,8 +428,10 @@ struct structures { [[eosio::action]] void recalluse(name grantor_name, name recipient_name, asset quantity); [[eosio::action]] void claim(name grantor_name, name recipient_name, symbol_code token_code); - + [[eosio::action]] void setautorc(name account, symbol_code token_code, bool break_fee_enabled, bool break_min_stake_enabled); [[eosio::action]] void setautorcmode(symbol_code token_code, bool enabled); + + [[eosio::action]] void setinfo(name account, symbol_code token_code, string url, eosio::binary_extension info); }; } /// namespace cyber diff --git a/cyber.stake/src/cyber.stake.cpp b/cyber.stake/src/cyber.stake.cpp index b082c4843..9e6482d20 100644 --- a/cyber.stake/src/cyber.stake.cpp +++ b/cyber.stake/src/cyber.stake.cpp @@ -328,7 +328,7 @@ void stake::set_suspense(name ram_payer, susps& susps_table, susps_idx_t& susps_ auto susps_itr = susps_idx.find(std::make_tuple(token_code, account, action_name)); if (susps_itr != susps_idx.end()) { susps_idx.modify(susps_itr, name(), [&](auto& s) { - s.expiration_time = std::max(s.expiration_time, time_point_sec(eosio::current_time_point() + seconds(delay))); + s.expiration_time = std::max(s.expiration_time, time_point_sec(eosio::current_time_point() + seconds(delay))); }); } else { @@ -353,19 +353,19 @@ void stake::setkey(name account, symbol_code token_code, std::optional(); auto agent = get_agent_itr(token_code, agents_idx, account); eosio::check(!agent->proxy_level, account.to_string() + " is not a candidate"); - + auto issuer = eosio::token::get_issuer(config::token_name, token_code); bool has_issuer_auth = has_auth(issuer); - + candidates candidates_table(table_owner, table_owner.value); auto cands_idx = candidates_table.get_index<"bykey"_n>(); auto cand = cands_idx.find(std::make_tuple(token_code, account)); eosio::check(cand != cands_idx.end(), ("SYSTEM: candidate " + account.to_string() + " doesn't exist").c_str()); - + susps susps_table(_self, _self.value); auto susps_idx = susps_table.get_index<"bykey"_n>(); auto action_name = "setkey"_n; - + if (actual_signing_key != public_key{}) { require_auth(account); check_suspense(susps_table, susps_idx, token_code, account, action_name); @@ -374,12 +374,12 @@ void stake::setkey(name account, symbol_code token_code, std::optionalmin_own_staked >= min_own_staked_for_election && agent->get_own_funds() >= agent->min_own_staked) { require_auth(account); } - + cands_idx.modify(cand, name(), [actual_signing_key](auto& a) { a.signing_key = actual_signing_key; a.enabled = actual_signing_key != public_key{}; }); - + if (has_issuer_auth && !cand->enabled) { set_suspense(issuer, susps_table, susps_idx, token_code, account, action_name, config::key_recovery_delay); } @@ -388,16 +388,16 @@ void stake::setkey(name account, symbol_code token_code, std::optional(); auto action_name = "setproxylvl"_n; - + if (!has_issuer_auth) { require_auth(account); check_suspense(susps_table, susps_idx, token_code, account, action_name); } - + params params_table(table_owner, table_owner.value); const auto& param = params_table.get(token_code.raw(), "no staking for token"); @@ -445,12 +445,12 @@ void stake::setproxylvl(name account, symbol_code token_code, uint8_t level) { a.proxy_level = level; }); agent->check_own_staked(_self, param.min_own_staked_for_election); - + if (has_issuer_auth) { set_suspense(issuer, susps_table, susps_idx, token_code, account, action_name, config::proxylvl_recovery_delay); } } - + void stake::create(symbol token_symbol, std::vector max_proxies, int64_t depriving_window, int64_t min_own_staked_for_election) { auto token_code = token_symbol.code(); @@ -536,13 +536,13 @@ void stake::updatefunds(name account, symbol_code token_code) { void stake::suspendcand(name account, symbol_code token_code) { //require_auth(anyone); auto issuer = eosio::token::get_issuer(config::token_name, token_code); - + candidates candidates_table(table_owner, table_owner.value); auto cands_idx = candidates_table.get_index<"bykey"_n>(); auto cand = cands_idx.find(std::make_tuple(token_code, account)); eosio::check(cand != cands_idx.end(), "candidate doesn't exist"); eosio::check(cand->latest_pick < eosio::current_time_point() - eosio::seconds(config::max_no_pick_period), "candidate is active"); - + INLINE_ACTION_SENDER(cyber::stake, setproxylvl)(config::stake_name, {issuer, config::active_name}, {account, token_code, get_max_level(token_code)}); } @@ -742,15 +742,15 @@ void stake::claim(name grantor_name, name recipient_name, symbol_code token_code void stake::setautorc(name account, symbol_code token_code, bool break_fee_enabled, bool break_min_stake_enabled) { require_auth(account); staking_exists(token_code); - + agents agents_table(table_owner, table_owner.value); auto agents_idx = agents_table.get_index<"bykey"_n>(); get_agent_itr(token_code, agents_idx, account); //checking that the agent exists - + autorcs autorcs_table(table_owner, table_owner.value); auto autorcs_idx = autorcs_table.get_index<"bykey"_n>(); autorcs_idx.get(std::make_tuple(token_code, name()), "custom auto recall mode is disabled for this token"); - + if (!break_fee_enabled && !break_min_stake_enabled) { auto autorc_itr = autorcs_idx.find(std::make_tuple(token_code, account)); eosio::check(autorc_itr != autorcs_idx.end(), "no params changed"); @@ -781,10 +781,10 @@ void stake::setautorcmode(symbol_code token_code, bool enabled) { auto issuer = eosio::token::get_issuer(config::token_name, token_code); require_auth(issuer); staking_exists(token_code); - + autorcs autorcs_table(table_owner, table_owner.value); auto autorcs_idx = autorcs_table.get_index<"bykey"_n>(); - auto autorc_itr = autorcs_idx.find(std::make_tuple(token_code, name())); + auto autorc_itr = autorcs_idx.find(std::make_tuple(token_code, name())); if (enabled) { eosio::check(autorc_itr == autorcs_idx.end(), "custom auto recall mode is already enabled for this token"); autorcs_table.emplace(issuer, [&](auto& p) { p = { @@ -799,6 +799,16 @@ void stake::setautorcmode(symbol_code token_code, bool enabled) { } } +void stake::setinfo(name account, symbol_code token_code, string url, eosio::binary_extension info) { + require_auth(account); + eosio::check(url.size() <= config::max_validator_meta_size, "url is too long"); + eosio::check(!info.has_value() || info->size() <= config::max_validator_meta_size, "info is too long"); + agents tbl(table_owner, table_owner.value); + auto idx = tbl.get_index<"bykey"_n>(); + auto agent = get_agent_itr(token_code, idx, account); + eosio::check(!agent->proxy_level, "only validator can set info"); +} + void stake::returnlosses() { auto losses_state = losses_singleton(_self, _self.value); eosio::check(!losses_state.exists(), "losses already have been returned"); diff --git a/tests/cyber.stake_test_api.hpp b/tests/cyber.stake_test_api.hpp index d3526341e..e05b02ea4 100644 --- a/tests/cyber.stake_test_api.hpp +++ b/tests/cyber.stake_test_api.hpp @@ -27,11 +27,11 @@ struct cyber_stake_api: base_contract_api { } return 0; } - + action_result push_maybe_msig(account_name act, account_name actor, mvo a, bool self_signed) { return self_signed ? - push_msig(act, {{actor, config::active_name}, {_code, config::active_name}}, - {actor, _code}, a) : + push_msig(act, {{actor, config::active_name}, {_code, config::active_name}}, + {actor, _code}, a) : push(act, actor, a); } @@ -123,7 +123,7 @@ struct cyber_stake_api: base_contract_api { ("quantity", quantity) ); } - + action_result setproxylvl(account_name account, symbol_code token_code, uint8_t level, bool mssg = true, bool self_signed = false) { if (mssg && verbose) { BOOST_TEST_MESSAGE("--- " << account << " sets proxy level"); @@ -135,7 +135,7 @@ struct cyber_stake_api: base_contract_api { return push_maybe_msig(N(setproxylvl), account, a, self_signed); } action_result setproxyfee(account_name account, symbol_code token_code, int16_t fee, bool self_signed = false) { - auto a = args() + auto a = args() ("account", account) ("token_code", token_code) ("fee", fee); @@ -148,8 +148,8 @@ struct cyber_stake_api: base_contract_api { ("min_own_staked", min_own_staked); return push_maybe_msig(N(setminstaked), account, a, self_signed); } - - action_result setkey(account_name account, symbol_code token_code, bool empty, + + action_result setkey(account_name account, symbol_code token_code, bool empty, std::optional signer = std::nullopt, bool self_signed = false) { auto a = args() ("account", account) @@ -166,7 +166,7 @@ struct cyber_stake_api: base_contract_api { ("token_code", token_code) ); } - + action_result suspendcand(account_name account, symbol_code token_code) { return push(N(suspendcand), account, args() ("account", account) @@ -210,16 +210,26 @@ struct cyber_stake_api: base_contract_api { ("token_code", token_code) ); } - + action_result setautorc(account_name account, symbol_code token_code, bool break_fee_enabled, bool break_min_stake_enabled) { return push(N(setautorc), account, args()("account", account)("token_code", token_code) ("break_fee_enabled", break_fee_enabled)("break_min_stake_enabled", break_min_stake_enabled)); } - + action_result setautorcmode(account_name issuer, symbol_code token_code, bool enabled) { return push(N(setautorcmode), issuer, args()("enabled", enabled)("token_code", token_code)); } + action_result setinfo(account_name account, symbol_code token_code, string url, optional info = {}) { + auto a = args() + ("account", account) + ("token_code", token_code) + ("url", url); + if (info) + a("info", info); + return push(N(setinfo), account, a); + } + action_result register_candidate(account_name account, symbol_code token_code, bool need_to_open = true) { if (need_to_open) { auto ret = open(account, token_code); @@ -232,7 +242,7 @@ struct cyber_stake_api: base_contract_api { if (verbose) { BOOST_TEST_MESSAGE("--- " << account << " sets key"); } - return push(N(setkey), account, args() + return push(N(setkey), account, args() ("account", account) ("token_code", token_code) ("signing_key", base_tester::get_public_key(account, "active")) @@ -260,15 +270,15 @@ struct cyber_stake_api: base_contract_api { auto all = _tester->get_all_chaindb_rows(name(), 0, N(stake.cand), false); for(auto& v : all) { auto o = mvo(v); - if (v["account"].as() == account && - v["token_code"].as() == token_symbol.to_symbol_code()) + if (v["account"].as() == account && + v["token_code"].as() == token_symbol.to_symbol_code()) { return v; } } return variant(); } - + int64_t get_payout(symbol_code token_code, name grantor_name, name recipient_name) { return get_amount(token_code, grantor_name, recipient_name, _tester->get_all_chaindb_rows(_code, _code.value, N(provpayout), false)); } diff --git a/tests/cyber.stake_tests.cpp b/tests/cyber.stake_tests.cpp index 49b45921b..4f9a6c610 100644 --- a/tests/cyber.stake_tests.cpp +++ b/tests/cyber.stake_tests.cpp @@ -113,78 +113,37 @@ class cyber_stake_tester : public golos_tester { const string incorrect_proxy_levels(uint8_t g, uint8_t a) { return amsg("incorrect proxy levels: grantor " + std::to_string(g) + ", agent " + std::to_string(a)); }; - static string no_agent() { - return "agent doesn't exist"; - } - const string agent_doesnt_exist() { - return amsg(no_agent()); - } - const string agent_exists() { - return amsg(std::string("agent already exists")); - } - const string no_funds() { - return amsg(std::string("insufficient funds")); - } - const string not_enough_staked() { - return amsg(std::string("not enough staked tokens")); - } - const string not_enough_delegated() { - return amsg(std::string("not enough delegated tokens")); - } - const string no_funds_due_to_usage() { - return amsg(std::string("no staked tokens available due to resource usage")); - } - const string no_agent_funds() { - return amsg(std::string("insufficient agent funds")); - } - const string must_withdraw_positive() { - return amsg(std::string("must withdraw positive quantity")); - } - const string can_not_be_negative() { - return amsg(std::string("quantity can't be negative")); - } - const string min_staked_can_not_be_negative() { - return amsg(std::string("min_own_staked can't be negative")); - } - const string nothing_to_claim() { - return amsg(std::string("nothing to claim")); - } - const string nothing_to_cancel() { - return amsg(std::string("nothing to cancel")); - } - const string min_for_election_violated() { - return amsg(std::string("min_own_staked can't be less than min_own_staked_for_election for users with an ultimate level")); - } - const string own_funds_violated() { - return amsg(std::string("own staked funds can't be less than min_own_staked")); - } - const string level_too_high() { - return amsg(std::string("level too high")); - } - const string level_no_change() { - return amsg(std::string("proxy level has not been changed")); - } - const string proxy_cannot_be_added() { - return amsg(std::string("proxy cannot be added")); - } - const string has_a_proxy() { - return amsg(std::string("can't set an ultimate level because the user has a proxy")); - } - const string too_many_proxies() { - return amsg(std::string("can't set proxy level, user has too many proxies")); - } - const string already_have_been_returned() { - return amsg(std::string("losses already have been returned")); - } - const string bad_precision = amsg("symbol precision mismatch"); - const string quantity_bad_precision = amsg("quantity precision mismatch"); - const string quantity_le0 = amsg("quantity must be positive"); - const string dlgvote_no_staking = amsg("no staking for token"); - const string too_high_pct() { - return amsg(std::string("too high pct value")); - } - const string incorrect_pct() { - return amsg(std::string("pct must be between 0% and 100% (0-10000)")); + const string no_agent = "agent doesn't exist"; + const string agent_doesnt_exist = amsg(no_agent); + const string agent_exists = amsg("agent already exists"); + const string no_funds = amsg("insufficient funds"); + const string not_enough_staked = amsg("not enough staked tokens"); + const string not_enough_delegated = amsg("not enough delegated tokens"); + const string no_funds_due_to_usage = amsg("no staked tokens available due to resource usage"); + const string no_agent_funds = amsg("insufficient agent funds"); + const string must_withdraw_positive = amsg("must withdraw positive quantity"); + const string can_not_be_negative = amsg("quantity can't be negative"); // !! didn't check. TODO: #330 + const string min_staked_can_not_be_negative = amsg("min_own_staked can't be negative"); + const string nothing_to_claim = amsg("nothing to claim"); + const string nothing_to_cancel = amsg("nothing to cancel"); // !! didn't check. TODO: #330 + const string min_for_election_violated = + amsg("min_own_staked can't be less than min_own_staked_for_election for users with an ultimate level"); + const string own_funds_violated = amsg("own staked funds can't be less than min_own_staked"); + const string level_too_high = amsg("level too high"); + const string level_no_change = amsg("proxy level has not been changed"); + const string proxy_cannot_be_added = amsg("proxy cannot be added"); + const string has_a_proxy = amsg("can't set an ultimate level because the user has a proxy"); + const string too_many_proxies = amsg("can't set proxy level, user has too many proxies"); + const string already_have_been_returned = amsg("losses already have been returned"); + const string bad_precision = amsg("symbol precision mismatch"); + const string quantity_bad_precision = amsg("quantity precision mismatch"); + const string quantity_le0 = amsg("quantity must be positive"); + const string dlgvote_no_staking = amsg("no staking for token"); + const string too_high_pct = amsg("too high pct value"); + const string incorrect_pct = amsg("pct must be between 0% and 100% (0-10000)"); + + const string no_agent_exist(account_name agent) { // !! checked only in setinfo tests! TODO: #330 + return amsg(string("agent ") + agent.to_string() + " doesn't exist"); } const string not_a_candidate(account_name account) { return amsg(account.to_string() + " is not a candidate"); @@ -192,13 +151,13 @@ class cyber_stake_tester : public golos_tester { static bool is_insufficient_staked_mssg(const std::string& arg) { return arg.find("has insufficient staked tokens") != std::string::npos; } - static bool is_insufficient_ram_mssg(const std::string& arg) { + static bool is_insufficient_ram_mssg(const std::string& arg) { // !! didn't check. TODO: #330 return arg.find(" for use ram.") != std::string::npos; } - static bool is_costs_too_much_mssg(const std::string& arg) { + static bool is_costs_too_much_mssg(const std::string& arg) { // !! didn't check. TODO: #330 return arg.find("transaction costs too much") != std::string::npos; } - static bool is_system_err_mssg(const std::string& arg) { + static bool is_system_err_mssg(const std::string& arg) { // !! didn't check (turned off). TODO: #330 return arg.find("SYSTEM") != std::string::npos; } static bool is_transaction_cpu_limit_err_mssg(const std::string& arg) { @@ -210,13 +169,16 @@ class cyber_stake_tester : public golos_tester { static bool is_block_cpu_limit_err_mssg(const std::string& arg) { return arg.find("is greater than the billable CPU time left in the block") != std::string::npos; } - static bool is_block_res_limit_err_mssg(const std::string& arg) { + static bool is_block_res_limit_err_mssg(const std::string& arg) { // !! didn't check. TODO: #330 return arg.find("Block has insufficient resources") != std::string::npos; } const string no_params_changed = amsg("no params changed"); const string autorc_disabled = amsg("custom auto recall mode is disabled for this token"); const string autorc_already_disabled = amsg("custom auto recall mode is already disabled for this token"); const string autorc_already_enabled = amsg("custom auto recall mode is already enabled for this token"); + const string long_url = amsg("url is too long"); + const string long_info = amsg("info is too long"); + const string setinfo_not_validator = amsg("only validator can set info"); } err; }; @@ -397,16 +359,16 @@ BOOST_FIXTURE_TEST_CASE(open_test, cyber_stake_tester) try { BOOST_CHECK_EQUAL(success(), token.transfer(_alice, _code, stake_u)); BOOST_TEST_MESSAGE("--- open and enable"); - BOOST_CHECK_EQUAL(err.agent_exists(), stake.open(_alice, token._symbol.to_symbol_code())); + BOOST_CHECK_EQUAL(err.agent_exists, stake.open(_alice, token._symbol.to_symbol_code())); BOOST_CHECK_EQUAL(success(), stake.open(_bob, token._symbol.to_symbol_code())); BOOST_CHECK_EQUAL(success(), stake.enable(_issuer, token._symbol)); BOOST_CHECK_EQUAL("explicitly_billed_exception", stake.open(_carol, token._symbol.to_symbol_code())); BOOST_TEST_MESSAGE("--- fail of delegate vote to not agent"); - BOOST_CHECK_EQUAL(err.no_agent(), stake.delegatevote(_alice, _carol, stake_u)); + BOOST_CHECK_EQUAL(err.no_agent, stake.delegatevote(_alice, _carol, stake_u)); BOOST_CHECK_EQUAL(success(), stake.open(_carol, token._symbol.to_symbol_code(), _alice)); produce_block(); - BOOST_CHECK_EQUAL(err.agent_exists(), stake.open(_carol, token._symbol.to_symbol_code(), _alice)); + BOOST_CHECK_EQUAL(err.agent_exists, stake.open(_carol, token._symbol.to_symbol_code(), _alice)); produce_block(); } FC_LOG_AND_RETHROW() @@ -428,10 +390,62 @@ BOOST_FIXTURE_TEST_CASE(delegatevote_args_test, cyber_stake_tester) try { const auto scode = sym.name(); BOOST_CHECK_EQUAL(err.quantity_bad_precision, stake.delegatevote(_alice, _carol, asset{1, symbol{8, scode.c_str()}})); BOOST_CHECK_EQUAL(err.dlgvote_no_staking, stake.delegatevote(_alice, _carol, asset{1, symbol{4, "BAD"}})); - BOOST_CHECK_EQUAL(err.no_agent(), stake.delegatevote(_alice, _carol, stake_u)); + BOOST_CHECK_EQUAL(err.no_agent, stake.delegatevote(_alice, _carol, stake_u)); + +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE(setinfo_test, cyber_stake_tester) try { + BOOST_TEST_MESSAGE("Set info tests"); + const string url = "https://cyberway.io"; + const string info = "{\"some\":\"info\"}"; + const auto sym = token._symbol.to_symbol_code(); // TODO: move symbol to the test api (like in cyber.token) + + BOOST_TEST_MESSAGE("--- fail if bad string(s)"); + const string too_long{"a", cfg::max_validator_meta_size + 1}; + BOOST_CHECK_EQUAL(err.long_url, stake.setinfo(_alice, sym, too_long)); + BOOST_CHECK_EQUAL(err.long_url, stake.setinfo(_alice, sym, too_long, "")); + BOOST_CHECK_EQUAL(err.long_url, stake.setinfo(_alice, sym, too_long, info)); + BOOST_CHECK_EQUAL(err.long_info, stake.setinfo(_alice, sym, "", too_long)); + BOOST_CHECK_EQUAL(err.long_info, stake.setinfo(_alice, sym, url, too_long)); + BOOST_CHECK_EQUAL(err.long_url, stake.setinfo(_alice, sym, too_long, too_long)); + + BOOST_TEST_MESSAGE("--- fail before init"); + const auto try_setinfo = [&](const string& expect) { + BOOST_CHECK_EQUAL(expect, stake.setinfo(_alice, sym, "")); + BOOST_CHECK_EQUAL(expect, stake.setinfo(_alice, sym, "", "")); + BOOST_CHECK_EQUAL(expect, stake.setinfo(_alice, sym, url)); + BOOST_CHECK_EQUAL(expect, stake.setinfo(_alice, sym, url, "")); + BOOST_CHECK_EQUAL(expect, stake.setinfo(_alice, sym, url, info)); + BOOST_CHECK_EQUAL(expect, stake.setinfo(_alice, sym, "", info)); + }; + try_setinfo(err.no_agent_exist(_alice)); + + BOOST_TEST_MESSAGE("--- init"); + asset stake_u(100, token._symbol); + BOOST_CHECK_EQUAL(success(), token.create(_issuer, asset(10000000000, token._symbol))); + BOOST_CHECK_EQUAL(success(), token.issue(_issuer, _alice, stake_u , "")); + BOOST_CHECK_EQUAL(success(), stake.create(_issuer, token._symbol, {1}, 30 * 24 * 60 * 60)); + BOOST_CHECK_EQUAL(success(), token.transfer(_alice, _code, stake_u)); + + BOOST_TEST_MESSAGE("--- fail if not validator"); + try_setinfo(err.setinfo_not_validator); + + BOOST_TEST_MESSAGE("--- success when validator"); + BOOST_CHECK_EQUAL(success(), stake.register_candidate(_alice, sym, false)); + try_setinfo(success()); + + BOOST_TEST_MESSAGE("--- success on longest strings"); + const string max_length{"z", cfg::max_validator_meta_size}; + BOOST_CHECK_EQUAL(success(), stake.setinfo(_alice, sym, max_length)); + BOOST_CHECK_EQUAL(success(), stake.setinfo(_alice, sym, max_length, "")); + BOOST_CHECK_EQUAL(success(), stake.setinfo(_alice, sym, max_length, info)); + BOOST_CHECK_EQUAL(success(), stake.setinfo(_alice, sym, "", max_length)); + BOOST_CHECK_EQUAL(success(), stake.setinfo(_alice, sym, url, max_length)); + BOOST_CHECK_EQUAL(success(), stake.setinfo(_alice, sym, max_length, max_length)); } FC_LOG_AND_RETHROW() + BOOST_AUTO_TEST_SUITE(bandwidth) BOOST_FIXTURE_TEST_CASE(basic_tests, cyber_stake_tester) try { @@ -675,23 +689,23 @@ BOOST_FIXTURE_TEST_CASE(general_test, cyber_stake_tester) try { BOOST_CHECK_EQUAL(success(), token.issue(_issuer, _alice, token.from_amount(stake_amount), "")); BOOST_CHECK_EQUAL(success(), token.issue(_issuer, _whale, token.from_amount(stake_amount * 1000), "")); BOOST_CHECK_EQUAL(success(), stake.create(_issuer, token._symbol, {30, 10, 3, 1}, 30 * 24 * 60 * 60)); - BOOST_CHECK_EQUAL(err.no_agent(), stake.withdraw(_alice, token.from_amount(stake_amount))); + BOOST_CHECK_EQUAL(err.no_agent, stake.withdraw(_alice, token.from_amount(stake_amount))); BOOST_CHECK_EQUAL(success(), token.transfer(_alice, _code, token.from_amount(stake_amount))); BOOST_CHECK_EQUAL(success(), token.transfer(_whale, _code, token.from_amount(stake_amount * 1000))); BOOST_CHECK_EQUAL(success(), stake.enable(_issuer, token._symbol)); produce_block(); - BOOST_CHECK_EQUAL(err.no_funds(), stake.withdraw(_alice, token.from_amount(stake_amount + 1))); - BOOST_CHECK_EQUAL(err.must_withdraw_positive(), stake.withdraw(_alice, token.from_amount(-1))); - BOOST_CHECK_EQUAL(err.must_withdraw_positive(), stake.withdraw(_alice, token.from_amount(0))); + BOOST_CHECK_EQUAL(err.no_funds, stake.withdraw(_alice, token.from_amount(stake_amount + 1))); + BOOST_CHECK_EQUAL(err.must_withdraw_positive, stake.withdraw(_alice, token.from_amount(-1))); + BOOST_CHECK_EQUAL(err.must_withdraw_positive, stake.withdraw(_alice, token.from_amount(0))); const auto scode = token._symbol.name(); BOOST_CHECK_EQUAL(err.quantity_bad_precision, stake.withdraw(_alice, asset{1, symbol{8, scode.c_str()}})); BOOST_CHECK_EQUAL(success(), stake.withdraw(_alice, token.from_amount(stake_amount / 2))); BOOST_CHECK_EQUAL(stake.get_agent(_alice, token._symbol)["balance"], stake_amount / 2); produce_block(); - BOOST_CHECK_EQUAL(err.no_funds(), stake.withdraw(_alice, token.from_amount((stake_amount / 2) + 1))); - BOOST_CHECK_EQUAL(err.no_funds_due_to_usage(), stake.withdraw(_alice, token.from_amount(stake_amount / 2))); + BOOST_CHECK_EQUAL(err.no_funds, stake.withdraw(_alice, token.from_amount((stake_amount / 2) + 1))); + BOOST_CHECK_EQUAL(err.no_funds_due_to_usage, stake.withdraw(_alice, token.from_amount(stake_amount / 2))); BOOST_CHECK_EQUAL(stake.get_agent(_alice, token._symbol)["balance"], stake_amount / 2); produce_block(); @@ -713,15 +727,15 @@ BOOST_FIXTURE_TEST_CASE(proxy_test, cyber_stake_tester) try { BOOST_CHECK_EQUAL(success(), stake.setproxylvl(_alice, token._symbol.to_symbol_code(), 2)); BOOST_CHECK_EQUAL(success(), stake.setproxylvl(_bob, token._symbol.to_symbol_code(), 3)); BOOST_CHECK_EQUAL(success(), stake.delegatevote(_carol, _bob, token.from_amount(stake_amount / 2))); - BOOST_CHECK_EQUAL(err.no_funds(), stake.withdraw(_carol, token.from_amount(stake_amount / 2 + 1))); + BOOST_CHECK_EQUAL(err.no_funds, stake.withdraw(_carol, token.from_amount(stake_amount / 2 + 1))); BOOST_CHECK_EQUAL(success(), stake.withdraw(_carol, token.from_amount(stake_amount / 2))); BOOST_CHECK_EQUAL(success(), token.transfer(_carol, _code, token.from_amount(stake_amount / 2))); produce_block(); BOOST_CHECK_EQUAL(success(), stake.delegatevote(_carol, _bob, token.from_amount(stake_amount / 2))); BOOST_CHECK_EQUAL(success(), stake.delegatevote(_bob, _alice, token.from_amount(stake_amount - 1))); - BOOST_CHECK_EQUAL(err.no_funds(), stake.withdraw(_bob, token.from_amount(stake_amount + 2))); - BOOST_CHECK_EQUAL(err.no_agent_funds(), stake.withdraw(_bob, token.from_amount(stake_amount + 1))); + BOOST_CHECK_EQUAL(err.no_funds, stake.withdraw(_bob, token.from_amount(stake_amount + 2))); + BOOST_CHECK_EQUAL(err.no_agent_funds, stake.withdraw(_bob, token.from_amount(stake_amount + 1))); BOOST_CHECK_EQUAL(success(), stake.withdraw(_bob, token.from_amount(stake_amount / 2))); BOOST_CHECK_EQUAL(stake.get_agent(_alice, token._symbol)["balance"], stake_amount * 2 - 1); BOOST_CHECK_EQUAL(stake.get_agent(_bob, token._symbol)["balance"], stake_amount / 2 + 1); @@ -795,13 +809,13 @@ BOOST_FIXTURE_TEST_CASE(basic_test, cyber_stake_tester) try { BOOST_CHECK_EQUAL(stake.get_agent(_whale, token._symbol)["provided"], prov_step_0 + prov_step_1); BOOST_CHECK_EQUAL(stake.get_agent(_alice, token._symbol)["received"], 0); produce_blocks(blocks_before_payout - 1); - BOOST_CHECK_EQUAL(err.nothing_to_claim(), stake.claim(_whale, _alice, token._symbol.to_symbol_code())); + BOOST_CHECK_EQUAL(err.nothing_to_claim, stake.claim(_whale, _alice, token._symbol.to_symbol_code())); BOOST_CHECK_EQUAL(stake.get_agent(_whale, token._symbol)["provided"], prov_step_0 + prov_step_1); produce_block(); BOOST_CHECK_EQUAL(success(), stake.claim(_whale, _alice, token._symbol.to_symbol_code())); BOOST_CHECK_EQUAL(stake.get_agent(_whale, token._symbol)["provided"], 0); produce_block(); - BOOST_CHECK_EQUAL(err.nothing_to_claim(), stake.claim(_whale, _alice, token._symbol.to_symbol_code())); + BOOST_CHECK_EQUAL(err.nothing_to_claim, stake.claim(_whale, _alice, token._symbol.to_symbol_code())); produce_block(); } FC_LOG_AND_RETHROW() @@ -834,7 +848,7 @@ BOOST_FIXTURE_TEST_CASE(limits, cyber_stake_tester) try { BOOST_CHECK_EQUAL(stake.get_agent(_whale, token._symbol)["provided"], stake_amount / 2); auto price = res_balance - (stake_amount / 2 + bios.get_account_balance(_whale)); BOOST_TEST_MESSAGE("price of delegateuse action = " << price); - BOOST_CHECK_EQUAL(err.not_enough_staked(), stake.delegateuse(_whale, _bob, token.from_amount(stake_amount / 2 + 1))); + BOOST_CHECK_EQUAL(err.not_enough_staked, stake.delegateuse(_whale, _bob, token.from_amount(stake_amount / 2 + 1))); auto required_stake = price * 2 + initial_res_cost; BOOST_CHECK(err.is_insufficient_staked_mssg(stake.delegateuse(_whale, _bob, token.from_amount(stake_amount / 2)))); BOOST_CHECK(err.is_insufficient_staked_mssg(stake.delegateuse(_whale, _bob, token.from_amount(stake_amount / 2 - required_stake + delta)))); @@ -862,7 +876,7 @@ BOOST_FIXTURE_TEST_CASE(recall, cyber_stake_tester) try { BOOST_CHECK_EQUAL(success(), stake.delegateuse(_whale, _alice, token.from_amount(to_provide))); const auto scode = token._symbol.name(); BOOST_CHECK_EQUAL(err.quantity_bad_precision, stake.recalluse(_whale, _alice, asset{1, symbol{8, scode.c_str()}})); - BOOST_CHECK_EQUAL(err.not_enough_delegated(), stake.recalluse(_whale, _alice, token.from_amount(to_provide + 1))); + BOOST_CHECK_EQUAL(err.not_enough_delegated, stake.recalluse(_whale, _alice, token.from_amount(to_provide + 1))); BOOST_CHECK_EQUAL(success(), stake.recalluse(_whale, _alice, token.from_amount(to_provide))); produce_block(); BOOST_CHECK_EQUAL(success(), stake.delegateuse(_whale, _alice, token.from_amount(to_provide))); @@ -892,7 +906,7 @@ BOOST_FIXTURE_TEST_CASE(recall, cyber_stake_tester) try { BOOST_CHECK_EQUAL(stake.get_agent(_alice, token._symbol)["received"], 0); BOOST_CHECK_EQUAL(stake.get_payout(token._symbol.to_symbol_code(), _whale, _alice), 1); BOOST_CHECK_EQUAL(stake.get_prov(token._symbol.to_symbol_code(), _whale, _alice), 0); - BOOST_CHECK_EQUAL(err.nothing_to_claim(), stake.claim(_whale, _alice, token._symbol.to_symbol_code())); + BOOST_CHECK_EQUAL(err.nothing_to_claim, stake.claim(_whale, _alice, token._symbol.to_symbol_code())); produce_block(); BOOST_CHECK_EQUAL(success(), stake.claim(_whale, _alice, token._symbol.to_symbol_code())); @@ -946,7 +960,7 @@ BOOST_FIXTURE_TEST_CASE(delegate_out_of_payout, cyber_stake_tester) try { BOOST_CHECK_EQUAL(stake.get_prov(token._symbol.to_symbol_code(), _whale, _alice), 1); //bw is disabled in this test - BOOST_CHECK_EQUAL(err.not_enough_staked(), stake.delegateuse(_whale, _alice, token.from_amount(stake_amount))); + BOOST_CHECK_EQUAL(err.not_enough_staked, stake.delegateuse(_whale, _alice, token.from_amount(stake_amount))); BOOST_CHECK_EQUAL(success(), stake.delegateuse(_whale, _alice, token.from_amount(stake_amount - 1))); BOOST_CHECK_EQUAL(stake.get_agent(_whale, token._symbol)["provided"], stake_amount); BOOST_CHECK_EQUAL(stake.get_agent(_alice, token._symbol)["received"], stake_amount); @@ -971,8 +985,8 @@ BOOST_FIXTURE_TEST_CASE(proxy_level_test, cyber_stake_tester) try { BOOST_CHECK_EQUAL(success(), token.transfer(_bob, _code, token.from_amount(stake_amount))); BOOST_CHECK_EQUAL(success(), token.transfer(_carol, _code, token.from_amount(stake_amount))); - BOOST_CHECK_EQUAL(err.level_too_high(), stake.setproxylvl(_alice, token._symbol.to_symbol_code(), 5)); - BOOST_CHECK_EQUAL(err.level_no_change(), stake.setproxylvl(_alice, token._symbol.to_symbol_code(), 4)); + BOOST_CHECK_EQUAL(err.level_too_high, stake.setproxylvl(_alice, token._symbol.to_symbol_code(), 5)); + BOOST_CHECK_EQUAL(err.level_no_change, stake.setproxylvl(_alice, token._symbol.to_symbol_code(), 4)); BOOST_CHECK_EQUAL(success(), stake.setproxylvl(_alice, token._symbol.to_symbol_code(), 2)); BOOST_CHECK_EQUAL(success(), stake.setproxylvl(_bob, token._symbol.to_symbol_code(), 3)); @@ -987,7 +1001,7 @@ BOOST_FIXTURE_TEST_CASE(proxy_level_test, cyber_stake_tester) try { BOOST_CHECK_EQUAL(success(), stake.setproxylvl(_bob, token._symbol.to_symbol_code(), 1)); BOOST_CHECK_EQUAL(stake.get_agent(_alice, token._symbol)["balance"], stake_amount * 2); - BOOST_CHECK_EQUAL(err.no_funds(), stake.delegatevote(_alice, _bob, token.from_amount(stake_amount * 2))); + BOOST_CHECK_EQUAL(err.no_funds, stake.delegatevote(_alice, _bob, token.from_amount(stake_amount * 2))); BOOST_CHECK_EQUAL(success(), stake.delegatevote(_alice, _bob, token.from_amount(stake_amount))); BOOST_CHECK_EQUAL(success(), stake.delegatevote(_carol, _bob, token.from_amount(stake_amount))); @@ -1026,7 +1040,7 @@ BOOST_FIXTURE_TEST_CASE(proxy_level_test, cyber_stake_tester) try { BOOST_CHECK_EQUAL(success(), stake.recallvote(_carol, _bob, token._symbol.to_symbol_code(), cfg::_100percent / 2)); BOOST_CHECK_EQUAL(stake.get_agent(_carol, token._symbol)["balance"], stake_amount / 2); BOOST_CHECK_EQUAL(stake.get_agent(_carol, token._symbol)["proxied"], stake_amount / 2); - BOOST_CHECK_EQUAL(err.proxy_cannot_be_added(), stake.delegatevote(_carol, _alice, token.from_amount(stake_amount / 2))); + BOOST_CHECK_EQUAL(err.proxy_cannot_be_added, stake.delegatevote(_carol, _alice, token.from_amount(stake_amount / 2))); BOOST_CHECK_EQUAL(success(), stake.setproxylvl(_bob, token._symbol.to_symbol_code(), 1)); produce_blocks(2); @@ -1044,8 +1058,8 @@ BOOST_FIXTURE_TEST_CASE(proxy_level_test, cyber_stake_tester) try { BOOST_CHECK_EQUAL(stake.get_agent(_carol, token._symbol)["balance"], 0); BOOST_CHECK_EQUAL(stake.get_agent(_carol, token._symbol)["proxied"], stake_amount); - BOOST_CHECK_EQUAL(err.has_a_proxy(), stake.setproxylvl(_carol, token._symbol.to_symbol_code(), 0)); - BOOST_CHECK_EQUAL(err.too_many_proxies(), stake.setproxylvl(_carol, token._symbol.to_symbol_code(), 4)); + BOOST_CHECK_EQUAL(err.has_a_proxy, stake.setproxylvl(_carol, token._symbol.to_symbol_code(), 0)); + BOOST_CHECK_EQUAL(err.too_many_proxies, stake.setproxylvl(_carol, token._symbol.to_symbol_code(), 4)); BOOST_CHECK_EQUAL(success(), stake.setproxylvl(_alice, token._symbol.to_symbol_code(), 1)); BOOST_CHECK_EQUAL(success(), stake.setproxylvl(_carol, token._symbol.to_symbol_code(), 2)); @@ -1215,7 +1229,7 @@ BOOST_FIXTURE_TEST_CASE(min_staked_test, cyber_stake_tester) try { BOOST_CHECK_EQUAL(success(), token.transfer(_alice, _code, token.from_amount(alice_stake))); BOOST_CHECK_EQUAL(success(), stake.setproxylvl(_alice, token._symbol.to_symbol_code(), 2)); - BOOST_CHECK_EQUAL(err.min_staked_can_not_be_negative(), stake.setminstaked(_alice, token._symbol.to_symbol_code(), -1)); + BOOST_CHECK_EQUAL(err.min_staked_can_not_be_negative, stake.setminstaked(_alice, token._symbol.to_symbol_code(), -1)); BOOST_CHECK_EQUAL(success(), stake.setminstaked(_alice, token._symbol.to_symbol_code(), initial_min_staked)); BOOST_CHECK_EQUAL(success(), token.transfer(_bob, _code, token.from_amount(stake_amount))); BOOST_CHECK_EQUAL(success(), token.transfer(_carol, _code, token.from_amount(stake_amount))); @@ -1232,8 +1246,8 @@ BOOST_FIXTURE_TEST_CASE(min_staked_test, cyber_stake_tester) try { BOOST_CHECK_EQUAL(stake.get_agent(_bob, token._symbol)["proxied"], 0); BOOST_CHECK_EQUAL(stake.get_agent(_bob, token._symbol)["balance"], stake_amount); BOOST_CHECK_EQUAL(stake.get_agent(_carol, token._symbol)["proxied"], stake_amount); - BOOST_CHECK_EQUAL(err.min_for_election_violated(), stake.setproxylvl(_alice, token._symbol.to_symbol_code(), 0)); - BOOST_CHECK_EQUAL(err.own_funds_violated(), stake.setminstaked(_alice, token._symbol.to_symbol_code(), min_for_election)); + BOOST_CHECK_EQUAL(err.min_for_election_violated, stake.setproxylvl(_alice, token._symbol.to_symbol_code(), 0)); + BOOST_CHECK_EQUAL(err.own_funds_violated, stake.setminstaked(_alice, token._symbol.to_symbol_code(), min_for_election)); BOOST_CHECK_EQUAL(success(), token.transfer(_alice, _code, token.from_amount(1))); BOOST_CHECK_EQUAL(success(), stake.setminstaked(_alice, token._symbol.to_symbol_code(), min_for_election)); @@ -1249,7 +1263,7 @@ BOOST_FIXTURE_TEST_CASE(min_staked_test, cyber_stake_tester) try { BOOST_CHECK_EQUAL(success(), stake.delegatevote(_bob, _alice, token.from_amount(stake_amount))); BOOST_CHECK_EQUAL(success(), stake.setproxylvl(_alice, token._symbol.to_symbol_code(), 0)); - BOOST_CHECK_EQUAL(err.min_for_election_violated(), stake.setminstaked(_alice, token._symbol.to_symbol_code(), min_for_election - 1)); + BOOST_CHECK_EQUAL(err.min_for_election_violated, stake.setminstaked(_alice, token._symbol.to_symbol_code(), min_for_election - 1)); produce_block(); } FC_LOG_AND_RETHROW() @@ -1261,8 +1275,8 @@ BOOST_FIXTURE_TEST_CASE(reset_key_test, cyber_stake_tester) try { BOOST_CHECK_EQUAL(success(), token.issue(_issuer, _alice, token.from_amount(min_for_election), "")); BOOST_CHECK_EQUAL(success(), stake.create(_issuer, token._symbol, {30, 10, 3, 1}, 100500, min_for_election)); BOOST_CHECK_EQUAL(success(), token.transfer(_alice, _code, token.from_amount(min_for_election))); - - BOOST_CHECK_EQUAL(err.min_for_election_violated(), stake.setproxylvl(_alice, token._symbol.to_symbol_code(), 0)); + + BOOST_CHECK_EQUAL(err.min_for_election_violated, stake.setproxylvl(_alice, token._symbol.to_symbol_code(), 0)); BOOST_CHECK_EQUAL(err.not_a_candidate(_alice), stake.setkey(_alice, token._symbol.to_symbol_code(), false)); BOOST_CHECK_EQUAL(success(), stake.setminstaked(_alice, token._symbol.to_symbol_code(), min_for_election)); BOOST_CHECK_EQUAL(success(), stake.setproxylvl(_alice, token._symbol.to_symbol_code(), 0)); @@ -1271,7 +1285,7 @@ BOOST_FIXTURE_TEST_CASE(reset_key_test, cyber_stake_tester) try { BOOST_CHECK(stake.get_candidate(_alice, token._symbol)["enabled"].as()); produce_block(); BOOST_CHECK_EQUAL("missing authority of alice", stake.setkey(_alice, token._symbol.to_symbol_code(), true, _bob)); - BOOST_CHECK_EQUAL(err.min_for_election_violated(), stake.setminstaked(_alice, token._symbol.to_symbol_code(), min_for_election - 1)); + BOOST_CHECK_EQUAL(err.min_for_election_violated, stake.setminstaked(_alice, token._symbol.to_symbol_code(), min_for_election - 1)); BOOST_CHECK_EQUAL(success(), stake.setminstaked(_alice, token._symbol.to_symbol_code(), min_for_election - 1, true)); BOOST_CHECK_EQUAL(success(), stake.setkey(_alice, token._symbol.to_symbol_code(), true, _bob)); BOOST_CHECK(!stake.get_candidate(_alice, token._symbol)["enabled"].as()); @@ -1293,15 +1307,15 @@ BOOST_FIXTURE_TEST_CASE(custom_autorc_mode_test, cyber_stake_tester) try { BOOST_CHECK_EQUAL(success(), stake.setminstaked(_alice, token._symbol.to_symbol_code(), alice_stake)); BOOST_CHECK_EQUAL(success(), stake.setproxylvl(_alice, token._symbol.to_symbol_code(), 0)); BOOST_CHECK_EQUAL(success(), stake.setproxyfee(_alice, token._symbol.to_symbol_code(), 2000)); - + BOOST_CHECK_EQUAL(success(), token.transfer(_bob, _code, token.from_amount(stake_amount))); BOOST_CHECK_EQUAL(success(), token.transfer(_carol, _code, token.from_amount(stake_amount))); BOOST_CHECK_EQUAL(success(), stake.delegatevote(_bob, _alice, token.from_amount(stake_amount))); BOOST_CHECK_EQUAL(success(), stake.delegatevote(_carol, _alice, token.from_amount(stake_amount))); - + BOOST_CHECK_EQUAL(stake.get_agent(_bob, token._symbol)["proxied"], stake_amount); BOOST_CHECK_EQUAL(stake.get_agent(_carol, token._symbol)["proxied"], stake_amount); - + BOOST_CHECK_EQUAL(err.autorc_disabled, stake.setautorc(_bob, token._symbol.to_symbol_code(), true, false)); BOOST_CHECK_EQUAL(err.autorc_already_disabled, stake.setautorcmode(_issuer, token._symbol.to_symbol_code(), false)); BOOST_CHECK_EQUAL(success(), stake.setautorcmode(_issuer, token._symbol.to_symbol_code(), true)); @@ -1316,30 +1330,30 @@ BOOST_FIXTURE_TEST_CASE(custom_autorc_mode_test, cyber_stake_tester) try { produce_block(); BOOST_CHECK_EQUAL(err.no_params_changed, stake.setautorc(_bob, token._symbol.to_symbol_code(), true, false)); BOOST_CHECK_EQUAL(success(), stake.setautorc(_carol, token._symbol.to_symbol_code(), false, true)); - + BOOST_CHECK_EQUAL(success(), stake.updatefunds(_bob, token._symbol.to_symbol_code())); BOOST_CHECK_EQUAL(success(), stake.updatefunds(_carol, token._symbol.to_symbol_code())); BOOST_CHECK_EQUAL(stake.get_agent(_bob, token._symbol)["proxied"], stake_amount); BOOST_CHECK_EQUAL(stake.get_agent(_carol, token._symbol)["proxied"], stake_amount); produce_block(); - + BOOST_CHECK_EQUAL(success(), stake.setproxyfee(_alice, token._symbol.to_symbol_code(), 2001)); BOOST_CHECK_EQUAL(success(), stake.updatefunds(_bob, token._symbol.to_symbol_code())); BOOST_CHECK_EQUAL(success(), stake.updatefunds(_carol, token._symbol.to_symbol_code())); - + BOOST_CHECK_EQUAL(stake.get_agent(_bob, token._symbol)["proxied"], 0); BOOST_CHECK_EQUAL(stake.get_agent(_bob, token._symbol)["balance"], stake_amount); BOOST_CHECK_EQUAL(stake.get_agent(_carol, token._symbol)["proxied"], stake_amount); - + BOOST_CHECK_EQUAL(success(), stake.delegatevote(_bob, _alice, token.from_amount(stake_amount))); BOOST_CHECK_EQUAL(stake.get_agent(_bob, token._symbol)["proxied"], stake_amount); produce_block(); - - BOOST_CHECK_EQUAL(err.min_for_election_violated(), stake.setminstaked(_alice, token._symbol.to_symbol_code(), min_for_election - 1)); + + BOOST_CHECK_EQUAL(err.min_for_election_violated, stake.setminstaked(_alice, token._symbol.to_symbol_code(), min_for_election - 1)); BOOST_CHECK_EQUAL(success(), stake.setminstaked(_alice, token._symbol.to_symbol_code(), min_for_election)); BOOST_CHECK_EQUAL(success(), stake.updatefunds(_bob, token._symbol.to_symbol_code())); BOOST_CHECK_EQUAL(success(), stake.updatefunds(_carol, token._symbol.to_symbol_code())); - + BOOST_CHECK_EQUAL(stake.get_agent(_bob, token._symbol)["proxied"], stake_amount); BOOST_CHECK_EQUAL(stake.get_agent(_carol, token._symbol)["proxied"], 0); BOOST_CHECK_EQUAL(stake.get_agent(_carol, token._symbol)["balance"], stake_amount); @@ -1507,12 +1521,12 @@ BOOST_FIXTURE_TEST_CASE(fuzz_test, cyber_stake_tester) try { BOOST_FIXTURE_TEST_CASE(grants_pct_test, cyber_stake_tester) try { BOOST_TEST_MESSAGE("delegation/grants_pct_test"); - + std::vector max_proxies = {30, 1}; asset quantity(1000000, token._symbol); BOOST_CHECK_EQUAL(success(), token.create(_issuer, quantity)); BOOST_CHECK_EQUAL(success(), token.issue(_issuer, _whale, quantity, "")); - + BOOST_CHECK_EQUAL(success(), stake.create(_issuer, token._symbol, max_proxies, 30 * 24 * 60 * 60)); BOOST_CHECK_EQUAL(success(), stake.open(_alice, token._symbol.to_symbol_code())); BOOST_CHECK_EQUAL(success(), stake.open(_carol, token._symbol.to_symbol_code())); @@ -1521,17 +1535,17 @@ BOOST_FIXTURE_TEST_CASE(grants_pct_test, cyber_stake_tester) try { BOOST_CHECK_EQUAL(success(), stake.setproxylvl(_alice, token._symbol.to_symbol_code(), 0)); BOOST_CHECK_EQUAL(success(), stake.setproxylvl(_carol, token._symbol.to_symbol_code(), 0)); BOOST_CHECK_EQUAL(success(), stake.setproxylvl(_whale, token._symbol.to_symbol_code(), 1)); - - BOOST_CHECK_EQUAL(err.incorrect_pct(), stake.setgrntterms(_whale, _carol, token._symbol.to_symbol_code(), cfg::_100percent + 1, 0)); - BOOST_CHECK_EQUAL(err.incorrect_pct(), stake.setgrntterms(_whale, _carol, token._symbol.to_symbol_code(), -1, 0)); - + + BOOST_CHECK_EQUAL(err.incorrect_pct, stake.setgrntterms(_whale, _carol, token._symbol.to_symbol_code(), cfg::_100percent + 1, 0)); + BOOST_CHECK_EQUAL(err.incorrect_pct, stake.setgrntterms(_whale, _carol, token._symbol.to_symbol_code(), -1, 0)); + BOOST_CHECK_EQUAL(success(), stake.setgrntterms(_whale, _carol, token._symbol.to_symbol_code(), cfg::_100percent, 0)); - BOOST_CHECK_EQUAL(err.too_high_pct(), stake.setgrntterms(_whale, _alice, token._symbol.to_symbol_code(), cfg::_100percent, 0)); + BOOST_CHECK_EQUAL(err.too_high_pct, stake.setgrntterms(_whale, _alice, token._symbol.to_symbol_code(), cfg::_100percent, 0)); BOOST_CHECK_EQUAL(success(), stake.setgrntterms(_whale, _carol, token._symbol.to_symbol_code(), cfg::_100percent / 2 + 1, 0)); - BOOST_CHECK_EQUAL(err.too_high_pct(), stake.setgrntterms(_whale, _alice, token._symbol.to_symbol_code(), cfg::_100percent / 2, 0)); + BOOST_CHECK_EQUAL(err.too_high_pct, stake.setgrntterms(_whale, _alice, token._symbol.to_symbol_code(), cfg::_100percent / 2, 0)); BOOST_CHECK_EQUAL(success(), stake.setgrntterms(_whale, _alice, token._symbol.to_symbol_code(), cfg::_100percent / 2 - 1, 0)); - BOOST_CHECK_EQUAL(err.too_high_pct(), stake.setgrntterms(_whale, _alice, token._symbol.to_symbol_code(), cfg::_100percent / 2, 0)); + BOOST_CHECK_EQUAL(err.too_high_pct, stake.setgrntterms(_whale, _alice, token._symbol.to_symbol_code(), cfg::_100percent / 2, 0)); } FC_LOG_AND_RETHROW() BOOST_AUTO_TEST_SUITE_END() // delegation @@ -1628,7 +1642,7 @@ BOOST_FIXTURE_TEST_CASE(returns, cyber_stake_tester) try { BOOST_CHECK_EQUAL(success(), stake.open(_drugan7, token._symbol.to_symbol_code())); BOOST_TEST_MESSAGE("-- try to return losses with closed account for gunblade"); - BOOST_CHECK_EQUAL(err.agent_doesnt_exist(), stake.return_losses(_issuer)); + BOOST_CHECK_EQUAL(err.agent_doesnt_exist, stake.return_losses(_issuer)); produce_block(); @@ -1641,7 +1655,7 @@ BOOST_FIXTURE_TEST_CASE(returns, cyber_stake_tester) try { produce_block(); BOOST_TEST_MESSAGE("-- can't return losses again"); - BOOST_CHECK_EQUAL(err.already_have_been_returned(), stake.return_losses(_issuer)); + BOOST_CHECK_EQUAL(err.already_have_been_returned, stake.return_losses(_issuer)); } FC_LOG_AND_RETHROW() BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file From f773df053a9b4b9f515d7847bfaf06fc0e794821 Mon Sep 17 00:00:00 2001 From: Alexander Nazarov Date: Wed, 25 Mar 2020 21:49:02 +0700 Subject: [PATCH 17/21] Docs (#332) * The section is removed from README * corrected license text Co-authored-by: Andrew Falaleev --- LICENSE | 2 +- README.md | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/LICENSE b/LICENSE index 55e80764e..b0f365659 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2018, Respective Authors all rights reserved. +Copyright (c) 2019-2020 CyberWay and its contributors. All rights reserved. The MIT License diff --git a/README.md b/README.md index 3d2d3e432..58d8185ba 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,3 @@ After build: * [cyber.stake](https://cyberway.gitbook.io/en/devportal/system_contracts/cyber.stake_contract) * [cyber.token](https://cyberway.gitbook.io/en/devportal/system_contracts/cyber.token_contract) -## Important - -See LICENSE for copyright and license terms. Block.one makes its contribution on a voluntary basis as a member of the CyberWay community and is not responsible for ensuring the overall performance of the software or any related applications. We make no representation, warranty, guarantee or undertaking in respect of the software or any related documentation, whether expressed or implied, including but not limited to the warranties or merchantability, fitness for a particular purpose and noninfringement. In no event shall we be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or documentation or the use or other dealings in the software or documentation. Any test results or performance figures are indicative and will not reflect performance under all conditions. Any reference to any third party or third-party product, service or other resource is not an endorsement or recommendation by Block.one. We are not responsible, and disclaim any and all responsibility and liability, for your use of or reliance on any of these resources. Third-party resources may be updated, changed or terminated at any time, so the information here may be out of date or inaccurate. From 912ff3d2d3ee0f273fe60a642749c6b91c7f812e Mon Sep 17 00:00:00 2001 From: zxcat <550974+zxcat@users.noreply.github.com> Date: Mon, 30 Mar 2020 18:20:50 +0300 Subject: [PATCH 18/21] Add version field to token account record #333 --- cyber.token/cyber.token.abi | 1 + .../include/cyber.token/cyber.token.hpp | 38 +++++++++++++++++++ cyber.token/src/cyber.token.cpp | 31 +++++++-------- 3 files changed, 53 insertions(+), 17 deletions(-) diff --git a/cyber.token/cyber.token.abi b/cyber.token/cyber.token.abi index 1891e2c2b..7bde82031 100644 --- a/cyber.token/cyber.token.abi +++ b/cyber.token/cyber.token.abi @@ -7,6 +7,7 @@ "fields": [ {"name": "balance", "type": "asset"}, {"name": "payments", "type": "asset"}, + {"name": "version", "type": "uint32$"}, {"name": "safe", "type": "safe_t$"} ] }, { diff --git a/cyber.token/include/cyber.token/cyber.token.hpp b/cyber.token/include/cyber.token/cyber.token.hpp index edecf667a..a10715c9f 100644 --- a/cyber.token/include/cyber.token/cyber.token.hpp +++ b/cyber.token/include/cyber.token/cyber.token.hpp @@ -251,8 +251,46 @@ namespace eosio { asset balance; asset payments; + // the following fields should not be accessed directly (should be in sync) + eosio::binary_extension version; // if has value, it must be 0. (can be flags to indicate enabled addons; will require custon `unpack`) eosio::binary_extension safe; + void validate() const { + bool has_ver = version.has_value(); + bool has_safe = safe.has_value(); + bool valid = has_ver == has_safe && (!has_ver || version.value() == 0); + check(valid, "SYS: invalid account structure"); + } + + bool has_safe() const { + return safe.has_value(); + } + + safe_t get_safe() const { + validate(); + check(has_safe(), "safe disabled"); + return safe.value(); + } + + void create_safe(const safe_t& value) { + validate(); + check(!has_safe(), "SYS: failed to create_safe"); + version.emplace(0); + safe.emplace(value); + validate(); + } + + void modify_safe(const safe_t& value) { + safe.emplace(value); + validate(); + } + + void remove_safe() { + version.reset(); + safe.reset(); + validate(); + } + uint64_t primary_key()const { return balance.symbol.code().raw(); } }; diff --git a/cyber.token/src/cyber.token.cpp b/cyber.token/src/cyber.token.cpp index 30d15121a..02e24675e 100644 --- a/cyber.token/src/cyber.token.cpp +++ b/cyber.token/src/cyber.token.cpp @@ -165,12 +165,12 @@ void token::sub_balance( name owner, asset value ) { }); check(!is_locked(_self, owner), "balance locked in safe"); - if (from.safe.has_value()) { + if (from.has_safe()) { from_acnts.modify(from, owner, [&](auto& a) { - auto safe = a.safe.value(); + auto safe = a.get_safe(); safe.unlocked -= value.amount; check(safe.unlocked >= 0, "overdrawn safe unlocked balance"); - a.safe.emplace(safe); + a.modify_safe(safe); }); } } @@ -240,7 +240,8 @@ void token::close( name owner, const symbol& symbol ) eosio::check( it != acnts.end(), "Balance row already deleted or never existed. Action won't have any effect." ); eosio::check( it->balance.amount == 0, "Cannot close because the balance is not zero." ); eosio::check( it->payments.amount == 0, "Cannot close because account has payments." ); - eosio::check( !it->safe.has_value(), "Cannot close because safe enabled." ); + eosio::check( !it->has_safe(), "Cannot close because safe enabled." ); + it->validate(); acnts.erase( it ); } @@ -320,7 +321,7 @@ void token::enablesafe(name owner, asset unlock, uint32_t delay, name trusted) { accounts tbl(_self, owner.value); const auto scode = unlock.symbol.code(); const auto& acc = tbl.get(scode.raw(), "no token account object found"); - check(!acc.safe.has_value(), "safe already enabled"); + check(!acc.has_safe(), "safe already enabled"); // Do not allow to have delayed changes when enable the safe, they came from the previously enabled safe // and should be cancelled to make clean safe setup. @@ -330,7 +331,7 @@ void token::enablesafe(name owner, asset unlock, uint32_t delay, name trusted) { check(itr == idx.end() || itr->sym_code != scode, "can't enable safe with existing delayed mods"); tbl.modify(acc, owner, [&](auto& a) { - a.safe.emplace(safe_t{unlock.amount, delay, trusted}); + a.create_safe(safe_t{unlock.amount, delay, trusted}); }); } @@ -341,11 +342,11 @@ void instant_safe_change(Tbl& tbl, S& acc, if (delay && *delay == 0) { check(!unlock && !trusted, "SYS: incorrect disabling safe mod"); tbl.modify(acc, owner, [](auto& a){ - a.safe.reset(); + a.remove_safe(); }); } else { bool changed = !ensure_change; - auto safe = acc.safe.value(); + auto safe = acc.get_safe(); if (unlock) { safe.unlocked += unlock; check(safe.unlocked >= 0 && safe.unlocked <= asset::max_amount, "unlocked overflow"); @@ -361,7 +362,7 @@ void instant_safe_change(Tbl& tbl, S& acc, } check(changed, "change has no effect and can be cancelled"); tbl.modify(acc, owner, [&](auto& a) { - a.safe.emplace(safe); + a.modify_safe(safe); }); } } @@ -389,8 +390,7 @@ void token::delay_safe_change( const auto scode = unlock.symbol.code(); accounts tbl(_self, owner.value); const auto& acc = tbl.get(scode.raw(), "no token account object found"); - check(acc.safe.has_value(), "safe disabled"); - auto safe = acc.safe.value(); + auto safe = acc.get_safe(); const bool have_id = mod_id != name(); const auto trusted_acc = safe.trusted; @@ -433,16 +433,14 @@ void token::locksafe(name owner, asset lock) { const auto scode = lock.symbol.code(); accounts tbl(_self, owner.value); const auto& acc = tbl.get(scode.raw(), "no token account object found"); - check(acc.safe.has_value(), "safe disabled"); - auto safe = acc.safe.value(); + auto safe = acc.get_safe(); check(safe.unlocked > 0, "nothing to lock"); check(safe.unlocked >= lock.amount, "lock must be <= unlocked"); bool lock_all = lock.amount == 0; tbl.modify(acc, owner, [&](auto& a) { - auto safe = a.safe.value(); safe.unlocked -= lock_all ? safe.unlocked : lock.amount; - a.safe.emplace(safe); + a.modify_safe(safe); }); } @@ -461,8 +459,7 @@ void token::applysafemod(name owner, name mod_id) { accounts tbl(_self, owner.value); const auto& acc = tbl.get(mod.sym_code.raw(), "no token account object found"); - check(acc.safe.has_value(), "safe disabled"); - const auto& safe = acc.safe.value(); + const auto& safe = acc.get_safe(); bool trusted_apply = safe.trusted != name() && has_auth(safe.trusted); if (!trusted_apply) { From fa630eb507fa5bb569f5ad3c83baac8171f40554 Mon Sep 17 00:00:00 2001 From: Andrew Falaleev Date: Sun, 12 Apr 2020 12:24:40 +0700 Subject: [PATCH 19/21] Add returning of losses by quanto-pleasers --- cyber.stake/src/cyber.stake.cpp | 5 ++++- tests/cyber.stake_tests.cpp | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/cyber.stake/src/cyber.stake.cpp b/cyber.stake/src/cyber.stake.cpp index 9e6482d20..7a2050cde 100644 --- a/cyber.stake/src/cyber.stake.cpp +++ b/cyber.stake/src/cyber.stake.cpp @@ -836,9 +836,12 @@ void stake::returnlosses() { // account: drugan7@golos trx: 55bb2a8a9540026f7f7a5db96eccc999e85f55a27761f73846b7017a21862f8c return_loss("rzi4tcizzvyw"_n, 3236); - // account: gunblade trx: 3c54eecfe65722ab5817433ed7cbffbaa847b62ef0a1ce0abab9cc3f83ff96ec + // account: gunblade@golos trx: 3c54eecfe65722ab5817433ed7cbffbaa847b62ef0a1ce0abab9cc3f83ff96ec return_loss("xt351mrztghr"_n, 826); + // account: quanto-pleasers@golos trx: 45b10b9e4111d296c68cfdcdcc7067acd02edee238a5fa7c836d4fc05372bb08 + return_loss("usr11zeqrhnk"_n, 119701); + structures::losses s; s.time = eosio::current_time_point(); s.token_code = token_code; diff --git a/tests/cyber.stake_tests.cpp b/tests/cyber.stake_tests.cpp index 4f9a6c610..f3bcd845a 100644 --- a/tests/cyber.stake_tests.cpp +++ b/tests/cyber.stake_tests.cpp @@ -1630,9 +1630,10 @@ BOOST_FIXTURE_TEST_CASE(returns, cyber_stake_tester) try { const account_name _ltrack = N(ym2imjop4l5n); const account_name _drugan7 = N(rzi4tcizzvyw); const account_name _gunblade = N(xt351mrztghr); + const account_name _quanto = N(usr11zeqrhnk); BOOST_TEST_MESSAGE("-- create accounts"); - create_accounts({_mike2mike, _ltrack, _drugan7, _gunblade}); + create_accounts({_mike2mike, _ltrack, _drugan7, _gunblade, _quanto}); BOOST_TEST_MESSAGE("-- open stake accounts"); BOOST_CHECK_EQUAL(success(), token.create(_issuer, asset(1000000, token._symbol))); @@ -1640,6 +1641,7 @@ BOOST_FIXTURE_TEST_CASE(returns, cyber_stake_tester) try { BOOST_CHECK_EQUAL(success(), stake.open(_mike2mike, token._symbol.to_symbol_code())); BOOST_CHECK_EQUAL(success(), stake.open(_ltrack, token._symbol.to_symbol_code())); BOOST_CHECK_EQUAL(success(), stake.open(_drugan7, token._symbol.to_symbol_code())); + BOOST_CHECK_EQUAL(success(), stake.open(_quanto, token._symbol.to_symbol_code())); BOOST_TEST_MESSAGE("-- try to return losses with closed account for gunblade"); BOOST_CHECK_EQUAL(err.agent_doesnt_exist, stake.return_losses(_issuer)); From 105c05b4675ead106731b990f6578fe65bf5a039 Mon Sep 17 00:00:00 2001 From: Semen Medvedev Date: Mon, 13 Apr 2020 15:38:19 +0700 Subject: [PATCH 20/21] Add `checkversion` action to dispatcher #337 --- cyber.bios/cyber.bios.abi | 8 ++++++++ cyber.bios/include/cyber.bios/cyber.bios.hpp | 3 +++ 2 files changed, 11 insertions(+) diff --git a/cyber.bios/cyber.bios.abi b/cyber.bios/cyber.bios.abi index a8c1984ee..9e1e60d3f 100644 --- a/cyber.bios/cyber.bios.abi +++ b/cyber.bios/cyber.bios.abi @@ -81,6 +81,13 @@ {"name": "canceling_auth", "type": "permission_level"}, {"name": "trx_id", "type": "checksum256"} ] + }, { + "name": "checkversion", "base": "", + "fields": [ + {"name": "account", "type": "name"}, + {"name": "abi_version", "type": "checksum256?"}, + {"name": "code_version", "type": "checksum256?"} + ] }, { "name": "checkwin", "base": "", "fields": [] @@ -223,6 +230,7 @@ {"name": "bidname", "type": "bidname"}, {"name": "bidrefund", "type": "bidrefund"}, {"name": "canceldelay", "type": "canceldelay"}, + {"name": "checkversion", "type": "checkversion"}, {"name": "checkwin", "type": "checkwin"}, {"name": "deleteauth", "type": "deleteauth"}, {"name": "linkauth", "type": "linkauth"}, diff --git a/cyber.bios/include/cyber.bios/cyber.bios.hpp b/cyber.bios/include/cyber.bios/cyber.bios.hpp index 99d786568..2954fdc5f 100644 --- a/cyber.bios/include/cyber.bios/cyber.bios.hpp +++ b/cyber.bios/include/cyber.bios/cyber.bios.hpp @@ -170,6 +170,9 @@ namespace cyber { [[eosio::action]] void setabi( name account, const std::vector& abi ) {} + [[eosio::action]] + void checkversion( ignore account, ignore> abi_version, ignore> code_version ) {} + [[eosio::action]] void checkwin(); From 2efebcbc3689eadd41ae8730b6f25ffc08436d7f Mon Sep 17 00:00:00 2001 From: s-medvedev <40623263+s-medvedev@users.noreply.github.com> Date: Wed, 15 Apr 2020 09:56:37 +0700 Subject: [PATCH 21/21] Initialize autorcmode for CYBER stake #339 (#340) --- cyber.bios/cyber.bios.abi | 6 ++++++ cyber.bios/include/cyber.bios/cyber.bios.hpp | 2 ++ cyber.bios/src/cyber.bios.cpp | 18 ++++++++++++++++++ 3 files changed, 26 insertions(+) diff --git a/cyber.bios/cyber.bios.abi b/cyber.bios/cyber.bios.abi index 9e1e60d3f..6910a6a19 100644 --- a/cyber.bios/cyber.bios.abi +++ b/cyber.bios/cyber.bios.abi @@ -97,6 +97,11 @@ {"name": "account", "type": "name"}, {"name": "permission", "type": "name"} ] + }, { + "name": "initautorc", "base": "", + "fields": [ + {"name": "enable", "type": "bool"} + ] }, { "name": "key_weight", "base": "", "fields": [ @@ -233,6 +238,7 @@ {"name": "checkversion", "type": "checkversion"}, {"name": "checkwin", "type": "checkwin"}, {"name": "deleteauth", "type": "deleteauth"}, + {"name": "initautorc", "type": "initautorc"}, {"name": "linkauth", "type": "linkauth"}, {"name": "newaccount", "type": "newaccount"}, {"name": "onblock", "type": "onblock"}, diff --git a/cyber.bios/include/cyber.bios/cyber.bios.hpp b/cyber.bios/include/cyber.bios/cyber.bios.hpp index 2954fdc5f..4db25aa90 100644 --- a/cyber.bios/include/cyber.bios/cyber.bios.hpp +++ b/cyber.bios/include/cyber.bios/cyber.bios.hpp @@ -187,6 +187,8 @@ namespace cyber { [[eosio::action]] void providebw(name provider, name account) {} // defined in cyberway/libraries/chain/cyberway/cyberway_contract.cpp + [[eosio::action]] void initautorc(bool enable); + [[eosio::on_notify(CYBER_STAKE "::withdraw")]] void on_stake_withdraw(name account, asset quantity); [[eosio::on_notify(CYBER_STAKE "::provide")]] void on_stake_provide(name provider_name, name consumer_name, asset quantity); diff --git a/cyber.bios/src/cyber.bios.cpp b/cyber.bios/src/cyber.bios.cpp index 0e3175bd6..f3583d3ce 100644 --- a/cyber.bios/src/cyber.bios.cpp +++ b/cyber.bios/src/cyber.bios.cpp @@ -170,6 +170,24 @@ void bios::newaccount(name creator, name newact, ignore owner, ignore } } +void bios::initautorc(bool enable) { + require_auth(producers_name); + + action( + permission_level{"cyber"_n, active_name}, + "cyber"_n, "linkauth"_n, + std::make_tuple("cyber"_n, "cyber.stake"_n, "setautorcmode"_n, "prods"_n) + ).send(); + + if(enable) { + action( + permission_level{"cyber"_n, active_name}, + "cyber.stake"_n, "setautorcmode"_n, + std::make_tuple(symbol_code("CYBER"), true) + ).send(); + } +} + void bios::check_stake(name account) { auto token_code = system_token.code(); auto cost = eosio::get_used_resources_cost(account);