提交 023846f2 编写于 作者: B Bucky Kittinger

Merge branch 'master' into test_porting

......@@ -29,7 +29,22 @@
"fields": [
{"name":"value", "type":"string"}
]
},{
"name": "regproducer",
"base": "",
"fields": [
{"name":"producer", "type":"account_name"}
{"name":"producer_key", "type":"bytes"}
]
},{
"name": "stakevote",
"base": "",
"fields": [
{"name":"voter", "type":"account_name"}
{"name":"amount", "type":"asset"}
]
}
],
"actions": [{
"name": "transfer",
......@@ -40,6 +55,12 @@
},{
"name": "nonce",
"type": "nonce"
},{
"name": "regproducer",
"type": "regproducer"
},{
"name": "stakevote",
"type": "stakevote"
}
],
"tables": [{
......@@ -50,4 +71,4 @@
"key_types" : ["name"]
}
]
}
\ No newline at end of file
}
......@@ -11,6 +11,7 @@ extern "C" {
/// The apply method implements the dispatch of events to this contract
void apply( uint64_t code, uint64_t act ) {
print( eosio::name(code), "::", eosio::name(act) );
eosiosystem::contract<N(eosio)>::apply( code, act );
}
}
......@@ -14,13 +14,59 @@
#include <eosiolib/multi_index.hpp>
#include <eosiolib/privileged.h>
#include <algorithm>
#include <map>
namespace eosiosystem {
using eosio::indexed_by;
using eosio::const_mem_fun;
using eosio::bytes;
using std::map;
using std::pair;
using eosio::print;
template<account_name SystemAccount>
class contract {
public:
static const account_name system_account = SystemAccount;
typedef eosio::generic_currency< eosio::token<system_account,S(4,EOS)> > currency;
typedef typename currency::token_type system_token_type;
struct producer_votes {
account_name owner;
uint128_t total_votes;
uint64_t primary_key()const { return owner; }
uint128_t by_votes()const { return total_votes; }
EOSLIB_SERIALIZE( producer_votes, (owner)(total_votes) );
};
typedef eosio::multi_index< N(producervote), producer_votes,
indexed_by<N(prototalvote), const_mem_fun<producer_votes, uint128_t, &producer_votes::by_votes> >
> producer_votes_index_type;
struct account_votes {
account_name owner;
account_name proxy;
uint32_t last_update;
system_token_type staked;
std::vector<account_name> producers;
uint64_t primary_key()const { return owner; }
EOSLIB_SERIALIZE( account_votes, (owner)(proxy)(last_update)(staked)(producers) );
};
typedef eosio::multi_index< N(accountvotes), account_votes> account_votes_index_type;
struct producer_config {
account_name owner;
eosio::bytes packed_key; /// a packed public key object
uint64_t primary_key()const { return owner; }
EOSLIB_SERIALIZE( producer_config, (owner)(packed_key) );
};
typedef eosio::multi_index< N(producercfg), producer_config> producer_config_index_type;
struct total_resources {
account_name owner;
......@@ -34,7 +80,6 @@ namespace eosiosystem {
};
/**
* Every user 'from' has a scope/table that uses every receipient 'to' as the primary key.
*/
......@@ -72,9 +117,10 @@ namespace eosiosystem {
};
ACTION( SystemAccount, regproducer ) {
account_name producer_to_register;
account_name producer;
bytes producer_key;
EOSLIB_SERIALIZE( regproducer, (producer_to_register) );
EOSLIB_SERIALIZE( regproducer, (producer)(producer_key) );
};
ACTION( SystemAccount, regproxy ) {
......@@ -83,21 +129,23 @@ namespace eosiosystem {
EOSLIB_SERIALIZE( regproxy, (proxy_to_register) );
};
ACTION( SystemAccount, delnetbw ) {
ACTION( SystemAccount, delegatebw ) {
account_name from;
account_name receiver;
typename currency::token_type stake_quantity;
typename currency::token_type stake_net_quantity;
typename currency::token_type stake_cpu_quantity;
EOSLIB_SERIALIZE( delnetbw, (from)(receiver)(stake_quantity) )
EOSLIB_SERIALIZE( delegatebw, (from)(receiver)(stake_net_quantity)(stake_cpu_quantity) )
};
ACTION( SystemAccount, undelnetbw ) {
ACTION( SystemAccount, undelegatebw ) {
account_name from;
account_name receiver;
typename currency::token_type stake_quantity;
typename currency::token_type unstake_net_quantity;
typename currency::token_type unstake_cpu_quantity;
EOSLIB_SERIALIZE( delnetbw, (delegator)(receiver)(stake_quantity) )
EOSLIB_SERIALIZE( undelegatebw, (from)(receiver)(unstake_net_quantity)(unstake_cpu_quantity) )
};
ACTION( SystemAccount, nonce ) {
......@@ -110,7 +158,14 @@ namespace eosiosystem {
// 1. hash + collision
// 2. incrementing count (key=> tablename
static void on( const delnetbw& del ) {
static void on( const delegatebw& del ) {
eosio_assert( del.stake_cpu_quantity.quantity >= 0, "must stake a positive amount" );
eosio_assert( del.stake_net_quantity.quantity >= 0, "must stake a positive amount" );
auto total_stake = del.stake_cpu_quantity + del.stake_net_quantity;
eosio_assert( total_stake.quantity >= 0, "must stake a positive amount" );
require_auth( del.from );
del_bandwidth_index_type del_index( SystemAccount, del.from );
......@@ -123,12 +178,14 @@ namespace eosiosystem {
del_index.emplace( del.from, [&]( auto& dbo ){
dbo.from = del.from;
dbo.to = del.receiver;
dbo.net_weight = del.stake_quantity;
dbo.net_weight = del.stake_net_quantity;
dbo.cpu_weight = del.stake_cpu_quantity;
});
}
else {
del_index.update( *itr, del.from, [&]( auto& dbo ){
dbo.net_weight = del.stake_quantity;
dbo.net_weight = del.stake_net_quantity;
dbo.cpu_weight = del.stake_cpu_quantity;
});
}
......@@ -136,25 +193,190 @@ namespace eosiosystem {
if( tot_itr == nullptr ) {
tot_itr = &total_index.emplace( del.from, [&]( auto& tot ) {
tot.owner = del.receiver;
tot.total_net_weight += del.stake_quantity;
tot.total_net_weight += del.stake_net_quantity;
tot.total_cpu_weight += del.stake_cpu_quantity;
});
} else {
total_index.update( *tot_itr, 0, [&]( auto& tot ) {
tot.total_net_weight += del.stake_quantity;
tot.total_net_weight += del.stake_net_quantity;
tot.total_cpu_weight += del.stake_cpu_quantity;
});
}
set_resource_limits( tot_itr->owner, tot_itr->total_ram, tot_itr->total_net_weight.quantity, tot_itr->total_cpu_weight.quantity, 0 );
currency::inline_transfer( del.from, SystemAccount, del.stake_quantity, "stake bandwidth" );
} // delnetbw
currency::inline_transfer( del.from, SystemAccount, total_stake, "stake bandwidth" );
} // delegatebw
static void on( const undelegatebw& del ) {
eosio_assert( del.unstake_cpu_quantity.quantity >= 0, "must stake a positive amount" );
eosio_assert( del.unstake_net_quantity.quantity >= 0, "must stake a positive amount" );
auto total_stake = del.unstake_cpu_quantity + del.unstake_net_quantity;
eosio_assert( total_stake.quantity >= 0, "must stake a positive amount" );
require_auth( del.from );
del_bandwidth_index_type del_index( SystemAccount, del.from );
total_resources_index_type total_index( SystemAccount, del.receiver );
//eosio_assert( is_account( del.receiver ), "can only delegate resources to an existing account" );
const auto& dbw = del_index.get(del.receiver);
eosio_assert( dbw.net_weight >= del.unstake_net_quantity, "insufficient staked net bandwidth" );
eosio_assert( dbw.cpu_weight >= del.unstake_cpu_quantity, "insufficient staked cpu bandwidth" );
del_index.update( dbw, del.from, [&]( auto& dbo ){
dbo.net_weight -= del.unstake_net_quantity;
dbo.cpu_weight -= del.unstake_cpu_quantity;
});
const auto& totals = total_index.get( del.receiver );
total_index.update( totals, 0, [&]( auto& tot ) {
tot.total_net_weight -= del.unstake_net_quantity;
tot.total_cpu_weight -= del.unstake_cpu_quantity;
});
set_resource_limits( totals.owner, totals.total_ram, totals.total_net_weight.quantity, totals.total_cpu_weight.quantity, 0 );
/// TODO: implement / enforce time delays on withdrawing
currency::inline_transfer( SystemAccount, del.from, total_stake, "unstake bandwidth" );
} // undelegatebw
/**
* This method will create a producr_config and producer_votes object for 'producer'
*
* @pre producer is not already registered
* @pre producer to register is an account
* @pre authority of producer to register
*
*/
static void on( const regproducer& reg ) {
require_auth( reg.producer_to_register );
auto producer = reg.producer;
require_auth( producer );
producer_votes_index_type votes( SystemAccount, SystemAccount );
const auto* existing = votes.find( producer );
eosio_assert( !existing, "producer already registered" );
votes.emplace( producer, [&]( auto& pv ){
pv.owner = producer;
pv.total_votes = 0;
});
producer_config_index_type proconfig( SystemAccount, SystemAccount );
proconfig.emplace( producer, [&]( auto& pc ) {
pc.owner = producer;
pc.packed_key = reg.producer_key;
});
}
ACTION( SystemAccount, stakevote ) {
account_name voter;
system_token_type amount;
EOSLIB_SERIALIZE( stakevote, (voter)(amount) )
};
static void on( const stakevote& sv ) {
print( "on stake vote\n" );
eosio_assert( sv.amount.quantity > 0, "must stake some tokens" );
require_auth( sv.voter );
account_votes_index_type avotes( SystemAccount, SystemAccount );
const auto* acv = avotes.find( sv.voter );
if( !acv ) {
acv = &avotes.emplace( sv.voter, [&]( auto& av ) {
av.owner = sv.voter;
av.last_update = now();
av.proxy = 0;
});
}
uint128_t old_weight = acv->staked.quantity;
uint128_t new_weight = old_weight + sv.amount.quantity;
producer_votes_index_type votes( SystemAccount, SystemAccount );
for( auto p : acv->producers ) {
votes.update( votes.get( p ), 0, [&]( auto& v ) {
v.total_votes -= old_weight;
v.total_votes += new_weight;
});
}
avotes.update( *acv, 0, [&]( auto av ) {
av.last_update = now();
av.staked += sv.amount;
});
currency::inline_transfer( sv.voter, SystemAccount, sv.amount, "stake for voting" );
};
ACTION( SystemAccount, voteproducer ) {
account_name voter;
account_name proxy;
std::vector<account_name> producers;
EOSLIB_SERIALIZE( voteproducer, (voter)(proxy)(producers) )
};
/**
* @pre vp.producers must be sorted from lowest to highest
* @pre if proxy is set then no producers can be voted for
* @pre every listed producer or proxy must have been previously registered
* @pre vp.voter must authorize this action
* @pre voter must have previously staked some EOS for voting
*/
static void on( const voteproducer& vp ) {
eosio_assert( std::is_sorted( vp.producers.begin(), vp.producers.end() ), "producer votes must be sorted" );
eosio_assert( vp.producers.size() <= 30, "attempt to vote for too many producers" );
if( vp.proxy != 0 ) eosio_assert( vp.producers.size() == 0, "cannot vote for producers and proxy at same time" );
require_auth( vp.voter );
account_votes_index_type avotes( SystemAccount, SystemAccount );
const auto& existing = avotes.get( vp.voter );
std::map<account_name, pair<uint128_t, uint128_t> > producer_vote_changes;
uint128_t old_weight = existing.staked.quantity; /// old time
uint128_t new_weight = old_weight; /// TODO: update for current weight
for( const auto& p : existing.producers )
producer_vote_changes[p].first = old_weight;
for( const auto& p : vp.producers )
producer_vote_changes[p].second = new_weight;
producer_votes_index_type votes( SystemAccount, SystemAccount );
for( const auto& delta : producer_vote_changes ) {
if( delta.second.first != delta.second.second ) {
const auto& provote = votes.get( delta.first );
votes.update( provote, 0, [&]( auto& pv ){
pv.total_votes -= delta.second.first;
pv.total_votes += delta.second.second;
});
}
}
avotes.update( existing, 0, [&]( auto& av ) {
av.proxy = vp.proxy;
av.last_update = now();
av.producers = vp.producers;
});
}
static void on( const regproxy& reg ) {
require_auth( reg.proxy_to_register );
}
static void on( const nonce& ) {
......@@ -162,7 +384,11 @@ namespace eosiosystem {
static void apply( account_name code, action_name act ) {
if( !eosio::dispatch<contract, regproducer, regproxy, delnetbw, nonce>( code, act) ) {
if( !eosio::dispatch<contract,
regproducer, regproxy,
delegatebw, undelegatebw,
regproducer, voteproducer, stakevote,
nonce>( code, act) ) {
if ( !eosio::dispatch<currency, typename currency::transfer, typename currency::issue>( code, act ) ) {
eosio::print("Unexpected action: ", eosio::name(act), "\n");
eosio_assert( false, "received unexpected action");
......
/**
* @file
* @copyright defined in eos/LICENSE.txt
*/
namespace native {
/**
@defgroup eoscontract EOS Contract
@brief Documents the interface to the EOS currency contract
@ingroup contracts
@{
*/
/**
* @ingroup contracts
* @brief Defines the base class for all contracts
*/
struct contract {
/**
* @brief updates the code that will be executed for this contract
*
* <h3> Required Authority </h3>
*
* Requires authority of *this* contract.
*
* <h3> Required Scope </h3>
*
* Requires scope of *this* contract.
*
* @note the change in code does not take effect until the start of the next block
*/
void setcode( Bytes code,
Abi abi,
uint8_t vm = 0,
uint8_t vm_version = 0 ) final;
/**
* @brief updates the authority required for a named permission
*
* <h3> Required Authority </h3>
*
* Requires authority of *this* contract.
*
* <h3> Required Scope </h3>
*
* Requires scope of *this* contract.
*/
void setauth( Name permission, ///< the name for the permission being set
Name parent, ///< the parent permission to this permission
Authority auth ///< the set of keys/accounts and threshold );
) final;
/**
* @brief set the local named permission required for `this` account/contract to
* call `con::act(...)`
*
* <h3> Required Authority </h3>
*
* Requires authority of *this* contract.
*
* <h3> Required Scope </h3>
*
* Requires scope of *this* contract.
*
* @pre myperm must be defined by prior call to @ref setauth
*
* @param con - the name of the contract this permission applies
* @param act - the name of the action on @ref con this permission applies to
* @param myperm - the name of a permission set by @ref setauth on `this` contract
*/
void setperm( Name con, Name act, Name myperm );
};
/**
* @class eos
* @brief A *native* currency contract implemented with account named `eos`
* @ingroup contracts
*
* @details The EOS contract is a *native* currency contract implemented with account named `eos`. This contract enables
* users to transfer EOS tokens to each other. This contract is designed to work the @ref stakedcontract and
* @ref systemcontract when creating new accounts, claiming staked EOS.
*/
struct eos : public contract {
/**
@brief This action will transfer funds from one account to another.
@pre `from`'s balance must be greaterthan or equal to `amount` transferred.
@pre The amount transferred must be greater than 0
@pre `to` and `from` may not be the same account.
<h3> Required Authority </h3>
This action requires the authority of the `from` account.
<h3>Required Scope </h3>
This action requires access to `from` and `to` account scopes. It does not require
access to the `eos` scope which means that multiple transfers can execute in parallel
as long as they don't have any overlapping scopes.
<h3> Required Recipients </h3>
This action requires that the accounts `from` and `to` are listed in the required recipients. This ensures
other contracts are notified anytime EOS tokens are transferred.
*/
void transfer (
account_name from, ///< account from which EOS will be withdrawn
account_name to, ///< account to receive EOS, may not be same as `from`
uint64_t amount ///< must be greater than 0 and less or equal to `from`'s balance
);
}; /// class EOS
/// @}
}
......@@ -110,7 +110,7 @@ namespace eosio {
static void inline_transfer( account_name from, account_name to, token_type quantity,
string memo = string() )
{
action act( permission_level(code,N(active)), transfer_memo( from, to, asset(quantity), move(memo) ));
action act( permission_level(from,N(active)), transfer_memo( from, to, asset(quantity), move(memo) ));
act.send();
}
......
......@@ -29,6 +29,9 @@ struct secondary_iterator<uint64_t> {
static int db_idx_next( int iterator, uint64_t* primary ) { return db_idx64_next( iterator, primary ); }
static int db_idx_prev( int iterator, uint64_t* primary ) { return db_idx64_previous( iterator, primary ); }
static void db_idx_remove( int iterator ) { db_idx64_remove( iterator ); }
static int db_idx_find_primary( uint64_t code, uint64_t scope, uint64_t table, uint64_t primary, uint64_t& secondary ) {
return db_idx64_find_primary( code, scope, table, &secondary, primary );
}
};
template<>
......@@ -36,8 +39,19 @@ struct secondary_iterator<uint128_t> {
static int db_idx_next( int iterator, uint64_t* primary ) { return db_idx128_next( iterator, primary ); }
static int db_idx_prev( int iterator, uint64_t* primary ) { return db_idx128_previous( iterator, primary ); }
static void db_idx_remove( int iterator ) { db_idx128_remove( iterator ); }
static int db_idx_find_primary( uint64_t code, uint64_t scope, uint64_t table,
uint64_t primary, uint128_t& secondary ) {
return db_idx128_find_primary( code, scope, table, &secondary, primary );
}
};
int db_idx_store( uint64_t scope, uint64_t table, uint64_t payer, uint64_t primary, const uint64_t& secondary ) {
return db_idx64_store( scope, table, payer, primary, &secondary );
}
int db_idx_store( uint64_t scope, uint64_t table, uint64_t payer, uint64_t primary, const uint128_t& secondary ) {
return db_idx128_store( scope, table, payer, primary, &secondary );
}
void db_idx_update( int iterator, uint64_t payer, const uint64_t& secondary ) {
db_idx64_update( iterator, payer, &secondary );
}
......@@ -77,67 +91,73 @@ int db_idx_upperbound( uint64_t code, uint64_t scope, uint64_t table, uint128_t&
template<uint64_t TableName, typename T, typename... Indicies>
class multi_index;
template<uint64_t IndexName, typename Extractor>
struct indexed_by {
enum constants { index_name = IndexName };
typedef Extractor secondary_extractor_type;
typedef decltype( Extractor()(nullptr) ) secondary_type;
};
template<int IndexNumber, uint64_t IndexName, typename T, typename Extractor>
template<uint64_t IndexName, typename T, typename Extractor, int N = 0>
struct index_by {
//typedef typename std::decay<decltype( (Extractor())( *((const T*)(nullptr)) ) )>::type value_type;
typedef Extractor extractor_secondary_type;
typedef decltype( Extractor()(nullptr) ) secondary_type;
typedef Extractor extractor_secondary_type;
typedef typename std::decay<decltype( Extractor()(nullptr) )>::type secondary_type;
index_by(){}
static const int index_number = IndexNumber;
static const uint64_t index_name = IndexName;
enum constants {
index_name = IndexName,
index_number = N
};
constexpr static int number() { return N; }
constexpr static uint64_t name() { return IndexName; }
private:
template<uint64_t, typename, typename... >
friend class multi_index;
Extractor extract_secondary_key;
static auto extract_secondary_key(const T& obj) { return extractor_secondary_type()(obj); }
int store( uint64_t scope, uint64_t payer, const T& obj ) {
// fetch primary key and secondary key here..
return -1;
static int store( uint64_t scope, uint64_t payer, const T& obj ) {
return db_idx_store( scope, IndexName, payer, obj.primary_key(), extract_secondary_key(obj) );
}
void update( int iterator, uint64_t payer, const secondary_type& secondary ) {
static void update( int iterator, uint64_t payer, const secondary_type& secondary ) {
db_idx_update( iterator, payer, secondary );
}
int find_primary( uint64_t code, uint64_t scope, uint64_t primary, secondary_type& secondary )const {
static int find_primary( uint64_t code, uint64_t scope, uint64_t primary, secondary_type& secondary ) {
return db_idx_find_primary( code, scope, IndexName, secondary, primary );
}
void remove( int itr ) {
static void remove( int itr ) {
secondary_iterator<secondary_type>::db_idx_remove( itr );
}
int find_secondary( uint64_t code, uint64_t scope, secondary_type& secondary, uint64_t& primary )const {
static int find_secondary( uint64_t code, uint64_t scope, secondary_type& secondary, uint64_t& primary ) {
return db_idx_find_secondary( code, scope, IndexName, secondary, primary );
}
int lower_bound( uint64_t code, uint64_t scope, secondary_type& secondary, uint64_t& primary )const {
static int lower_bound( uint64_t code, uint64_t scope, secondary_type& secondary, uint64_t& primary ) {
return db_idx_lowerbound( code, scope, IndexName, secondary, primary );
}
int upper_bound( uint64_t code, uint64_t scope, secondary_type& secondary, uint64_t& primary )const {
static int upper_bound( uint64_t code, uint64_t scope, secondary_type& secondary, uint64_t& primary ) {
return db_idx_upperbound( code, scope, IndexName, secondary, primary );
}
};
/*
template<int IndexNumber, uint64_t IndexName, typename T, typename Extractor>
auto make_index_by( Extractor&& l ) {
return index_by<IndexNumber, IndexName, T, Extractor>{ std::forward<Extractor>(l) };
}
*/
namespace hana = boost::hana;
template<uint64_t TableName, typename T, typename... Indicies>
class multi_index
{
private:
struct item : public T
{
template<typename Constructor>
......@@ -154,8 +174,28 @@ class multi_index
uint64_t _code;
uint64_t _scope;
boost::hana::tuple<Indicies...> _indicies;
template<uint64_t I>
struct intc { enum e{ value = I }; operator uint64_t()const{ return I; } };
static constexpr auto transform_indicies( ) {
typedef decltype( hana::zip_shortest(
hana::make_tuple( intc<0>(), intc<1>(), intc<2>(), intc<3>(), intc<4>(), intc<5>() ),
hana::tuple<Indicies...>() ) ) indicies_input_type;
return hana::transform( indicies_input_type(), [&]( auto&& idx ){
typedef typename std::decay<decltype(hana::at_c<0>(idx))>::type num_type;
typedef typename std::decay<decltype(hana::at_c<1>(idx))>::type idx_type;
return index_by<idx_type::index_name,
T,
typename idx_type::secondary_extractor_type,
num_type::e::value>();
});
}
typedef decltype( multi_index::transform_indicies() ) indicies_type;
indicies_type _indicies;
struct by_primary_key;
struct by_primary_itr;
......@@ -167,9 +207,6 @@ class multi_index
>
> _items_index;
// mutable std::map<uint64_t, item*> _items; /// by_primary_key
// mutable std::map<int, item*> _items_by_itr;
const item& load_object_by_primary_iterator( int itr )const {
const auto& by_pitr = _items_index.template get<by_primary_itr>();
auto cacheitr = by_pitr.find( itr );
......@@ -188,7 +225,7 @@ class multi_index
i.__primary_itr = itr;
boost::hana::for_each( _indicies, [&]( auto& idx ) {
i.__iters[idx.index_number] = -1;
i.__iters[ idx.number() ] = -1;
});
});
......@@ -202,18 +239,21 @@ class multi_index
public:
multi_index( uint64_t code, uint64_t scope ):_code(code),_scope(scope){}
~multi_index() {
}
~multi_index() { }
template<typename MultiIndexType, typename IndexType>
uint64_t get_code()const { return _code; }
uint64_t get_scope()const { return _scope; }
template<typename MultiIndexType, typename IndexType, uint64_t Number>
struct index {
private:
typedef typename MultiIndexType::item item_type;
typedef typename IndexType::secondary_type secondary_key_type;
public:
static constexpr uint64_t name() { return IndexType::name(); }
struct const_iterator {
private:
public:
friend bool operator == ( const const_iterator& a, const const_iterator& b ) {
return a._item == b._item;
......@@ -231,8 +271,20 @@ class multi_index
}
const_iterator& operator++() {
if( !_item ) return *this;
if( _item->__iters[Number] == -1 ) {
/// TODO: lookup iter for this item in this index
secondary_key_type temp_secondary_key;
auto idxitr = secondary_iterator<secondary_key_type>::db_idx_find_primary(
_idx.get_code(),
_idx.get_scope(),
_idx.name(),
_item->primary_key(), temp_secondary_key);
}
uint64_t next_pk = 0;
auto next_itr = secondary_iterator<secondary_key_type>::db_idx_next( _item->__iters[IndexType::index_number], &next_pk );
auto next_itr = secondary_iterator<secondary_key_type>::db_idx_next( _item->__iters[Number], &next_pk );
if( next_itr == -1 ) {
_item = nullptr;
return *this;
......@@ -240,15 +292,18 @@ class multi_index
const T& obj = *_idx._multidx.find( next_pk );
auto& mi = const_cast<item_type&>( static_cast<const item_type&>(obj) );
mi.__iters[IndexType::index_number] = next_itr;
mi.__iters[Number] = next_itr;
_item = &mi;
return *this;
}
const_iterator& operator--() {
if( !_item ) {
}
uint64_t prev_pk = 0;
auto prev_itr = secondary_iterator<secondary_key_type>::db_idx_prev( _item->__iters[IndexType::index_number], &prev_pk );
auto prev_itr = secondary_iterator<secondary_key_type>::db_idx_prev( _item->__iters[Number], &prev_pk );
if( prev_itr == -1 ) {
_item = nullptr;
return *this;
......@@ -256,7 +311,7 @@ class multi_index
const T& obj = *_idx._multidx.find( prev_pk );
auto& mi = const_cast<item_type&>( static_cast<const item_type&>(obj) );
mi.__iters[IndexType::index_number] = prev_itr;
mi.__iters[Number] = prev_itr;
_item = &mi;
return *this;
......@@ -281,24 +336,25 @@ class multi_index
}
const_iterator lower_bound( typename IndexType::secondary_type& secondary ) {
uint64_t primary = 0;
auto itr = _idx.lower_bound( _multidx._code, _multidx._scope, secondary, primary );
auto itr = IndexType::lower_bound( _multidx._code, _multidx._scope, secondary, primary );
if( itr == -1 ) return end();
const T& obj = *_multidx.find( primary );
auto& mi = const_cast<item_type&>( static_cast<const item_type&>(obj) );
mi.__iters[IndexType::index_number] = itr;
mi.__iters[Number] = itr;
return const_iterator( *this, &mi );
}
uint64_t get_code()const { return _multidx.get_code(); }
uint64_t get_scope()const { return _multidx.get_scope(); }
private:
friend class multi_index;
index( const MultiIndexType& midx, const IndexType& idx )
:_multidx(midx),_idx(idx){}
index( const MultiIndexType& midx ) //, const IndexType& idx )
:_multidx(midx){}
const MultiIndexType _multidx;
const IndexType& _idx;
};
......@@ -323,14 +379,20 @@ class multi_index
//eosio_assert( _item, "null ptr" );
uint64_t pk;
auto next_itr = db_next_i64( _item->__primary_itr, &pk );
_item = &_multidx.load_object_by_primary_iterator( next_itr );
if( next_itr == -1 )
_item = nullptr;
else
_item = &_multidx.load_object_by_primary_iterator( next_itr );
return *this;
}
const_iterator& operator--() {
//eosio_assert( _item, "null ptr" );
uint64_t pk;
auto next_itr = db_previous_i64( _item->__primary_itr, &pk );
_item = &_multidx.load_object_by_primary_iterator( next_itr );
if( next_itr == -1 )
_item = nullptr;
else
_item = &_multidx.load_object_by_primary_iterator( next_itr );
return *this;
}
......@@ -372,9 +434,21 @@ class multi_index
}
template<uint64_t IndexName>
auto get_index()const {
const auto& idx = boost::hana::find_if( _indicies, []( auto x ){
return std::integral_constant<bool,(decltype(x)::index_name == IndexName)>(); } ).value();
return index<multi_index, typename std::decay<decltype(idx)>::type>( *this, idx );
auto idx = boost::hana::find_if( _indicies, []( auto&& in ){
/*
auto& x = hana::at_c<1>(idxp);
return std::integral_constant<bool,(std::decay<decltype(x)>::type::index_name == IndexName)>();
*/
return std::integral_constant<bool, std::decay<decltype(in)>::type::index_name == IndexName>();
} ).value();
return index<multi_index, decltype(idx), idx.number()>( *this );
/*
typedef typename std::decay<decltype(hana::at_c<0>(idx))>::type num_type;
return index<multi_index, typename std::decay<decltype(hana::at_c<1>(idx))>::type, num_type::value >( *this, hana::at_c<1>(idx) );
*/
}
template<typename Lambda>
......@@ -391,7 +465,7 @@ class multi_index
i.__primary_itr = db_store_i64( _scope, TableName, payer, pk, tmp, sizeof(tmp) );
boost::hana::for_each( _indicies, [&]( auto& idx ) {
i.__iters[idx.index_number] = idx.store( _scope, payer, obj );
i.__iters[idx.number()] = idx.store( _scope, payer, obj );
});
});
......@@ -406,7 +480,7 @@ class multi_index
// eosio_assert( &objitem.__idx == this, "invalid object" );
auto secondary_keys = boost::hana::transform( _indicies, [&]( auto& idx ) {
auto secondary_keys = boost::hana::transform( _indicies, [&]( auto&& idx ) {
return idx.extract_secondary_key( obj );
});
......@@ -421,18 +495,25 @@ class multi_index
db_update_i64( objitem.__primary_itr, payer, tmp, sizeof(tmp) );
boost::hana::for_each( _indicies, [&]( auto& idx ) {
typedef typename std::decay<decltype(idx)>::type index_type;
auto secondary = idx.extract_secondary_key( mutableobj );
if( boost::hana::at_c<std::decay<decltype(idx)>::type::index_number>(secondary_keys) != secondary ) {
auto indexitr = mutableitem.__iters[idx.index_number];
if( hana::at_c<index_type::index_number>(secondary_keys) != secondary ) {
auto indexitr = mutableitem.__iters[idx.number()];
if( indexitr == -1 )
indexitr = mutableitem.__iters[idx.index_number] = idx.find_primary( _code, _scope, pk, secondary );
indexitr = mutableitem.__iters[idx.number()] = idx.find_primary( _code, _scope, pk, secondary );
idx.update( indexitr, payer, secondary );
}
});
}
const T& get( uint64_t primary )const {
auto result = find( primary );
eosio_assert( result != nullptr, "unable to find key" );
return *result;
}
const T* find( uint64_t primary )const {
auto cacheitr = _items_index.find(primary);
if( cacheitr != _items_index.end() )
......@@ -453,7 +534,7 @@ class multi_index
db_remove_i64( objitem.__primary_itr );
boost::hana::for_each( _indicies, [&]( auto& idx ) {
auto i = objitem.__iters[idx.index_number];
auto i = objitem.__iters[idx.number()];
if( i == -1 ) {
typename std::decay<decltype(idx)>::type::secondary_type second;
i = idx.find_primary( _code, _scope, objitem.primary_key(), second );
......
extern "C" {
/**
*
* @return an ID that serves as an iterator to the object stored, -1 for error
*/
int db_store_i64( uint64_t scope, uint64_t table, uint64_t payer, uint64_t id, char* buffer, size_t buffer_size );
int db_update_i64( int iterator, char* buffer, size_t buffer_size );
/**
* max_buffer_size should start out with the space reserved for buffer, but is set to the actual size of buffer
*
* if max_buffer_size is greater than actual buffer size, then buffer will be filled with contents, otherwise not
*
* @return an ID that serves as an iterator to the object stored, -1 for error/not found
*/
int db_find_i64( uint64_t code, uint64_t scope, uint64_t table, uint64_t id, char* buffer, size_t* max_buffer_size );
int db_lower_bound_i64( uint64_t code, uint64_t scope, uint64_t table, uint64_t id, char* buffer, size_t* max_buffer_size );
int db_upper_bound_i64( uint64_t code, uint64_t scope, uint64_t table, uint64_t id, char* buffer, size_t* max_buffer_size );
/** return an iterator to the next item after @param iterator and optionally fetch data if buffer is not null */
int db_next( int iterator, uint64_t* id, char* buffer, size_t* max_buffer_size );
/** return an iterator to the prev item after @param iterator and optionally fetch data if buffer is not null */
int db_prev( int iterator, uint64_t* id, char* buffer, size_t* max_buffer_size );
/**
* @return the number of elements in the table stored at code/scope/table
*/
int db_count_i64( uint64_t code, uint64_t scope, uint64_t table );
void db_remove_i64( int iterator );
int db_find_primary_index64( uint64_t scope, uint64_t table, uint64_t primary, uint64_t* secondary );
int db_find_secondary_index64( uint64_t scope, uint64_t table, uint64_t* primary, uint64_t secondary );
int db_upper_bound_primary_index64( uint64_t scope, uint64_t table, uint64_t primary, uint64_t* secondary );
int db_upper_bound_secondary_index64( uint64_t scope, uint64_t table, uint64_t* primary, uint64_t secondary );
int db_lower_bound_primary_index64( uint64_t scope, uint64_t table, uint64_t primary, uint64_t* secondary );
int db_lower_bound_secondary_index64( uint64_t scope, uint64_t table, uint64_t* primary, uint64_t secondary );
int db_update_index64( int iterator, uint64_t payer, uint64_t id, uint64_t indexvalue );
int db_remove_index64( int iterator );
int db_next_index64( int iterator, uint64_t* value );
int db_prev_index64( int iterator, uint64_t* value );
int db_find_primary_index128( uint64_t scope, uint64_t table, uint64_t primary, uint128_t* secondary );
int db_find_secondary_index128( uint64_t scope, uint64_t table, uint64_t* primary, const uint128_t* secondary );
int db_upper_bound_primary_index128( uint64_t scope, uint64_t table, uint64_t primary, uint128_t* secondary );
int db_upper_bound_secondary_index128( uint64_t scope, uint64_t table, uint64_t* primary, const uint128_t* secondary );
int db_lower_bound_primary_index128( uint64_t scope, uint64_t table, uint64_t primary, uint128_t* secondary );
int db_lower_bound_secondary_index128( uint64_t scope, uint64_t table, uint64_t* primary, const uint128_t* secondary );
int db_update_index128( int iterator, uint64_t payer, uint64_t id, const uint128_t* secondary );
int db_remove_index128( int iterator );
int db_next_index128( int iterator, uint128_t* value );
int db_prev_index128( int iterator, uint128_t* value );
} /// extern "C"
namespace eosio {
namespace detail {
template<typename T>
struct id_for{};
}
struct limit_order {
uint64_t id;
uint128_t price;
uint64_t expiration;
account_name owner;
uint128_t by_owner_id()const {
uint128_t result(owner);
result << 64;
result |= id;
return result;
}
uint128_t by_price()const { return price; }
uint64_t by_expiration()const { return expiration; }
EOSLIB_SERIALIZE( (id)(price)(expiration)(owner) )
};
template<uint64_t Number, uint64_t IndexName, typename Extractor>
class index_by {
typedef std::decay<decltype( ex( *((const T*)(nullptr)) ) )>::type value_type
static const uint64_t index_name = IndexName;
static const uint64_t number = Number;
Extractor ex;
};
template<typename T, typename IndexTuple>
class multi_index {
auto indicies = make_tuple( make_index(N(byorderid), []( const T& obj ){ return obj.id; }),
make_index(N(byexpiration), []( const T& obj) { return obj.by_expiration(); } )
make_index(N(byprice), []( const T& obj) { return obj.by_price(); } ) );
mutable map<uint64_t, item*> _items;
public:
template<uint64_t IndexName, typename Key>
const T* find( const Key& k )const {
}
template<typename Existing>
auto get_keys( const T& obj ) {
return get_keys<1>( std::make_tuple(
}
template<int I, typename Existing, std::enable_if< I == std::tuple_size(IndexTuple) - 1> = 0 >
auto get_keys( Existing&& e, const T& obj ) {
return std::tuple_cat( std::forward<Existing>(e), std::get<I>( indicies ).extract( obj ) );
}
template<int I, typename Existing, std::enable_if< (I < std::tuple_size(IndexTuple) - 1) > = 0 >
auto get_keys( Existing&& e, const T& obj ) {
return get_keys<I+1>( std::tuple_cat( std::forward<Existing>(e), std::get<I>( indicies ).extract( obj ) ), obj );
}
template<typename Lambda>
const T& create( Lambda&& constructor, uint64_t payer ) {
auto i = new item( *this );
constructor( static_cast<T&>(*i) );
const T& obj = static_cast<const T&>(*i);
char tmp[ pack_size( obj ) ];
datastream<char*> ds( tmp, sizeof(tmp) );
pack( ds, obj );
auto pk = obj.primary_key();
i->__itrs[0] = db_store_i64( _code, _scope, _tables[0], payer, pk, tmp, sizeof(tmp) );
for_each( indicies, [&]( auto& idx ) {
i->__itrs[idx.number] = idx.store( _code, _scope, idx.index_name, payer, pk, idx.extract( obj ) );
});
items[pk] = i;
return obj;
}
template<typename Lambda>
void update( const T& obj, uint64_t payer, Lambda&& updater ) {
T& mobj = const_cast<T&>(obj);
item& i = static_cast<item&>(mobj);
auto pk = mobj.primary_key();
eosio_assert( &i.__mutli_idx == this );
auto old_idx = std::make_tuple(
obj.primary_key(), obj.expiration(), obj.by_owner_id(), obj.by_price() );
updater(mobj);
char tmp[ pack_size( mobj ) ];
datastream<char*> ds( tmp, sizeof(tmp) );
pack( ds, mobj );
db_update_i64( i.__itrs[0], payer, tmp, sizeof(tmp) );
auto new_idx = std::make_tuple(
obj.primary_key(), obj.expiration(), obj.by_owner_id(), obj.by_price() );
if( std::get<1>(old_idx) != std::get<1>(new_idx) ) {
if( i.__itrs[1] == -2 ) i.__itrs[1] = db_idx64_find_primary( pk );
db_idx64_update( i.__itrs[1], payer, std::get<1>(new_idx) );
}
if( std::get<2>(old_idx) != std::get<2>(new_idx) ) {
if( i.__itrs[2] == -2 ) i.__itrs[2] = db_idx64_find_primary( pk );
db_idx64_update( i.__itrs[2], payer, std::get<2>(new_idx) );
}
if( std::get<3>(old_idx) != std::get<3>(new_idx) ) {
if( i.__itrs[3] == -2 ) i.__itrs[3] = db_idx64_find_primary( pk );
db_idx64_update( i.__itrs[3], payer, std::get<3>(new_idx) );
}
}
private:
struct item : public T
{
item( multi_index& o ):__mutli_idx(o) {
}
multi_index& __multi_idx;
int __itrs[3];
};
};
/*
multi_index< N(limitorders), limit_order,
index< N(ownerid), &limit_order::by_owner_id >,
index< N(byprice), &limit_order::by_price >,
index< N(byexpire), &limit_order::by_expiration>
> orderbook;
*/
/*
template<uint64_t Code, uint64_t TableName, typename ObjectType>
class multi_index {
public:
struct cache_object : public ObjectType {
int primary_itr;
};
const cache_object* find( uint64_t primary ) {
auto itr = _cache.find( primary );
if( itr != cache.end() )
return itr->second;
db_find_i64( Code, _scope, TableName, primary );
}
private:
std::map<uint64_t, cache_object*> _cache;
};
auto order = orderbook.begin();
auto end = orderbook.end();
while( order != end ) {
auto cur = order;
++order;
orderbook.remove( cur );
}
const limit_order& order = *orderbook.begin();
/// Options:
// 1. maintain a wasm-side cache of all dereferenced objects
// a. keeps pointers valid
// b. minimizes temporary copies
// c. eliminates redundant deserialization losses
// d. will utilize more heap memory than necessary, potentially hitting sbrk
//
// 2. keep API light and return a copy
template<typename ObjectType, typename SecondaryKeyType>
class index
{
public:
index( uint64_t code, uint64_t scope, uint64_t table );
struct iterator {
iterator( index& idx, int itr )
:_idx(idx), _itr(itr){
if( itr >= 0 )
db_index<SecondaryKeyType>::get( itr, _primary, _secondary );
}
uint64_t primary()const { return _primary; }
const SecondaryKeyType& secondary()const { return _secondary; }
private:
uint64_t _primary;
SecondaryKeyType _secondary;
index& _idx;
int _itr;
};
iterator lower_bound( const SecondaryKeyType& lb ) {
return iterator( db_index<SecondaryKeyType>::secondary_lower_bound( _code, _scope, _table, lb ) );
}
private:
uint64_t _code;
uint64_t _scope;
uint64_t _table;
};
template<typename T, typename Indices>
class multi_index {
public:
struct iterator {
private:
int primary_itr;
};
multi_index( code_name code, scope_name scope, table_name table )
:_code(code),_scope(scope),_table(table){}
void insert( const T& obj, account_name payer ) {
uint64_t id = id_for<T>::get(obj);
auto buf = pack(obj);
store_i64( _scope, _table, payer, id, buf.data(), buf.size() );
Indices::insert( _code, _scope, _table, payer, obj );
}
template<typename Constructor>
void emplace( Constructor&& c ) {
T tmp;
id_for<T>::get(tmp) = allocate_id();
c(tmp);
insert( tmp );
}
template<typename Lambda>
void modify( const T& obj, account_name payer, Lambda&& update ) {
update(tmp);
uint64_t id = id_for<T>::get(value);
auto buf = pack(tmp);
store_i64( _code, _scope, _table, payer, id, buf.data(), buf.size() );
Indices::update( _code, _scope, _table, payer, obj );
}
private:
};
} // namespace eosio
......@@ -20,8 +20,8 @@ extern "C" {
/// The apply method implements the dispatch of events to this contract
void apply( uint64_t code, uint64_t action ) {
eosio::multi_index<N(orders), limit_order,
index_by<0, N(byexp), limit_order, const_mem_fun<limit_order, uint64_t, &limit_order::get_expiration> >,
index_by<1, N(byprice), limit_order, const_mem_fun<limit_order, uint128_t, &limit_order::get_price> >
indexed_by<N(byexp), const_mem_fun<limit_order, uint64_t, &limit_order::get_expiration> >,
indexed_by<N(byprice), const_mem_fun<limit_order, uint128_t, &limit_order::get_price> >
> orders( N(exchange), N(exchange) );
auto payer = code;
......
Subproject commit ef2b0c8d64f770d80ce537ec04d0de4bdc4d3585
Subproject commit a0cf75ad7c39137ebf03b0f3b0b4e5b7f731296b
......@@ -117,6 +117,7 @@ bool apply_context::is_account( const account_name& account )const {
void apply_context::require_authorization( const account_name& account )const {
for( const auto& auth : act.authorization )
if( auth.actor == account ) return;
wdump((act));
EOS_ASSERT( false, tx_missing_auth, "missing authority of ${account}", ("account",account));
}
void apply_context::require_authorization(const account_name& account,
......
......@@ -36,6 +36,13 @@ namespace eosio { namespace chain {
fc::datastream<char*> ds( abi.data(), abi.size() );
fc::raw::pack( ds, a );
}
eosio::chain::contracts::abi_def get_abi()const {
eosio::chain::contracts::abi_def a;
fc::datastream<const char*> ds( abi.data(), abi.size() );
fc::raw::unpack( ds, a );
return a;
}
};
using account_id_type = account_object::id_type;
......
Subproject commit 664fdd9e79263a894794f96959612ec2d1d013d0
Subproject commit d48ebabf56b4115753fcabb7648a0ffcf3b0f5e9
......@@ -29,7 +29,11 @@ namespace eosio { namespace testing {
transaction_trace push_transaction( packed_transaction& trx );
transaction_trace push_transaction( signed_transaction& trx );
action_result push_action(action&& cert_act, uint64_t authorizer);
action_result push_action(action&& cert_act, uint64_t authorizer);
transaction_trace push_action( const account_name& code, const action_name& act, const account_name& signer, const variant_object &data );
void set_tapos( signed_transaction& trx ) const;
void create_accounts( vector<account_name> names, bool multisig = false ) {
......
......@@ -365,4 +365,30 @@ namespace eosio { namespace testing {
return s;
}
transaction_trace tester::push_action( const account_name& code,
const action_name& acttype,
const account_name& actor,
const variant_object& data
)
{ try {
chain::contracts::abi_serializer abis( control->get_database().get<account_object,by_name>(code).get_abi() );
string action_type_name = abis.get_action_type(acttype);
action act;
act.account = code;
act.name = acttype;
act.authorization = vector<permission_level>{{actor, config::active_name}};
act.data = abis.variant_to_binary(action_type_name, data);
wdump((act));
signed_transaction trx;
trx.actions.emplace_back(std::move(act));
set_tapos(trx);
trx.sign(get_private_key(actor, "active"), chain_id_type());
wdump((get_public_key( actor, "active" )));;
return push_transaction(trx);
} FC_CAPTURE_AND_RETHROW( (code)(acttype)(actor) ) }
} } /// eosio::test
#include <boost/test/unit_test.hpp>
#include <eosio/testing/tester.hpp>
#include <eosio/chain/contracts/abi_serializer.hpp>
#include <eosio/chain_plugin/chain_plugin.hpp>
#include <eosio.system/eosio.system.wast.hpp>
#include <eosio.system/eosio.system.abi.hpp>
#include <Runtime/Runtime.h>
#include <fc/variant_object.hpp>
using namespace eosio::testing;
using mvo = fc::mutable_variant_object;
BOOST_AUTO_TEST_SUITE(eosio_system_tests)
BOOST_FIXTURE_TEST_CASE( eosio_system_load, tester ) try {
produce_block();
edump((name(config::system_account_name)));
set_code(config::system_account_name, eosio_system_wast);
set_abi(config::system_account_name, eosio_system_abi);
produce_block();
create_accounts( {N(dan),N(brendan)} );
push_action(N(eosio), N(issue), N(eosio), mvo()
("to", "eosio")
("quantity", "1000000.0000 EOS")
);
push_action(N(eosio), N(transfer), N(eosio), mvo()
("from", "eosio")
("to", "dan")
("quantity", "100.0000 EOS")
("memo", "hi" )
);
push_action(N(eosio), N(transfer), N(dan), mvo()
("from", "dan")
("to", "brendan")
("quantity", "50.0000 EOS")
("memo", "hi" )
);
wlog( "reg producer" );
auto regtrace = push_action(N(eosio), N(regproducer), N(dan), mvo()
("producer", "dan")
("producer_key", "")
);
wdump((regtrace));
produce_block();
wlog( "transfer" );
push_action(N(eosio), N(transfer), N(dan), mvo()
("from", "dan")
("to", "brendan")
("quantity", "5.0000 EOS")
("memo", "hi" )
);
/*
permission_level_weight plw{ permission_level{N(eosio),N(active)}, 1};;
set_authority( N(dan), N(active),
authority( 1,
vector<key_weight>({ {get_public_key(N(dan),"active"),1 } }),
vector<permission_level_weight>({plw}) ) );
*/
wlog( "stake vote" );
auto trace = push_action(N(eosio), N(stakevote), N(dan), mvo()
("voter", "dan")
("amount", "5.0000 EOS")
);
wdump((trace));
/*
produce_blocks(2);
create_accounts( {N(multitest)} );
produce_blocks(2);
set_code( N(multitest), eosio_system_test_wast );
set_abi( N(multitest), eosio_system_test_abi );
produce_blocks(1);
*/
} FC_LOG_AND_RETHROW()
BOOST_AUTO_TEST_SUITE_END()
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册