Skip to content

Commit

Permalink
Posting: allow app to add/delete permlinks #893
Browse files Browse the repository at this point in the history
  • Loading branch information
zxcat committed Aug 28, 2019
1 parent 530bce4 commit ee7dce6
Show file tree
Hide file tree
Showing 7 changed files with 252 additions and 17 deletions.
49 changes: 34 additions & 15 deletions golos.publication/golos.publication.abi
Original file line number Diff line number Diff line change
Expand Up @@ -529,16 +529,6 @@
}
]
},
{
"name": "createacc",
"base": "",
"fields": [
{
"name": "name",
"type": "name"
}
]
},
{
"name": "setlimit",
"base": "",
Expand Down Expand Up @@ -872,6 +862,34 @@
}
]
},
{
"name": "addpermlink",
"base": "",
"fields": [
{ "type": "mssgid", "name": "msg" },
{ "type": "mssgid", "name": "parent" },
{ "type": "uint16", "name": "level" },
{ "type": "uint32", "name": "childcount" }
]
},{
"name": "delpermlink",
"base": "",
"fields": [
{ "type": "mssgid", "name": "msg" }
]
},{
"name": "addpermlinks",
"base": "",
"fields": [
{ "type": "addpermlink[]", "name": "permlinks" }
]
},{
"name": "delpermlinks",
"base": "",
"fields": [
{ "type": "mssgid[]", "name": "permlinks" }
]
},
{
"name": "post_event",
"base": "",
Expand Down Expand Up @@ -964,10 +982,6 @@
"name": "closemssgs",
"type": "closemssgs"
},
{
"name": "createacc",
"type": "createacc"
},
{
"name": "setrules",
"type": "setrules"
Expand Down Expand Up @@ -1007,7 +1021,12 @@
{
"name": "setmaxpayout",
"type": "setmaxpayout"
}
},

{ "name": "addpermlink", "type": "addpermlink" },
{ "name": "delpermlink", "type": "delpermlink" },
{ "name": "addpermlinks", "type": "addpermlinks" },
{ "name": "delpermlinks", "type": "delpermlinks" }
],
"tables": [
{
Expand Down
71 changes: 70 additions & 1 deletion golos.publication/golos.publication.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ extern "C" {
execute_action(&publication::set_max_payout);
if (NN(deletevotes) == action)
execute_action(&publication::deletevotes);

if (NN(addpermlink) == action)
execute_action(&publication::addpermlink);
if (NN(delpermlink) == action)
execute_action(&publication::delpermlink);
if (NN(addpermlinks) == action)
execute_action(&publication::addpermlinks);
if (NN(delpermlinks) == action)
execute_action(&publication::delpermlinks);
}
#undef NN
}
Expand Down Expand Up @@ -271,7 +280,7 @@ void publication::create_message(
item.cashout_time = cur_time + seconds(cashout_window_param.window).count();
});

