forked from EOSIO/eosio.contracts
-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add new contract: personal.bos (#66)
* add new contract: personal.bos ,which contains actions: 1.setpersonal 2.sethomepage this contract has a table "personaldata" ,which's a key-value map to store customized public info for accounts. it has 2 columns: key: the primary key value: the value, should be less than 1024. setpersonal:user can call to set the key-value they wish,and read it via get table. sethomepage:the same with setpersonal, just the key is fixed to "homepage", by supplying homepage, dapp can easily guide user to their homepage. with commits on "boscore/bos",user can also use "cleos set personal ..." and "cleos get account ..." to set personaldata and read homepage. also add an inline interface "getpersonal" in personal.bos.hpp for easily accessing this multi-index for contracts. Deployment: when deploying,you need to create account "personal.bos" and set this contract to it to let it work. * fix memory leak
- Loading branch information
1 parent
91928e7
commit 4a0d96d
Showing
7 changed files
with
265 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
add_contract(personal.bos personal.bos ${CMAKE_CURRENT_SOURCE_DIR}/src/personal.bos.cpp) | ||
target_include_directories(personal.bos.wasm | ||
PUBLIC | ||
${CMAKE_CURRENT_SOURCE_DIR}/include) | ||
|
||
set_target_properties(personal.bos.wasm | ||
PROPERTIES | ||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
personal.bos | ||
----------- | ||
|
||
This bos contract allows users to maintain personal data | ||
actions: | ||
void setpersonal(name account,string key,string value); | ||
void sethomepage(name account,string url); | ||
|
||
it's a simple key-value map. | ||
sethomepage actually calls setpersonal, just set key to "homepage". | ||
by supplying homepage, dapp can guide users to their homepage. | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
/** | ||
* @file | ||
* @copyright defined in eos/LICENSE.txt | ||
*/ | ||
#pragma once | ||
|
||
#include <eosiolib/eosio.hpp> | ||
|
||
#include <string> | ||
|
||
namespace eosio { | ||
|
||
using std::string; | ||
|
||
class [[eosio::contract("personal.bos")]] personal_contract : public contract { | ||
public: | ||
using contract::contract; | ||
|
||
[[eosio::action]] | ||
void setpersonal( name account, name key, string value); | ||
|
||
[[eosio::action]] | ||
void sethomepage( name account, string url); | ||
|
||
string get_personal( name account, name key ){ | ||
personal_table personal( _self , account.value ); | ||
auto ite = personal.find( key.value ); | ||
if(ite != personal.end()){ | ||
return ite->value; | ||
}else{ | ||
return ""; | ||
} | ||
} | ||
|
||
private: | ||
/*For accounts to store their personal specific key-value data. | ||
key needs to be string and follow rules: only contain a-z,0-5,not longer than 12. | ||
for the key shorter than 12,will add 0 at higher bits. | ||
by doing this we can use key as uint64_t,to save space and easy use. | ||
usage: ex. dapp owner can call sethomepage to set its homepage,guiding users to their dapp*/ | ||
struct [[eosio::table]] personal{ | ||
name key; | ||
string value; | ||
EOSLIB_SERIALIZE(personal,(key)(value)) | ||
|
||
uint64_t primary_key()const { return key.value; } | ||
}; | ||
typedef eosio::multi_index< "personaldata"_n, personal > personal_table; | ||
}; | ||
|
||
} /// namespace eosio |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
/** | ||
* @file | ||
* @copyright defined in eos/LICENSE.txt | ||
*/ | ||
|
||
#include <personal.bos/personal.bos.hpp> | ||
|
||
namespace eosio { | ||
static void check_url( string url ){ | ||
eosio_assert( url.size() <= 256, "url is too long"); | ||
eosio_assert( url.find("http") == 0, "illegal url"); | ||
} | ||
|
||
void personal_contract::sethomepage( name account , string url ){ | ||
check_url( url ); | ||
setpersonal( account , "homepage"_n , url ); | ||
} | ||
|
||
void personal_contract::setpersonal( name account , name key , string value ){ | ||
require_auth( account ); | ||
if(key.value == "homepage"_n.value){ | ||
check_url( value ); | ||
} | ||
//not support too long value, for safety. | ||
eosio_assert( value.size() <= 1024 , "value should be less than 1024" ); | ||
personal_table personal( _self , account.value ); | ||
auto ite = personal.find( key.value ); | ||
if( ite != personal.end() ){ | ||
personal.modify( ite , account , [&](auto& data){ | ||
data.value = value; | ||
}); | ||
}else{ | ||
personal.emplace( account , [&](auto& data){ | ||
data.key = key; | ||
data.value = value; | ||
}); | ||
} | ||
} | ||
} | ||
|
||
EOSIO_DISPATCH( eosio::personal_contract, (sethomepage)(setpersonal) ) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
#include <boost/test/unit_test.hpp> | ||
#include <eosio/testing/tester.hpp> | ||
#include <eosio/chain/abi_serializer.hpp> | ||
#include "eosio.system_tester.hpp" | ||
|
||
#include "Runtime/Runtime.h" | ||
|
||
#include <fc/variant_object.hpp> | ||
|
||
using namespace eosio::testing; | ||
using namespace eosio; | ||
using namespace eosio::chain; | ||
using namespace eosio::testing; | ||
using namespace fc; | ||
using namespace std; | ||
|
||
using mvo = fc::mutable_variant_object; | ||
|
||
class personal_bos_tester : public tester { | ||
public: | ||
|
||
personal_bos_tester() { | ||
produce_blocks( 2 ); | ||
|
||
create_accounts( { N(alice), N(bob), N(personal.bos) } ); | ||
produce_blocks( 2 ); | ||
|
||
set_code( N(personal.bos), contracts::personal_wasm() ); | ||
set_abi( N(personal.bos), contracts::personal_abi().data() ); | ||
|
||
produce_blocks(); | ||
|
||
const auto& accnt = control->db().get<account_object,by_name>( N(personal.bos) ); | ||
abi_def abi; | ||
BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true); | ||
abi_ser.set_abi(abi, abi_serializer_max_time); | ||
} | ||
|
||
action_result push_action( const account_name& signer, const action_name &name, const variant_object &data ) { | ||
string action_type_name = abi_ser.get_action_type(name); | ||
|
||
action act; | ||
act.account = N(personal.bos); | ||
act.name = name; | ||
act.data = abi_ser.variant_to_binary( action_type_name, data,abi_serializer_max_time ); | ||
|
||
return base_tester::push_action( std::move(act), uint64_t(signer)); | ||
} | ||
|
||
fc::variant get_personal( const name act,const name key ) { | ||
vector<char> data = get_row_by_account( N(personal.bos), act, N(personaldata), key.value ); | ||
return abi_ser.binary_to_variant( "personal", data, abi_serializer_max_time ); | ||
} | ||
|
||
abi_serializer abi_ser; | ||
}; | ||
|
||
BOOST_AUTO_TEST_SUITE(personal_bos_tests) | ||
|
||
BOOST_FIXTURE_TEST_CASE( set_personal_and_homepage, personal_bos_tester ) try { | ||
//only owner can call setpersonal | ||
BOOST_REQUIRE_EQUAL(error("missing authority of alice"), | ||
push_action( N(bob), N(setpersonal), mvo() | ||
("account", "alice") | ||
("key", "favorfood") | ||
("value", "pizza")) | ||
); | ||
|
||
//value length check | ||
string long_value; | ||
long_value.assign(1025,'a'); | ||
BOOST_REQUIRE_EQUAL(wasm_assert_msg("value should be less than 1024"), | ||
push_action( N(alice), N(setpersonal), mvo() | ||
("account", "alice") | ||
("key", "somekey") | ||
("value",long_value)) | ||
); | ||
|
||
//setpersonal should success,and check result | ||
BOOST_REQUIRE_EQUAL(success(), | ||
push_action( N(alice), N(setpersonal), mvo() | ||
("account", "alice") | ||
("key", "favorfood") | ||
("value","pizza") | ||
) | ||
); | ||
auto data=get_personal(N(alice),"favorfood"); | ||
BOOST_REQUIRE_EQUAL(0,data["key"].as_string().find("favorfood")); | ||
BOOST_REQUIRE_EQUAL(0,data["value"].as_string().find("pizza")); | ||
|
||
//setpersonal to other value check | ||
BOOST_REQUIRE_EQUAL(success(), | ||
push_action( N(alice), N(setpersonal), mvo() | ||
("account", "alice") | ||
("key", "favorfood") | ||
("value","noodles") | ||
) | ||
); | ||
data=get_personal(N(alice),"favorfood"); | ||
BOOST_REQUIRE_EQUAL(0,data["key"].as_string().find("favorfood")); | ||
BOOST_REQUIRE_EQUAL(0,data["value"].as_string().find("noodles")); | ||
|
||
//sethomepage check | ||
//long url check | ||
string long_url; | ||
long_url.assign(257,'a'); | ||
BOOST_REQUIRE_EQUAL(wasm_assert_msg("url is too long"), | ||
push_action( N(alice), N(sethomepage), mvo() | ||
("account", "alice") | ||
("url",long_url)) | ||
); | ||
|
||
//url prefix check | ||
string url="abcdefg"; | ||
BOOST_REQUIRE_EQUAL(wasm_assert_msg("illegal url"), | ||
push_action( N(alice), N(sethomepage), mvo() | ||
("account", "alice") | ||
("url",url)) | ||
); | ||
|
||
//sethomepage should success,and check result | ||
BOOST_REQUIRE_EQUAL(success(), | ||
push_action( N(alice), N(sethomepage), mvo() | ||
("account", "alice") | ||
("url","http://somepage.com") | ||
) | ||
); | ||
data=get_personal(N(alice),"homepage"); | ||
BOOST_REQUIRE_EQUAL(0,data["value"].as_string().find("http://somepage.com")); | ||
|
||
//if call setpersonal and key's "homepage",should do check url,and url's modified. | ||
BOOST_REQUIRE_EQUAL(wasm_assert_msg("illegal url"), | ||
push_action( N(alice), N(setpersonal), mvo() | ||
("account", "alice") | ||
("key","homepage") | ||
("value",url)) | ||
); | ||
BOOST_REQUIRE_EQUAL(success(), | ||
push_action( N(alice), N(setpersonal), mvo() | ||
("account", "alice") | ||
("key","homepage") | ||
("value","https://newpage.com")) | ||
); | ||
data=get_personal(N(alice),"homepage"); | ||
BOOST_REQUIRE_EQUAL(0,data["value"].as_string().find("https://newpage.com")); | ||
} FC_LOG_AND_RETHROW() | ||
|
||
BOOST_AUTO_TEST_SUITE_END() |