提交 fd4652f0 编写于 作者: D Daniel Larimer

fixed unit test with respect to delegated authority

上级 7f6e41b5
......@@ -458,31 +458,12 @@ void chain_controller::_finalize_block( const signed_block& b ) { try {
} FC_CAPTURE_AND_RETHROW( (b) ) }
namespace {
auto make_get_permission(const chainbase::database& db) {
return [&db](const permission_level& permission) {
auto key = boost::make_tuple(permission.actor, permission.permission);
return db.get<permission_object, by_owner>(key);
};
}
auto make_authority_checker(const chainbase::database& db, const flat_set<public_key_type>& signing_keys) {
auto get_permission = make_get_permission(db);
auto get_authority = [get_permission](const permission_level& permission) {
return get_permission(permission).auth;
};
auto depth_limit = db.get<global_property_object>().configuration.max_authority_depth;
wdump((depth_limit));
return make_auth_checker( move(get_authority), depth_limit, signing_keys);
}
}
flat_set<public_key_type> chain_controller::get_required_keys(const signed_transaction& trx,
const flat_set<public_key_type>& candidate_keys)const
{
auto checker = make_authority_checker(_db, candidate_keys);
auto checker = make_auth_checker( [&](auto p){ return get_permission(p).auth; },
get_global_properties().configuration.max_authority_depth,
candidate_keys);
for (const auto& act : trx.actions ) {
for (const auto& declared_auth : act.authorization) {
......@@ -500,23 +481,21 @@ flat_set<public_key_type> chain_controller::get_required_keys(const signed_trans
void chain_controller::check_transaction_authorization(const signed_transaction& trx,
bool allow_unused_signatures)const
{
if ((_skip_flags & skip_transaction_signatures) && (_skip_flags & skip_authority_check)) {
//ilog("Skipping auth and sigs checks");
return;
}
auto get_permission = make_get_permission(_db);
// #warning TODO: Use a real chain_id here (where is this stored? Do we still need it?)
auto checker = make_authority_checker(_db, trx.get_signature_keys(chain_id_type{}));
auto checker = make_auth_checker( [&](auto p){ return get_permission(p).auth; },
get_global_properties().configuration.max_authority_depth,
trx.get_signature_keys(chain_id_type{}) );
for( const auto& act : trx.actions )
for( const auto& declared_auth : act.authorization ) {
const auto& min_permission = lookup_minimum_permission(declared_auth.actor, act.scope, act.name);
const auto& min_permission = lookup_minimum_permission(declared_auth.actor,
act.scope, act.name);
if ((_skip_flags & skip_authority_check) == false) {
const auto& index = _db.get_index<permission_index>().indices();
EOS_ASSERT(get_permission(declared_auth).satisfies(min_permission, index), tx_irrelevant_auth,
EOS_ASSERT(get_permission(declared_auth).satisfies(min_permission, index),
tx_irrelevant_auth,
"action declares irrelevant authority '${auth}'; minimum authority is ${min}",
("auth", declared_auth)("min", min_permission.name));
}
......@@ -529,14 +508,19 @@ void chain_controller::check_transaction_authorization(const signed_transaction&
if (!allow_unused_signatures && (_skip_flags & skip_transaction_signatures) == false)
EOS_ASSERT(checker.all_keys_used(), tx_irrelevant_sig,
"transaction bears irrelevant signatures from these keys: ${keys}", ("keys", checker.unused_keys()));
"transaction bears irrelevant signatures from these keys: ${keys}",
("keys", checker.unused_keys()));
}
void chain_controller::validate_scope( const transaction& trx )const {
for( uint32_t i = 1; i < trx.read_scope.size(); ++i )
EOS_ASSERT( trx.read_scope[i-1] < trx.read_scope[i], transaction_exception, "Scopes must be sorted and unique" );
for( uint32_t i = 1; i < trx.write_scope.size(); ++i )
EOS_ASSERT( trx.write_scope[i-1] < trx.write_scope[i], transaction_exception, "Scopes must be sorted and unique" );
for( uint32_t i = 1; i < trx.read_scope.size(); ++i ) {
EOS_ASSERT( trx.read_scope[i-1] < trx.read_scope[i], transaction_exception,
"Scopes must be sorted and unique" );
}
for( uint32_t i = 1; i < trx.write_scope.size(); ++i ) {
EOS_ASSERT( trx.write_scope[i-1] < trx.write_scope[i], transaction_exception,
"Scopes must be sorted and unique" );
}
vector<account_name> intersection;
std::set_intersection( trx.read_scope.begin(), trx.read_scope.end(),
......@@ -875,6 +859,11 @@ const producer_object& chain_controller::get_producer(const account_name& owner_
return _db.get<producer_object, by_owner>(owner_name);
} FC_CAPTURE_AND_RETHROW( (owner_name) ) }
const permission_object& chain_controller::get_permission( const permission_level& level )const
{ try {
return _db.get<permission_object, by_owner>( boost::make_tuple(level.actor,level.permission) );
} FC_CAPTURE_AND_RETHROW( (level) ) }
uint32_t chain_controller::last_irreversible_block_num() const {
return get_dynamic_global_properties().last_irreversible_block_num;
}
......
......@@ -379,6 +379,8 @@ void apply_eosio_setproxy(apply_context& context) {
}
void apply_eosio_updateauth(apply_context& context) {
context.require_write_scope( config::eosio_auth_scope );
auto update = context.act.as<updateauth>();
EOS_ASSERT(!update.permission.empty(), action_validate_exception, "Cannot create authority with empty name");
EOS_ASSERT(update.permission != update.parent, action_validate_exception,
......@@ -407,14 +409,14 @@ void apply_eosio_updateauth(apply_context& context) {
if (permission) {
EOS_ASSERT(parent_id == permission->parent, action_validate_exception,
"Changing parent authority is not currently supported");
if (context.controller.is_applying_block())
// TODO/QUESTION: If we are updating an existing permission, should we check if the message declared
// permission satisfies the permission we want to modify?
db.modify(*permission, [&update, &parent_id](permission_object& po) {
po.auth = update.authority;
po.parent = parent_id;
});
} else if (context.controller.is_applying_block()) {
db.modify(*permission, [&update, &parent_id](permission_object& po) {
po.auth = update.authority;
po.parent = parent_id;
});
} else {
// TODO/QUESTION: If we are creating a new permission, should we check if the message declared
// permission satisfies the parent permission?
db.create<permission_object>([&update, &parent_id](permission_object& po) {
......
......@@ -50,92 +50,96 @@ namespace detail {
* @tparam F A callable which takes a single argument of type @ref AccountPermission and returns the corresponding
* authority
*/
template<typename F>
template<typename PermissionToAuthorityFunc>
class authority_checker {
F permission_to_authority;
uint16_t recursion_depth_limit;
vector<public_key_type> signing_keys;
vector<bool> _used_keys;
struct weight_tally_visitor {
using result_type = uint32_t;
authority_checker& checker;
uint16_t recursion_depth;
uint32_t total_weight = 0;
weight_tally_visitor(authority_checker& checker, uint16_t recursion_depth)
: checker(checker), recursion_depth(recursion_depth) {}
uint32_t operator()(const key_weight& permission) {
auto itr = boost::find(checker.signing_keys, permission.key);
if (itr != checker.signing_keys.end()) {
checker._used_keys[itr - checker.signing_keys.begin()] = true;
total_weight += permission.weight;
private:
PermissionToAuthorityFunc permission_to_authority;
uint16_t recursion_depth_limit;
vector<public_key_type> signing_keys;
vector<bool> _used_keys;
struct weight_tally_visitor {
using result_type = uint32_t;
authority_checker& checker;
uint16_t recursion_depth;
uint32_t total_weight = 0;
weight_tally_visitor(authority_checker& checker, uint16_t recursion_depth)
: checker(checker), recursion_depth(recursion_depth) {}
uint32_t operator()(const key_weight& permission) {
auto itr = boost::find(checker.signing_keys, permission.key);
if (itr != checker.signing_keys.end()) {
checker._used_keys[itr - checker.signing_keys.begin()] = true;
total_weight += permission.weight;
}
return total_weight;
}
return total_weight;
}
uint32_t operator()(const permission_level_weight& permission) {
if (recursion_depth < checker.recursion_depth_limit
&& checker.satisfied(permission.permission, recursion_depth + 1))
total_weight += permission.weight;
return total_weight;
uint32_t operator()(const permission_level_weight& permission) {
if (recursion_depth < checker.recursion_depth_limit) {
if( checker.satisfied(permission.permission, recursion_depth + 1) )
total_weight += permission.weight;
}
return total_weight;
}
};
public:
authority_checker( PermissionToAuthorityFunc permission_to_authority,
uint16_t recursion_depth_limit, const flat_set<public_key_type>& signing_keys)
: permission_to_authority(permission_to_authority),
recursion_depth_limit(recursion_depth_limit),
signing_keys(signing_keys.begin(), signing_keys.end()),
_used_keys(signing_keys.size(), false)
{}
bool satisfied(const permission_level& permission, uint16_t depth = 0) {
return satisfied(permission_to_authority(permission), depth);
}
};
public:
authority_checker(F permission_to_authority, uint16_t recursion_depth_limit, const flat_set<public_key_type>& signing_keys)
: permission_to_authority(permission_to_authority),
recursion_depth_limit(recursion_depth_limit),
signing_keys(signing_keys.begin(), signing_keys.end()),
_used_keys(signing_keys.size(), false)
{}
bool satisfied(const permission_level& permission, uint16_t depth = 0) {
return satisfied(permission_to_authority(permission), depth);
}
template<typename AuthorityType>
bool satisfied(const AuthorityType& authority, uint16_t depth = 0) {
// This check is redundant, since weight_tally_visitor did it too, but I'll leave it here for future-proofing
if (depth > recursion_depth_limit)
template<typename AuthorityType>
bool satisfied(const AuthorityType& authority, uint16_t depth = 0) {
// This check is redundant, since weight_tally_visitor did it too, but I'll leave it here for future-proofing
if (depth > recursion_depth_limit)
return false;
// Save the current used keys; if we do not satisfy this authority, the newly used keys aren't actually used
auto KeyReverter = fc::make_scoped_exit([this, keys = _used_keys] () mutable {
_used_keys = keys;
});
// Sort key permissions and account permissions together into a single set of meta_permissions
detail::meta_permission_set permissions;
permissions.insert(authority.keys.begin(), authority.keys.end());
permissions.insert(authority.accounts.begin(), authority.accounts.end());
// Check all permissions, from highest weight to lowest, seeing if signing_keys satisfies them or not
weight_tally_visitor visitor(*this, depth);
for (const auto& permission : permissions)
// If we've got enough weight, to satisfy the authority, return!
if (permission.visit(visitor) >= authority.threshold) {
KeyReverter.cancel();
return true;
}
return false;
}
// Save the current used keys; if we do not satisfy this authority, the newly used keys aren't actually used
auto KeyReverter = fc::make_scoped_exit([this, keys = _used_keys] () mutable {
_used_keys = keys;
});
// Sort key permissions and account permissions together into a single set of meta_permissions
detail::meta_permission_set permissions;
permissions.insert(authority.keys.begin(), authority.keys.end());
permissions.insert(authority.accounts.begin(), authority.accounts.end());
// Check all permissions, from highest weight to lowest, seeing if signing_keys satisfies them or not
weight_tally_visitor visitor(*this, depth);
for (const auto& permission : permissions)
// If we've got enough weight, to satisfy the authority, return!
if (permission.visit(visitor) >= authority.threshold) {
KeyReverter.cancel();
return true;
}
return false;
}
bool all_keys_used() const { return boost::algorithm::all_of_equal(_used_keys, true); }
bool all_keys_used() const { return boost::algorithm::all_of_equal(_used_keys, true); }
flat_set<public_key_type> used_keys() const {
auto range = utilities::FilterDataByMarker(signing_keys, _used_keys, true);
return {range.begin(), range.end()};
}
flat_set<public_key_type> unused_keys() const {
auto range = utilities::FilterDataByMarker(signing_keys, _used_keys, false);
return {range.begin(), range.end()};
}
flat_set<public_key_type> used_keys() const {
auto range = utilities::FilterDataByMarker(signing_keys, _used_keys, true);
return {range.begin(), range.end()};
}
flat_set<public_key_type> unused_keys() const {
auto range = utilities::FilterDataByMarker(signing_keys, _used_keys, false);
return {range.begin(), range.end()};
}
}; /// authority_checker
template<typename F>
authority_checker<F> make_auth_checker(F&& pta, uint16_t recursion_depth_limit, const flat_set<public_key_type>& signing_keys) {
return authority_checker<F>(std::forward<F>(pta), recursion_depth_limit, signing_keys);
template<typename PermissionToAuthorityFunc>
auto make_auth_checker(PermissionToAuthorityFunc&& pta, uint16_t recursion_depth_limit, const flat_set<public_key_type>& signing_keys) {
return authority_checker<PermissionToAuthorityFunc>(std::forward<PermissionToAuthorityFunc>(pta), recursion_depth_limit, signing_keys);
}
} } // namespace eosio::chain
......@@ -255,6 +255,7 @@ namespace eosio { namespace chain {
const global_property_object& get_global_properties()const;
const dynamic_global_property_object& get_dynamic_global_properties()const;
const producer_object& get_producer(const account_name& ownername)const;
const permission_object& get_permission( const permission_level& level )const;
time_point head_block_time()const;
uint32_t head_block_num()const;
......
......@@ -49,6 +49,7 @@ const static uint32_t producers_authority_threshold = 14;
const static share_type default_elected_pay = asset(100).amount;
const static share_type default_min_eos_balance = asset(100).amount;
const static uint16_t max_recursion_depth = 6;
/**
* The number of sequential blocks produced by a single producer
......
......@@ -81,6 +81,7 @@ namespace eosio { namespace testing {
.recovery = authority( get_public_key( a, "recovery" ) ),
.deposit = initial_balance
});
trx.sign( get_private_key( creator, "active" ), chain_id_type() );
control->push_transaction( trx );
......
......@@ -139,7 +139,10 @@ BOOST_AUTO_TEST_CASE( transfer_delegation ) { try {
});
test.set_authority( N(dan), config::active_name, dans_active_auth );
idump((dans_active_auth));
const auto& danauth = test.control->get_permission( permission_level{N(dan),config::active_name} );
const auto& trustauth = test.control->get_permission( permission_level{N(trust),config::active_name} );
test.produce_block();
......@@ -161,11 +164,11 @@ BOOST_AUTO_TEST_CASE( transfer_delegation ) { try {
test.set_tapos( trx );
trx.sign( test.get_private_key( N(trust), "active" ), chain_id_type() );
/// action not provided from authority
test.control->push_transaction( trx );
}
} FC_LOG_AND_RETHROW() }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册