permlink_table.emplace(message_id.author, [&]( auto &item) {
permlink_table.emplace(message_id.author, [&](auto& item) {
item.id = message_pk;
item.parentacc = parent_id.author;
item.parent_id = parent_pk;
Expand All @@ -281,6 +290,66 @@ void publication::create_message(
});
}

void publication::addpermlink(structures::mssgid msg, structures::mssgid parent, uint16_t level, uint32_t childcount) {
require_auth(_self);
uint64_t parent_pk = 0;
if (parent.author) {
eosio::check(parent.permlink.size() > 0, "Parent permlink must not be empty");
tables::permlink_table tbl(_self, parent.author.value);
auto idx = tbl.get_index<"byvalue"_n>();
auto itr = idx.find(parent.permlink);
eosio::check(itr != idx.end(), "Parent permlink doesn't exist");
eosio::check(itr->level + 1 == level, "Parent permlink level mismatch");
eosio::check(itr->childcount > 0, "Parent permlink should have children");
// Note: can try to also check (itr.childcount <= actual children), but it's hard due scope
parent_pk = itr->id;
} else {
eosio::check(msg.permlink.size() > 0, "Permlink must not be empty");
eosio::check(msg.permlink.size() < config::max_length, "Permlink must be less than 256 symbols");
eosio::check(validate_permlink(msg.permlink), "Permlink must only contain 0-9, a-z and _ symbols");
eosio::check(0 == level, "Root permlink must have 0 level");
eosio::check(parent.permlink.size() == 0, "Root permlink must have empty parent");
}
eosio::check(is_account(msg.author), "Author account must exist");

tables::permlink_table tbl(_self, msg.author.value);
auto idx = tbl.get_index<"byvalue"_n>();
auto itr = idx.find(msg.permlink);
eosio::check(itr == idx.end(), "Permlink already exists");

tbl.emplace(_self, [&](auto& pl) {
pl.id = tbl.available_primary_key();
pl.parentacc = parent.author;
pl.parent_id = parent_pk;
pl.value = msg.permlink;
pl.level = level;
pl.childcount = childcount;
});
}

void publication::delpermlink(structures::mssgid msg) {
require_auth(_self);
tables::permlink_table tbl(_self, msg.author.value);
auto idx = tbl.get_index<"byvalue"_n>();
auto itr = idx.find(msg.permlink);
eosio::check(itr != idx.end(), "Permlink doesn't exist");
idx.erase(itr);
}

void publication::addpermlinks(std::vector<structures::permlink_info> permlinks) {
eosio::check(permlinks.size() > 0, "`permlinks` must not be empty");
for (const auto& p: permlinks) {
addpermlink(p.msg, p.parent, p.level, p.childcount);
}
}

void publication::delpermlinks(std::vector<structures::mssgid> permlinks) {
eosio::check(permlinks.size() > 0, "`permlinks` must not be empty");
for (const auto& p: permlinks) {
delpermlink(p);
}
}

void publication::update_message(structures::mssgid message_id,
std::string headermssg, std::string bodymssg,
std::string languagemssg, std::vector<std::string> tags,
Expand Down
7 changes: 7 additions & 0 deletions golos.publication/golos.publication.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ class publication : public contract {
void calcrwrdwt(name account, int64_t mssg_id, int64_t post_charge);
void paymssgrwrd(structures::mssgid message_id);
void deletevotes(int64_t message_id, name author);

[[eosio::action]]
void addpermlink(structures::mssgid msg, structures::mssgid parent, uint16_t level, uint32_t childcount);
[[eosio::action]] void delpermlink(structures::mssgid msg);
[[eosio::action]] void addpermlinks(std::vector<structures::permlink_info> permlinks);
[[eosio::action]] void delpermlinks(std::vector<structures::mssgid> permlinks);

private:
const posting_state& params();
void set_vote(name voter, const structures::mssgid &message_id, int16_t weight);
Expand Down
7 changes: 7 additions & 0 deletions golos.publication/objects.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,13 @@ struct permlink {
}
};

struct permlink_info {
mssgid msg;
mssgid parent;
uint16_t level;
uint32_t childcount;
};

struct delegate_voter {
delegate_voter() = default;

Expand Down
18 changes: 18 additions & 0 deletions tests/golos.posting_test_api.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,24 @@ struct golos_posting_api: base_contract_api {
return closemssgs(_code);
}

action_result add_permlink(mssgid msg, mssgid parent, uint16_t level = 0, uint32_t childcount = 0) {
return push(N(addpermlink), _code, args()
("msg", msg)
("parent", parent)
("level", level)
("childcount", childcount)
);
}
action_result del_permlink(mssgid msg) {
return push(N(delpermlink), _code, args()("msg", msg));
}
action_result add_permlinks(std::vector<permlink_info> permlinks) {
return push(N(addpermlinks), _code, args()("permlinks", permlinks));
}
action_result del_permlinks(std::vector<mssgid> permlinks) {
return push(N(delpermlinks), _code, args()("permlinks", permlinks));
}


action_result init_default_params() {
auto vote_changes = get_str_vote_changes(max_vote_changes);
Expand Down
109 changes: 108 additions & 1 deletion tests/golos.posting_unit_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,23 @@ class posting_tester : public golos_tester {
vector<account_name> _users;

struct errors: contract_error_messages {
const string too_low_power = amsg("too low voting power");
const string too_low_power = amsg("too low voting power");
const string too_low_weight = amsg("too low vote weight");

const string plnk_empty_parent = amsg("Parent permlink must not be empty");
const string plnk_no_parent = amsg("Parent permlink doesn't exist");
const string plnk_bad_level = amsg("Parent permlink level mismatch");
const string plnk_bad_children = amsg("Parent permlink should have children");
const string plnk_empty = amsg("Permlink must not be empty");
const string plnk_too_long = amsg("Permlink must be less than 256 symbols");
const string plnk_invalid = amsg("Permlink must only contain 0-9, a-z and _ symbols");
const string plnk_bad_root_lvl = amsg("Root permlink must have 0 level");
const string plnk_root_parent = amsg("Root permlink must have empty parent");
const string plnk_no_author = amsg("Author account must exist");
const string plnk_already_there = amsg("Permlink already exists");
const string plnk_not_found = amsg("Permlink doesn't exist");
const string plnk_empty_vector = amsg("`permlinks` must not be empty");

} err;

public:
Expand Down Expand Up @@ -314,4 +329,96 @@ BOOST_FIXTURE_TEST_CASE(overcharge_test, posting_tester) try {

} FC_LOG_AND_RETHROW()

BOOST_FIXTURE_TEST_CASE(permlinks_internal_test, posting_tester) try {
BOOST_TEST_MESSAGE("Test internal permlink actions");
BOOST_TEST_MESSAGE("--- create test accounts and init");
_users.resize(2);
init(100500);
produce_block();

auto parent = _users[0];
auto author = _users[1];
auto unk = "non.existing"_n;

BOOST_TEST_MESSAGE("--- fails on bad parameters");
mssgid msg{parent, "test"};
mssgid empty{};
BOOST_CHECK_EQUAL(err.plnk_empty_parent, post.add_permlink(msg, {unk, ""}));
BOOST_CHECK_EQUAL(err.plnk_no_parent, post.add_permlink(msg, {unk, "not-created"}));
BOOST_CHECK_EQUAL(err.plnk_empty, post.add_permlink({unk, ""}, empty));
BOOST_CHECK_EQUAL(err.plnk_too_long, post.add_permlink({unk, std::string(256, 'a')}, empty));
BOOST_CHECK_EQUAL(err.plnk_invalid, post.add_permlink({unk, "bad+symbol"}, empty));
BOOST_CHECK_EQUAL(err.plnk_bad_root_lvl, post.add_permlink(msg, empty, 1));
BOOST_CHECK_EQUAL(err.plnk_bad_root_lvl, post.add_permlink(msg, empty, 10));
BOOST_CHECK_EQUAL(err.plnk_root_parent, post.add_permlink(msg, {{}, "non-empty"}));
BOOST_CHECK_EQUAL(err.plnk_no_author, post.add_permlink({unk, "not-registered"}, empty));

BOOST_CHECK_EQUAL(err.plnk_empty_vector, post.add_permlinks({}));
BOOST_CHECK_EQUAL(err.plnk_empty_vector, post.del_permlinks({}));

BOOST_TEST_MESSAGE("--- add permlink to test parent asserts");
mssgid lastchild{author, "no-children"};
BOOST_CHECK_EQUAL(success(), post.add_permlink(msg, empty, 0, 1));
BOOST_CHECK_EQUAL(success(), post.add_permlink(lastchild, msg, 1));
mssgid longpl{parent, std::string(255, 'a')};
BOOST_CHECK_EQUAL(success(), post.add_permlink(longpl, empty));
CHECK_MATCHING_OBJECT(post.get_permlink(msg), mvo()("value", msg.permlink));
CHECK_MATCHING_OBJECT(post.get_permlink(lastchild), mvo()("value", lastchild.permlink));
CHECK_MATCHING_OBJECT(post.get_permlink(longpl), mvo()("value", longpl.permlink));

BOOST_TEST_MESSAGE("--- fails on mismatch with parent");
mssgid poem{author, "poem"};
BOOST_CHECK_EQUAL(err.plnk_bad_level, post.add_permlink(poem, msg, 0));
BOOST_CHECK_EQUAL(err.plnk_bad_level, post.add_permlink(poem, msg, 2));
BOOST_CHECK_EQUAL(err.plnk_bad_level, post.add_permlink(poem, msg, 3));
BOOST_CHECK_EQUAL(err.plnk_bad_level, post.add_permlink(poem, lastchild, 0));
BOOST_CHECK_EQUAL(err.plnk_bad_level, post.add_permlink(poem, lastchild, 1));
BOOST_CHECK_EQUAL(err.plnk_bad_level, post.add_permlink(poem, lastchild, 3));
BOOST_CHECK_EQUAL(err.plnk_bad_level, post.add_permlink(poem, lastchild, 4));
BOOST_CHECK_EQUAL(err.plnk_bad_children, post.add_permlink(poem, lastchild, 2));

BOOST_TEST_MESSAGE("--- fails if already exists or not exists");
BOOST_CHECK_EQUAL(err.plnk_already_there, post.add_permlink(msg, empty));
BOOST_CHECK_EQUAL(err.plnk_not_found, post.del_permlink(poem));

BOOST_TEST_MESSAGE("--- delete works even with parent having children");
BOOST_CHECK_EQUAL(success(), post.del_permlink(msg));
BOOST_CHECK(post.get_permlink(msg).is_null());
produce_block(); // avoid "duplicate transaction"
BOOST_CHECK_EQUAL(err.plnk_not_found, post.del_permlink(msg));

BOOST_TEST_MESSAGE("--- test batch add/delete");
BOOST_CHECK_EQUAL(success(), post.add_permlinks({
{{parent, "1st"}, empty, 0, 1},
{{author, "2nd"}, {parent, "1st"}, 1, 1},
{{parent, "2nd-a"}, {parent, "1st"}, 1, 0},
{{author, "3rd"}, {author, "2nd"}, 2, 1},
{{author, "4th"}, {author, "3rd"}, 3, 0},
}));
BOOST_CHECK_EQUAL(success(), post.add_permlinks({{{parent, "one-more"}, empty, 0, 0}}));
BOOST_CHECK_EQUAL(success(), post.del_permlinks({{author, "3rd"}}));
BOOST_CHECK_EQUAL(success(), post.del_permlinks({
{parent, "1st"},
{author, "2nd"},
{parent, "2nd-a"},
{author, "4th"},
{parent, "one-more"}
}));
produce_block();
BOOST_CHECK_EQUAL(err.plnk_not_found, post.del_permlinks({{parent, "1st"}}));
BOOST_CHECK_EQUAL(err.plnk_not_found, post.del_permlinks({{author, "2nd"}}));
BOOST_CHECK_EQUAL(err.plnk_not_found, post.del_permlinks({{parent, "2nd-a"}}));
BOOST_CHECK_EQUAL(err.plnk_not_found, post.del_permlinks({{author, "3rd"}}));
BOOST_CHECK_EQUAL(err.plnk_not_found, post.del_permlinks({{author, "4th"}}));
BOOST_CHECK_EQUAL(err.plnk_not_found, post.del_permlinks({{parent, "one-more"}}));

BOOST_CHECK(post.get_permlink({parent, "1st"}).is_null());
BOOST_CHECK(post.get_permlink({author, "2nd"}).is_null());
BOOST_CHECK(post.get_permlink({parent, "2nd-a"}).is_null());
BOOST_CHECK(post.get_permlink({author, "3rd"}).is_null());
BOOST_CHECK(post.get_permlink({author, "4th"}).is_null());
BOOST_CHECK(post.get_permlink({parent, "one-more"}).is_null());

} FC_LOG_AND_RETHROW()

BOOST_AUTO_TEST_SUITE_END()
8 changes: 8 additions & 0 deletions tests/golos.publication_rewards_types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ struct mssgid {
}
};

struct permlink_info {
mssgid msg;
mssgid parent;
uint16_t level;
uint32_t childcount;
};


struct aprox_val_t {
double val;
Expand Down Expand Up @@ -418,3 +425,4 @@ struct state {

FC_REFLECT(eosio::testing::beneficiary, (account)(weight))
FC_REFLECT(eosio::testing::mssgid, (author)(permlink))
FC_REFLECT(eosio::testing::permlink_info, (msg)(parent)(level)(childcount))

0 comments on commit ee7dce6

Please sign in to comment.