Skip to content

Commit

Permalink
add new contract: personal.bos (#66)
Browse files Browse the repository at this point in the history
* 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
maodaishan authored and Thaipanda committed Mar 27, 2019
1 parent 91928e7 commit 4a0d96d
Show file tree
Hide file tree
Showing 7 changed files with 265 additions and 0 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ add_subdirectory(eosio.system)
add_subdirectory(eosio.token)
add_subdirectory(bos.pegtoken)
add_subdirectory(redpacket)
add_subdirectory(personal.bos)


if (APPLE)
Expand Down
8 changes: 8 additions & 0 deletions personal.bos/CMakeLists.txt
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}")
13 changes: 13 additions & 0 deletions personal.bos/README.md
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.


51 changes: 51 additions & 0 deletions personal.bos/include/personal.bos/personal.bos.hpp
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
41 changes: 41 additions & 0 deletions personal.bos/src/personal.bos.cpp
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) )
3 changes: 3 additions & 0 deletions tests/contracts.hpp.in
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ struct contracts {
static std::vector<uint8_t> bios_wasm() { return read_wasm("${CMAKE_BINARY_DIR}/../eosio.bios/eosio.bios.wasm"); }
static std::string bios_wast() { return read_wast("${CMAKE_BINARY_DIR}/../eosio.bios/eosio.bios.wast"); }
static std::vector<char> bios_abi() { return read_abi("${CMAKE_BINARY_DIR}/../eosio.bios/eosio.bios.abi"); }
static std::vector<uint8_t> personal_wasm() { return read_wasm("${CMAKE_BINARY_DIR}/../personal.bos/personal.bos.wasm"); }
static std::string personal_wast() { return read_wast("${CMAKE_BINARY_DIR}/../personal.bos/personal.bos.wast"); }
static std::vector<char> personal_abi() { return read_abi("${CMAKE_BINARY_DIR}/../personal.bos/personal.bos.abi"); }

struct util {
static std::vector<uint8_t> test_api_wasm() { return read_wasm("${CMAKE_SOURCE_DIR}/test_contracts/test_api.wasm"); }
Expand Down
148 changes: 148 additions & 0 deletions tests/personal.bos_tests.cpp
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()

0 comments on commit 4a0d96d

Please sign in to comment.