未验证 提交 f19297f1 编写于 作者: W wanderingbort 提交者: GitHub

Merge pull request #2881 from EOSIO/slim-fix-permissions

Add wait weights to authority, simplify check authorization, and contracts are now only granted the "eosio.code" virtual permission
......@@ -45,7 +45,7 @@ void multisig::propose() {
(const char*)0, 0,
packed_requested.data(), packed_requested.size()
);
eosio_assert( res >= 0, "transaction authorization failed" );
eosio_assert( res > 0, "transaction authorization failed" );
proptable.emplace( proposer, [&]( auto& prop ) {
prop.proposal_name = proposal_name;
......@@ -123,7 +123,7 @@ void multisig::exec( account_name proposer, name proposal_name, account_name exe
(const char*)0, 0,
packed_provided_approvals.data(), packed_provided_approvals.size()
);
eosio_assert( res >= 0, "transaction authorization failed" );
eosio_assert( res > 0, "transaction authorization failed" );
send_deferred( (uint128_t(proposer) << 64) | proposal_name, executer, prop_it->packed_transaction.data(), prop_it->packed_transaction.size() );
......
......@@ -16,9 +16,9 @@ extern "C" {
* @param perms_data - pointer to the start of the serialized vector of provided permissions (empty permission name acts as wildcard)
* @param perms_size - size (in bytes) of the serialized vector of provided permissions
*
* @return the minimum delay (in microseconds) that was required to satisfy the authorization if the transaction is authorized, -1 otherwise
* @return 1 if the transaction is authorized, 0 otherwise
*/
int64_t
int32_t
check_transaction_authorization( const char* trx_data, uint32_t trx_size,
const char* pubkeys_data, uint32_t pubkeys_size,
const char* perms_data, uint32_t perms_size
......@@ -35,9 +35,9 @@ extern "C" {
* @param perms_size - size (in bytes) of the serialized vector of provided permissions
* @param delay_us - the provided delay in microseconds (cannot exceed INT64_MAX)
*
* @return the minimum delay (in microseconds) that was required to satisfy the authorization if the permission is authorized, -1 otherwise
* @return 1 if the permission is authorized, 0 otherwise
*/
int64_t
int32_t
check_permission_authorization( account_name account,
permission_name permission,
const char* pubkeys_data, uint32_t pubkeys_size,
......
......@@ -6,7 +6,6 @@
#include <eosiolib/permission.h>
#include <eosiolib/transaction.hpp>
#include <eosiolib/optional.hpp>
#include <set>
#include <limits>
......@@ -20,9 +19,9 @@ namespace eosio {
* @param provided_permissions - the set of permissions which have authorized the transaction (empty permission name acts as wildcard)
* @param provided_keys - the set of public keys which have authorized the transaction
*
* @return an optional of the minimum delay (in microseconds) required to satisfy the authorization (returns empty optional if transaction was not authorized by provided keys and permissions)
* @return whether the transaction was authorized by provided keys and permissions
*/
optional<uint64_t>
bool
check_transaction_authorization( const transaction& trx,
const std::set<permission_level>& provided_permissions ,
const std::set<public_key>& provided_keys = std::set<public_key>()
......@@ -50,10 +49,7 @@ namespace eosio {
(nperms > 0) ? packed_perms.size() : 0
);
if( res >= 0 )
return static_cast<uint64_t>(res);
else
return optional<uint64_t>();
return (res > 0);
}
/**
......@@ -65,9 +61,9 @@ namespace eosio {
* @param provided_permissions - the set of permissions which have authorized the transaction (empty permission name acts as wildcard)
* @param provided_delay_us - the provided delay in microseconds (cannot exceed INT64_MAX)
*
* @return an optional of the minimum delay (in microseconds) required to satisfy the authorization (returns empty optional if permission was not authorized by provided delay, keys, and permissions)
* @return whether the permission was authorized by provided delay, keys, and permissions
*/
optional<uint64_t>
bool
check_permission_authorization( account_name account,
permission_name permission,
const std::set<public_key>& provided_keys,
......@@ -96,10 +92,7 @@ namespace eosio {
provided_delay_us
);
if( res >= 0 )
return static_cast<uint64_t>(res);
else
return optional<uint64_t>();
return (res > 0);
}
}
......@@ -211,7 +211,7 @@ void apply_context::execute_inline( action&& a ) {
control.get_authorization_manager()
.check_authorization( {a},
{},
{{receiver, permission_name()}},
{{receiver, config::eosio_code_name}},
control.pending_block_time() - trx_context.published,
std::bind(&apply_context::checktime, this, std::placeholders::_1),
false
......@@ -269,7 +269,7 @@ void apply_context::schedule_deferred_transaction( const uint128_t& sender_id, a
control.get_authorization_manager()
.check_authorization( trx.actions,
{},
{{receiver, permission_name()}},
{{receiver, config::eosio_code_name}},
delay,
std::bind(&apply_context::checktime, this, std::placeholders::_1),
false
......@@ -421,17 +421,6 @@ int apply_context::get_context_free_data( uint32_t index, char* buffer, size_t b
return copy_size;
}
void apply_context::check_auth( const transaction& trx, const vector<permission_level>& perm ) {
control.get_authorization_manager()
.check_authorization( trx.actions,
{},
{perm.begin(), perm.end()},
fc::microseconds(0),
std::bind(&apply_context::checktime, this, std::placeholders::_1),
false
);
}
int apply_context::db_store_i64( uint64_t scope, uint64_t table, const account_name& payer, uint64_t id, const char* buffer, size_t buffer_size ) {
return db_store_i64( receiver, scope, table, payer, id, buffer, buffer_size);
}
......
......@@ -345,11 +345,6 @@ struct controller_impl {
majority_permission.id,
active_producers_authority,
conf.genesis.initial_timestamp );
const auto& any_producer_permission = authorization.create_permission( config::producers_account_name,
config::any_producer_permission_name,
minority_permission.id,
active_producers_authority,
conf.genesis.initial_timestamp );
}
......@@ -931,9 +926,6 @@ struct controller_impl {
config::minority_producers_permission_name}),
calculate_threshold( 1, 3 ) /* more than one-third */ );
update_permission( authorization.get_permission({config::producers_account_name,
config::any_producer_permission_name}), 1 );
//TODO: Add tests
}
......
......@@ -14,7 +14,6 @@
#include <eosio/chain/account_object.hpp>
#include <eosio/chain/permission_object.hpp>
#include <eosio/chain/permission_link_object.hpp>
#include <eosio/chain/generated_transaction_object.hpp>
#include <eosio/chain/global_property_object.hpp>
#include <eosio/chain/contract_types.hpp>
#include <eosio/chain/producer_object.hpp>
......@@ -36,8 +35,26 @@ uint128_t transaction_id_to_sender_id( const transaction_id_type& tid ) {
void validate_authority_precondition( const apply_context& context, const authority& auth ) {
for(const auto& a : auth.accounts) {
context.db.get<account_object, by_name>(a.permission.actor);
context.control.get_authorization_manager().get_permission({a.permission.actor, a.permission.permission});
auto* acct = context.db.find<account_object, by_name>(a.permission.actor);
EOS_ASSERT( acct != nullptr, action_validate_exception,
"account '${account}' does not exist",
("account", a.permission.actor)
);
if( a.permission.permission == config::owner_name || a.permission.permission == config::active_name )
continue; // account was already checked to exist, so its owner and active permissions should exist
if( a.permission.permission == config::eosio_code_name ) // virtual eosio.code permission does not really exist but is allowed
continue;
try {
context.control.get_authorization_manager().get_permission({a.permission.actor, a.permission.permission});
} catch( const permission_query_exception& ) {
EOS_THROW( action_validate_exception,
"permission '${perm}' does not exist",
("perm", a.permission)
);
}
}
}
......@@ -73,10 +90,6 @@ void apply_eosio_newaccount(apply_context& context) {
"Cannot create account named ${name}, as that name is already taken",
("name", create.name));
for( const auto& auth : { create.owner, create.active } ){
validate_authority_precondition( context, auth );
}
const auto& new_account = db.create<account_object>([&](auto& a) {
a.name = create.name;
a.creation_date = context.control.pending_block_time();
......@@ -86,6 +99,10 @@ void apply_eosio_newaccount(apply_context& context) {
a.name = create.name;
});
for( const auto& auth : { create.owner, create.active } ){
validate_authority_precondition( context, auth );
}
const auto& owner_permission = authorization.create_permission( create.name, config::owner_name, 0,
std::move(create.owner) );
const auto& active_permission = authorization.create_permission( create.name, config::active_name, owner_permission.id,
......@@ -196,10 +213,12 @@ void apply_eosio_updateauth(apply_context& context) {
else
EOS_ASSERT(!update.parent.empty(), action_validate_exception, "Only owner permission can have empty parent" );
auto max_delay = context.control.get_global_properties().configuration.max_transaction_delay;
EOS_ASSERT( update.auth.delay_sec <= max_delay, action_validate_exception,
"Cannot set delay longer than max_transacton_delay, which is ${max_delay} seconds",
("max_delay", max_delay) );
if( update.auth.waits.size() > 0 ) {
auto max_delay = context.control.get_global_properties().configuration.max_transaction_delay;
EOS_ASSERT( update.auth.waits.back().wait_sec <= max_delay, action_validate_exception,
"Cannot set delay longer than max_transacton_delay, which is ${max_delay} seconds",
("max_delay", max_delay) );
}
validate_authority_precondition(context, update.auth);
......@@ -227,7 +246,6 @@ void apply_eosio_updateauth(apply_context& context) {
po.auth = update.auth;
po.parent = parent_id;
po.last_updated = context.control.pending_block_time();
po.delay = fc::seconds(update.auth.delay_sec);
});
int64_t new_size = (int64_t)(config::billable_size_v<permission_object> + permission->auth.get_billable_size());
......@@ -364,28 +382,6 @@ void apply_eosio_canceldelay(apply_context& context) {
const auto& trx_id = cancel.trx_id;
const auto& generated_transaction_idx = context.control.db().get_index<generated_transaction_multi_index>();
const auto& generated_index = generated_transaction_idx.indices().get<by_trx_id>();
const auto& itr = generated_index.lower_bound(trx_id);
FC_ASSERT (itr != generated_index.end() && itr->sender == account_name() && itr->trx_id == trx_id,
"cannot cancel trx_id=${tid}, there is no deferred transaction with that transaction id",("tid", trx_id));
auto trx = fc::raw::unpack<transaction>(itr->packed_trx.data(), itr->packed_trx.size());
bool found = false;
for( const auto& act : trx.actions ) {
for( const auto& auth : act.authorization ) {
if( auth == cancel.canceling_auth ) {
found = true;
break;
}
context.checktime( 20 );
}
if( found ) break;
}
EOS_ASSERT( found, action_validate_exception,
"canceling_auth in canceldelay action was not found as authorization in the original delayed transaction" );
context.cancel_deferred_transaction(transaction_id_to_sender_id(trx_id), account_name());
context.checktime( 1000 );
......
......@@ -173,13 +173,6 @@ abi_def eosio_contract_abi(const abi_def& eosio_system_abi)
}
});
eos_abi.structs.emplace_back( struct_def {
"permission_level_weight", "", {
{"permission", "permission_level"},
{"weight", "weight_type"}
}
});
eos_abi.structs.emplace_back( struct_def {
"transaction_header", "", {
{"expiration", "time_point_sec"},
......@@ -212,12 +205,26 @@ abi_def eosio_contract_abi(const abi_def& eosio_system_abi)
}
});
eos_abi.structs.emplace_back( struct_def {
"permission_level_weight", "", {
{"permission", "permission_level"},
{"weight", "weight_type"}
}
});
eos_abi.structs.emplace_back( struct_def {
"wait_weight", "", {
{"wait_sec", "uint32"},
{"weight", "weight_type"}
}
});
eos_abi.structs.emplace_back( struct_def {
"authority", "", {
{"threshold", "uint32"},
{"delay_sec", "uint32"},
{"keys", "key_weight[]"},
{"accounts", "permission_level_weight[]"}
{"accounts", "permission_level_weight[]"},
{"waits", "wait_weight[]"}
}
});
......
......@@ -13,9 +13,28 @@ namespace eosio { namespace chain {
permission_name permission;
};
inline bool operator== (const permission_level& lhs, const permission_level& rhs)
{
return (lhs.actor == rhs.actor) && (lhs.permission == rhs.permission);
inline bool operator== (const permission_level& lhs, const permission_level& rhs) {
return std::tie(lhs.actor, lhs.permission) == std::tie(rhs.actor, rhs.permission);
}
inline bool operator!= (const permission_level& lhs, const permission_level& rhs) {
return std::tie(lhs.actor, lhs.permission) != std::tie(rhs.actor, rhs.permission);
}
inline bool operator< (const permission_level& lhs, const permission_level& rhs) {
return std::tie(lhs.actor, lhs.permission) < std::tie(rhs.actor, rhs.permission);
}
inline bool operator<= (const permission_level& lhs, const permission_level& rhs) {
return std::tie(lhs.actor, lhs.permission) <= std::tie(rhs.actor, rhs.permission);
}
inline bool operator> (const permission_level& lhs, const permission_level& rhs) {
return std::tie(lhs.actor, lhs.permission) > std::tie(rhs.actor, rhs.permission);
}
inline bool operator>= (const permission_level& lhs, const permission_level& rhs) {
return std::tie(lhs.actor, lhs.permission) >= std::tie(rhs.actor, rhs.permission);
}
/**
......
......@@ -516,9 +516,6 @@ class apply_context {
bool all_authorizations_used()const;
vector<permission_level> unused_authorizations()const;
void check_auth( const transaction& trx, const vector<permission_level>& perm );
/// Console methods:
public:
......
......@@ -30,6 +30,15 @@ struct key_weight {
}
};
struct wait_weight {
uint32_t wait_sec;
weight_type weight;
friend bool operator == ( const wait_weight& lhs, const wait_weight& rhs ) {
return tie( lhs.wait_sec, lhs.weight ) == tie( rhs.wait_sec, rhs.weight );
}
};
namespace config {
template<>
struct billable_size<permission_level_weight> {
......@@ -40,74 +49,85 @@ namespace config {
struct billable_size<key_weight> {
static const uint64_t value = 8; ///< over value of weight for safety, dynamically sizing key
};
template<>
struct billable_size<wait_weight> {
static const uint64_t value = 16; ///< over value of weight and wait_sec for safety
};
}
struct authority {
authority( public_key_type k, uint32_t delay = 0 ):threshold(1),delay_sec(delay),keys({{k,1}}){}
authority( uint32_t t, vector<key_weight> k, vector<permission_level_weight> p = {}, uint32_t delay = 0 )
:threshold(t),delay_sec(delay),keys(move(k)),accounts(move(p)){}
authority( public_key_type k, uint32_t delay_sec = 0 )
:threshold(1),keys({{k,1}})
{
if( delay_sec > 0 ) {
threshold = 2;
waits.push_back(wait_weight{delay_sec, 1});
}
}
authority( uint32_t t, vector<key_weight> k, vector<permission_level_weight> p = {}, vector<wait_weight> w = {} )
:threshold(t),keys(move(k)),accounts(move(p)),waits(move(w)){}
authority(){}
uint32_t threshold = 0;
uint32_t delay_sec = 0;
vector<key_weight> keys;
vector<permission_level_weight> accounts;
vector<wait_weight> waits;
friend bool operator == ( const authority& lhs, const authority& rhs ) {
return tie( lhs.threshold, lhs.delay_sec, lhs.keys, lhs.accounts ) == tie( rhs.threshold, rhs.delay_sec, rhs.keys, rhs.accounts );
return tie( lhs.threshold, lhs.keys, lhs.accounts, lhs.waits ) == tie( rhs.threshold, rhs.keys, rhs.accounts, rhs.waits );
}
friend bool operator != ( const authority& lhs, const authority& rhs ) {
return tie( lhs.threshold, lhs.delay_sec, lhs.keys, lhs.accounts ) != tie( rhs.threshold, rhs.delay_sec, rhs.keys, rhs.accounts );
return tie( lhs.threshold, lhs.keys, lhs.accounts, lhs.waits ) != tie( rhs.threshold, rhs.keys, rhs.accounts, rhs.waits );
}
};
struct shared_authority {
shared_authority( chainbase::allocator<char> alloc )
:keys(alloc),accounts(alloc){}
:keys(alloc),accounts(alloc),waits(alloc){}
shared_authority& operator=(const authority& a) {
threshold = a.threshold;
delay_sec = a.delay_sec;
keys = decltype(keys)(a.keys.begin(), a.keys.end(), keys.get_allocator());
accounts = decltype(accounts)(a.accounts.begin(), a.accounts.end(), accounts.get_allocator());
waits = decltype(waits)(a.waits.begin(), a.waits.end(), waits.get_allocator());
return *this;
}
uint32_t threshold = 0;
uint32_t delay_sec = 0;
shared_vector<key_weight> keys;
shared_vector<permission_level_weight> accounts;
shared_vector<wait_weight> waits;
operator authority()const { return to_authority(); }
authority to_authority()const {
authority auth;
auth.threshold = threshold;
auth.delay_sec = delay_sec;
auth.keys.reserve(keys.size());
auth.accounts.reserve(accounts.size());
auth.waits.reserve(waits.size());
for( const auto& k : keys ) { auth.keys.emplace_back( k ); }
for( const auto& a : accounts ) { auth.accounts.emplace_back( a ); }
for( const auto& w : waits ) { auth.waits.emplace_back( w ); }
return auth;
}
size_t get_billable_size() const {
size_t accounts_size = accounts.size() * config::billable_size_v<permission_level_weight>;
size_t waits_size = waits.size() * config::billable_size_v<wait_weight>;
size_t keys_size = 0;
for (const auto& k: keys) {
keys_size += config::billable_size_v<key_weight>;
keys_size += fc::raw::pack_size(k.key); ///< serialized size of the key
}
return accounts_size + keys_size;
return accounts_size + waits_size + keys_size;
}
};
inline bool operator< (const permission_level& a, const permission_level& b) {
return std::tie(a.actor, a.permission) < std::tie(b.actor, b.permission);
}
/**
* Makes sure all keys are unique and sorted and all account permissions are unique and sorted and that authority can
* be satisfied
......@@ -119,25 +139,44 @@ inline bool validate( const Authority& auth ) {
static_assert( std::is_same<decltype(auth.threshold), uint32_t>::value &&
std::is_same<weight_type, uint16_t>::value &&
std::is_same<typename decltype(auth.keys)::value_type, key_weight>::value &&
std::is_same<typename decltype(auth.accounts)::value_type, permission_level_weight>::value,
std::is_same<typename decltype(auth.accounts)::value_type, permission_level_weight>::value &&
std::is_same<typename decltype(auth.waits)::value_type, wait_weight>::value,
"unexpected type for threshold and/or weight in authority" );
if( ( auth.keys.size() + auth.accounts.size() ) > (1 << 16) )
if( ( auth.keys.size() + auth.accounts.size() + auth.waits.size() ) > (1 << 16) )
return false; // overflow protection (assumes weight_type is uint16_t and threshold is of type uint32_t)
const key_weight* prev = nullptr;
for( const auto& k : auth.keys ) {
if( prev && ( prev->key < k.key || prev->key == k.key ) ) return false;
total_weight += k.weight;
prev = &k;
if( auth.threshold == 0 )
return false;
{
const key_weight* prev = nullptr;
for( const auto& k : auth.keys ) {
if( prev && !(prev->key < k.key) ) return false; // TODO: require keys to be sorted in ascending order rather than descending (requires modifying many tests)
total_weight += k.weight;
prev = &k;
}
}
const permission_level_weight* pa = nullptr;
for( const auto& a : auth.accounts ) {
if(pa && ( pa->permission < a.permission || pa->permission == a.permission ) ) return false;
total_weight += a.weight;
pa = &a;
{
const permission_level_weight* prev = nullptr;
for( const auto& a : auth.accounts ) {
if( prev && ( prev->permission >= a.permission ) ) return false; // TODO: require permission_levels to be sorted in ascending order rather than descending (requires modifying many tests)
total_weight += a.weight;
prev = &a;
}
}
{
const wait_weight* prev = nullptr;
if( auth.waits.size() > 0 && auth.waits.front().wait_sec == 0 )
return false;
for( const auto& w : auth.waits ) {
if( prev && ( prev->wait_sec >= w.wait_sec ) ) return false;
total_weight += w.weight;
prev = &w;
}
}
return auth.threshold > 0 && total_weight >= auth.threshold;
return total_weight >= auth.threshold;
}
} } // namespace eosio::chain
......@@ -145,5 +184,6 @@ inline bool validate( const Authority& auth ) {
FC_REFLECT(eosio::chain::permission_level_weight, (permission)(weight) )
FC_REFLECT(eosio::chain::key_weight, (key)(weight) )
FC_REFLECT(eosio::chain::authority, (threshold)(delay_sec)(keys)(accounts))
FC_REFLECT(eosio::chain::shared_authority, (threshold)(delay_sec)(keys)(accounts))
FC_REFLECT(eosio::chain::wait_weight, (wait_sec)(weight) )
FC_REFLECT(eosio::chain::authority, (threshold)(keys)(accounts)(waits) )
FC_REFLECT(eosio::chain::shared_authority, (threshold)(keys)(accounts)(waits) )
......@@ -21,21 +21,26 @@ namespace eosio { namespace chain {
namespace detail {
using meta_permission = static_variant<key_weight, permission_level_weight>;
// Order of the template types in the static_variant matters to meta_permission_comparator.
using meta_permission = static_variant<permission_level_weight, key_weight, wait_weight>;
struct get_weight_visitor {
using result_type = uint32_t;
template<typename Permission>
uint32_t operator()(const Permission& permission) { return permission.weight; }
uint32_t operator()( const Permission& permission ) { return permission.weight; }
};
// Orders permissions descending by weight, and breaks ties with Key permissions being less than Account permissions
// Orders permissions descending by weight, and breaks ties with Wait permissions being less than
// Key permissions which are in turn less than Account permissions
struct meta_permission_comparator {
bool operator()(const meta_permission& a, const meta_permission& b) const {
bool operator()( const meta_permission& lhs, const meta_permission& rhs ) const {
get_weight_visitor scale;
if (a.visit(scale) > b.visit(scale)) return true;
return a.contains<key_weight>() && b.contains<permission_level_weight>();
auto lhs_weight = lhs.visit(scale);
auto lhs_type = lhs.which();
auto rhs_weight = rhs.visit(scale);
auto rhs_type = rhs.which();
return std::tie( lhs_weight, lhs_type ) > std::tie( rhs_weight, rhs_type );
}
};
......@@ -53,34 +58,31 @@ namespace detail {
* @tparam F A callable which takes a single argument of type @ref AccountPermission and returns the corresponding
* authority
*/
template<typename PermissionToAuthorityFunc, typename PermissionVisitor>
template<typename PermissionToAuthorityFunc>
class authority_checker {
private:
PermissionToAuthorityFunc permission_to_authority;
PermissionVisitor permission_visitor;
const std::function<void(uint32_t)>& checktime;
vector<public_key_type> signing_keys; // Making this a flat_set<public_key_type> causes runtime problems with utilities::filter_data_by_marker for some reason. TODO: Figure out why.
vector<public_key_type> provided_keys; // Making this a flat_set<public_key_type> causes runtime problems with utilities::filter_data_by_marker for some reason. TODO: Figure out why.
flat_set<permission_level> provided_permissions;
vector<bool> _used_keys;
fc::microseconds delay_threshold;
fc::microseconds provided_delay;
uint16_t recursion_depth_limit;
public:
authority_checker( PermissionToAuthorityFunc permission_to_authority,
PermissionVisitor permission_visitor,
uint16_t recursion_depth_limit,
const flat_set<public_key_type>& signing_keys,
const flat_set<public_key_type>& provided_keys,
const flat_set<permission_level>& provided_permissions,
fc::microseconds delay_threshold,
fc::microseconds provided_delay,
const std::function<void(uint32_t)>& checktime
)
:permission_to_authority(permission_to_authority)
,permission_visitor(permission_visitor)
,checktime( checktime )
,signing_keys(signing_keys.begin(), signing_keys.end())
,provided_keys(provided_keys.begin(), provided_keys.end())
,provided_permissions(provided_permissions)
,_used_keys(signing_keys.size(), false)
,delay_threshold(delay_threshold)
,_used_keys(provided_keys.size(), false)
,provided_delay(provided_delay)
,recursion_depth_limit(recursion_depth_limit)
{
FC_ASSERT( static_cast<bool>(checktime), "checktime cannot be empty" );
......@@ -88,7 +90,6 @@ namespace detail {
enum permission_cache_status {
being_evaluated,
permission_provided,
permission_unsatisfied,
permission_satisfied
};
......@@ -96,15 +97,15 @@ namespace detail {
typedef map<permission_level, permission_cache_status> permission_cache_type;
bool satisfied( const permission_level& permission,
fc::microseconds override_delay_threshold,
fc::microseconds override_provided_delay,
permission_cache_type* cached_perms = nullptr
)
{
auto delay_threshold_reverter = fc::make_scoped_exit([this, delay = delay_threshold] () mutable {
delay_threshold = delay;
auto delay_reverter = fc::make_scoped_exit( [this, delay = provided_delay] () mutable {
provided_delay = delay;
});
delay_threshold = override_delay_threshold;
provided_delay = override_provided_delay;
return satisfied( permission, cached_perms );
}
......@@ -115,23 +116,21 @@ namespace detail {
if( cached_perms == nullptr )
cached_perms = initialize_permission_cache( cached_permissions );
weight_tally_visitor visitor(*this, *cached_perms, 0);
return ( visitor(permission_level_weight{permission, 1}) > 0 );
}
template<typename AuthorityType>
bool satisfied( const AuthorityType& authority,
fc::microseconds override_delay_threshold,
fc::microseconds override_provided_delay,
permission_cache_type* cached_perms = nullptr
)
{
auto delay_threshold_reverter = fc::make_scoped_exit([this, delay = delay_threshold] () mutable {
delay_threshold = delay;
auto delay_reverter = fc::make_scoped_exit( [this, delay = provided_delay] () mutable {
provided_delay = delay;
});
delay_threshold = override_delay_threshold;
provided_delay = override_provided_delay;
return satisfied( authority, cached_perms );
}
......@@ -149,18 +148,14 @@ namespace detail {
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::filter_data_by_marker(signing_keys, _used_keys, true);
auto range = utilities::filter_data_by_marker(provided_keys, _used_keys, true);
return {range.begin(), range.end()};
}
flat_set<public_key_type> unused_keys() const {
auto range = utilities::filter_data_by_marker(signing_keys, _used_keys, false);
auto range = utilities::filter_data_by_marker(provided_keys, _used_keys, false);
return {range.begin(), range.end()};
}
PermissionVisitor& get_permission_visitor() {
return permission_visitor;
}
static optional<permission_cache_status>
permission_status_in_cache( const permission_cache_type& permissions,
const permission_level& level )
......@@ -179,7 +174,7 @@ namespace detail {
private:
permission_cache_type* initialize_permission_cache( permission_cache_type& cached_permissions ) {
for( const auto& p : provided_permissions ) {
cached_permissions.emplace_hint( cached_permissions.end(), p, permission_provided );
cached_permissions.emplace_hint( cached_permissions.end(), p, permission_satisfied );
}
return &cached_permissions;
}
......@@ -194,10 +189,11 @@ namespace detail {
// Sort key permissions and account permissions together into a single set of meta_permissions
detail::meta_permission_set permissions;
permissions.insert(authority.waits.begin(), authority.waits.end());
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
// Check all permissions, from highest weight to lowest, seeing if provided authorization factors satisfies them or not
weight_tally_visitor visitor(*this, cached_permissions, depth);
for( const auto& permission : permissions )
// If we've got enough weight, to satisfy the authority, return!
......@@ -222,18 +218,23 @@ namespace detail {
,recursion_depth(recursion_depth)
{}
uint32_t operator()(const wait_weight& permission) {
if( checker.provided_delay >= fc::seconds(permission.wait_sec) ) {
total_weight += permission.weight;
}
return total_weight;
}
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;
auto itr = boost::find( checker.provided_keys, permission.key );
if( itr != checker.provided_keys.end() ) {
checker._used_keys[itr - checker.provided_keys.begin()] = true;
total_weight += permission.weight;
}
return total_weight;
}
uint32_t operator()(const permission_level_weight& permission) {
checker.permission_visitor.push_undo();
checker.permission_visitor( permission.permission );
uint32_t operator()(const permission_level_weight& permission) {
auto status = authority_checker::permission_status_in_cache( cached_permissions, permission.permission );
if( !status ) {
if( recursion_depth < checker.recursion_depth_limit ) {
......@@ -244,53 +245,25 @@ namespace detail {
try {
auto&& auth = checker.permission_to_authority( permission.permission );
propagate_error = true;
if( fc::microseconds(auth.delay_sec) > checker.delay_threshold ) {
checker.permission_visitor.pop_undo();
return total_weight; // if delay of permission is higher than the threshold, continue without it
}
auto res = cached_permissions.emplace( permission.permission, being_evaluated );
itr = res.first;
r = checker.satisfied( std::forward<decltype(auth)>(auth), cached_permissions, recursion_depth + 1 );
} catch( const permission_query_exception& ) {
checker.permission_visitor.pop_undo();
if( propagate_error )
throw;
else
return total_weight; // if the permission doesn't exist, continue without it
} catch( ... ) {
checker.permission_visitor.pop_undo();
throw;
}
if( r ) {
total_weight += permission.weight;
itr->second = permission_satisfied;
checker.permission_visitor( permission.permission, false );
checker.permission_visitor.squash_undo();
} else {
itr->second = permission_unsatisfied;
checker.permission_visitor.pop_undo();
}
}
} else if( *status == permission_satisfied ) {
total_weight += permission.weight;
checker.permission_visitor( permission.permission, true );
checker.permission_visitor.squash_undo();
} else if( *status == permission_provided ) {
auto res = cached_permissions.emplace( permission.permission, permission_provided );
try {
checker.permission_to_authority( permission.permission ); // make sure permission exists
} catch( const permission_query_exception& ) {
res.first->second = permission_unsatisfied; // do not bother checking again
checker.permission_visitor.pop_undo();
return total_weight; // if the permission doesn't exist, continue without it
}
total_weight += permission.weight;
res.first->second = permission_satisfied;
checker.permission_visitor( permission.permission, false );
checker.permission_visitor.squash_undo();
}
return total_weight;
}
......@@ -298,39 +271,23 @@ namespace detail {
}; /// authority_checker
template<typename PermissionToAuthorityFunc, typename PermissionVisitor>
template<typename PermissionToAuthorityFunc>
auto make_auth_checker( PermissionToAuthorityFunc&& pta,
PermissionVisitor&& permission_visitor,
uint16_t recursion_depth_limit,
const flat_set<public_key_type>& signing_keys,
const flat_set<public_key_type>& provided_keys,
const flat_set<permission_level>& provided_permissions = flat_set<permission_level>(),
fc::microseconds delay_threshold = fc::microseconds::maximum(),
fc::microseconds provided_delay = fc::microseconds(0),
const std::function<void(uint32_t)>& _checktime = std::function<void(uint32_t)>()
)
{
auto noop_checktime = []( uint32_t ) {};
const auto& checktime = ( static_cast<bool>(_checktime) ? _checktime : noop_checktime );
return authority_checker< PermissionToAuthorityFunc,
PermissionVisitor >( std::forward<PermissionToAuthorityFunc>(pta),
std::forward<PermissionVisitor>(permission_visitor),
recursion_depth_limit,
signing_keys,
provided_permissions,
delay_threshold,
checktime );
return authority_checker< PermissionToAuthorityFunc>( std::forward<PermissionToAuthorityFunc>(pta),
recursion_depth_limit,
provided_keys,
provided_permissions,
provided_delay,
checktime );
}
class noop_permission_visitor {
public:
void push_undo() {}
void pop_undo() {}
void squash_undo() {}
void operator()(const permission_level& perm_level) {} // Called when entering a permission level (may not exist)
void operator()(const permission_level& perm_level, bool repeat) {} // Called when permission level was satisfied
// if repeat == false, the perm_level could possibly not exist; but if repeat == true, then it must exist.
// Note: permission "existing" assumes that permission_to_authority successfully returns an authority if the permission exists,
// and throws a permission_query_exception exception if the permission does not exist.
};
} } // namespace eosio::chain
......@@ -66,10 +66,8 @@ namespace eosio { namespace chain {
* @param provided_delay - the delay satisfied by the transaction
* @param checktime - the function that can be called to track CPU usage and time during the process of checking authorization
* @param allow_unused_keys - true if method should not assert on unused keys
*
* @return the maximum delay among the authorities needed to satisfy the authorizations (throws if authorization was not satsified)
*/
fc::microseconds
void
check_authorization( const vector<action>& actions,
const flat_set<public_key_type>& provided_keys,
const flat_set<permission_level>& provided_permissions = flat_set<permission_level>(),
......@@ -89,10 +87,8 @@ namespace eosio { namespace chain {
* @param provided_delay - the delay considered to be satisfied for the authorization check
* @param checktime - the function that can be called to track CPU usage and time during the process of checking authorization
* @param allow_unused_keys - true if method does not require all keys to be used
*
* @return the maximum delay among the authorities needed to satisfy the authorizations (throws if authorization was not satsified)
*/
fc::microseconds
void
check_authorization( account_name account,
permission_name permission,
const flat_set<public_key_type>& provided_keys,
......@@ -104,7 +100,7 @@ namespace eosio { namespace chain {
flat_set<public_key_type> get_required_keys( const transaction& trx,
const flat_set<public_key_type>& candidate_keys,
fc::microseconds delay_threshold = fc::microseconds(0)
fc::microseconds provided_delay = fc::microseconds(0)
)const;
......@@ -114,11 +110,11 @@ namespace eosio { namespace chain {
const controller& _control;
chainbase::database& _db;
optional<fc::microseconds> check_updateauth_authorization( const updateauth& update, const vector<permission_level>& auths )const;
fc::microseconds check_deleteauth_authorization( const deleteauth& del, const vector<permission_level>& auths )const;
fc::microseconds check_linkauth_authorization( const linkauth& link, const vector<permission_level>& auths )const;
fc::microseconds check_unlinkauth_authorization( const unlinkauth& unlink, const vector<permission_level>& auths )const;
void check_canceldelay_authorization( const canceldelay& cancel, const vector<permission_level>& auths )const;
void check_updateauth_authorization( const updateauth& update, const vector<permission_level>& auths )const;
void check_deleteauth_authorization( const deleteauth& del, const vector<permission_level>& auths )const;
void check_linkauth_authorization( const linkauth& link, const vector<permission_level>& auths )const;
void check_unlinkauth_authorization( const unlinkauth& unlink, const vector<permission_level>& auths )const;
fc::microseconds check_canceldelay_authorization( const canceldelay& cancel, const vector<permission_level>& auths )const;
optional<permission_name> lookup_linked_permission( account_name authorizer_account,
scope_name code_account,
......
......@@ -23,7 +23,6 @@ const static uint64_t producers_account_name = N(producers);
// Active permission of producers account requires greater than 2/3 of the producers to authorize
const static uint64_t majority_producers_permission_name = N(prod.major); // greater than 1/2 of producers needed to authorize
const static uint64_t minority_producers_permission_name = N(prod.minor); // greater than 1/3 of producers needed to authorize0
const static uint64_t any_producer_permission_name = N(prod.any); // any producer needed to authorize
const static uint64_t eosio_auth_scope = N(eosio.auth);
const static uint64_t eosio_all_scope = N(eosio.all);
......@@ -31,6 +30,7 @@ const static uint64_t eosio_all_scope = N(eosio.all);
const static uint64_t active_name = N(active);
const static uint64_t owner_name = N(owner);
const static uint64_t eosio_any_name = N(eosio.any);
const static uint64_t eosio_code_name = N(eosio.code);
const static int block_interval_ms = 500;
const static int block_interval_us = block_interval_ms*1000;
......
......@@ -212,12 +212,12 @@ namespace eosio { namespace chain {
FC_DECLARE_DERIVED_EXCEPTION( authorization_exception, chain_exception,
3090000, "Authorization exception" )
FC_DECLARE_DERIVED_EXCEPTION( tx_missing_sigs, authorization_exception,
3090001, "signatures do not satisfy declared authorizations" )
FC_DECLARE_DERIVED_EXCEPTION( tx_duplicate_sig, authorization_exception,
3090002, "duplicate signature included" )
3090001, "duplicate signature included" )
FC_DECLARE_DERIVED_EXCEPTION( tx_irrelevant_sig, authorization_exception,
3090003, "irrelevant signature included" )
3090002, "irrelevant signature included" )
FC_DECLARE_DERIVED_EXCEPTION( unsatisfied_authorization, authorization_exception,
3090003, "provided keys, permissions, and delays do not satisfy declared authorizations" )
FC_DECLARE_DERIVED_EXCEPTION( missing_auth_exception, authorization_exception,
3090004, "missing required authority" )
FC_DECLARE_DERIVED_EXCEPTION( irrelevant_auth_exception, authorization_exception,
......
......@@ -17,45 +17,38 @@ namespace eosio { namespace chain {
permission_name name; ///< human-readable name for the permission
shared_authority auth; ///< authority required to execute this permission
time_point last_updated; ///< the last time this authority was updated
fc::microseconds delay; ///< delay associated with this permission
/**
* @brief Checks if this permission is equivalent or greater than other
* @tparam Index The permission_index
* @return a fc::microseconds set to the maximum delay encountered between this and the permission that is other;
* empty optional otherwise
* @return true if this permission is equivalent or greater than other, false otherwise
*
* Permissions are organized hierarchically such that a parent permission is strictly more powerful than its
* children/grandchildren. This method checks whether this permission is of greater or equal power (capable of
* satisfying) permission @ref other. The returned value is an optional<fc::microseconds> that will indicate the
* maximum delay encountered walking the hierarchy between this permission and other, if this satisfies other,
* otherwise an empty optional is returned.
* satisfying) permission @ref other.
*/
template <typename Index>
optional<fc::microseconds> satisfies(const permission_object& other, const Index& permission_index) const {
bool satisfies(const permission_object& other, const Index& permission_index) const {
// If the owners are not the same, this permission cannot satisfy other
if( owner != other.owner )
return optional<fc::microseconds>();
return false;
// if this permission satisfies other, then other's delay and this delay will have to contribute
auto max_delay = other.delay > delay ? other.delay : delay;
// If this permission matches other, or is the immediate parent of other, then this permission satisfies other
if( id == other.id || id == other.parent )
return optional<fc::microseconds>(max_delay);
return true;
// Walk up other's parent tree, seeing if we find this permission. If so, this permission satisfies other
const permission_object* parent = &*permission_index.template get<by_id>().find(other.parent);
while( parent ) {
if( max_delay < parent->delay )
max_delay = parent->delay;
if( id == parent->parent )
return optional<fc::microseconds>(max_delay);
return true;
if( parent->parent._id == 0 )
return optional<fc::microseconds>();
return false;
parent = &*permission_index.template get<by_id>().find(parent->parent);
}
// This permission is not a parent of other, and so does not satisfy other
return optional<fc::microseconds>();
return false;
}
};
......@@ -124,7 +117,7 @@ CHAINBASE_SET_INDEX_TYPE(eosio::chain::permission_object, eosio::chain::permissi
CHAINBASE_SET_INDEX_TYPE(eosio::chain::permission_usage_object, eosio::chain::permission_usage_index)
FC_REFLECT(chainbase::oid<eosio::chain::permission_object>, (_id))
FC_REFLECT(eosio::chain::permission_object, (id)(owner)(parent)(name)(auth)(last_updated)(delay))
FC_REFLECT(eosio::chain::permission_object, (id)(owner)(parent)(name)(auth)(last_updated))
FC_REFLECT(chainbase::oid<eosio::chain::permission_usage_object>, (_id))
FC_REFLECT(eosio::chain::permission_usage_object, (id)(account)(permission)(last_used))
......@@ -103,7 +103,7 @@ flat_set<public_key_type> transaction::get_signature_keys( const vector<signatur
}
bool successful_insertion = false;
std::tie(std::ignore, successful_insertion) = recovered_pub_keys.insert(recov);
EOS_ASSERT( allow_duplicate_keys || successful_insertion, tx_irrelevant_sig,
EOS_ASSERT( allow_duplicate_keys || successful_insertion, tx_duplicate_sig,
"transaction includes more than one signature signed using the same key associated with public key: ${key}",
("key", recov)
);
......
......@@ -762,10 +762,10 @@ class permission_api : public context_aware_api {
public:
using context_aware_api::context_aware_api;
int64_t check_transaction_authorization( array_ptr<char> trx_data, size_t trx_size,
array_ptr<char> pubkeys_data, size_t pubkeys_size,
array_ptr<char> perms_data, size_t perms_size
)
bool check_transaction_authorization( array_ptr<char> trx_data, size_t trx_size,
array_ptr<char> pubkeys_data, size_t pubkeys_size,
array_ptr<char> perms_data, size_t perms_size
)
{
transaction trx = fc::raw::unpack<transaction>( trx_data, trx_size );
......@@ -776,26 +776,26 @@ class permission_api : public context_aware_api {
unpack_provided_permissions( provided_permissions, perms_data, perms_size );
try {
auto delay = context.control
.get_authorization_manager()
.check_authorization( trx.actions,
provided_keys,
provided_permissions,
fc::seconds(trx.delay_sec),
std::bind(&apply_context::checktime, &context, std::placeholders::_1),
false
);
return delay.count();
context.control
.get_authorization_manager()
.check_authorization( trx.actions,
provided_keys,
provided_permissions,
fc::seconds(trx.delay_sec),
std::bind(&apply_context::checktime, &context, std::placeholders::_1),
false
);
return true;
} catch( const authorization_exception& e ) {}
return -1;
return false;
}
int64_t check_permission_authorization( account_name account, permission_name permission,
array_ptr<char> pubkeys_data, size_t pubkeys_size,
array_ptr<char> perms_data, size_t perms_size,
uint64_t delay_us
)
bool check_permission_authorization( account_name account, permission_name permission,
array_ptr<char> pubkeys_data, size_t pubkeys_size,
array_ptr<char> perms_data, size_t perms_size,
uint64_t delay_us
)
{
EOS_ASSERT( delay_us <= static_cast<uint64_t>(std::numeric_limits<int64_t>::max()),
action_validate_exception, "provided delay is too large" );
......@@ -807,20 +807,20 @@ class permission_api : public context_aware_api {
unpack_provided_permissions( provided_permissions, perms_data, perms_size );
try {
auto delay = context.control
.get_authorization_manager()
.check_authorization( account,
permission,
provided_keys,
provided_permissions,
fc::microseconds(delay_us),
std::bind(&apply_context::checktime, &context, std::placeholders::_1),
false
);
return delay.count();
context.control
.get_authorization_manager()
.check_authorization( account,
permission,
provided_keys,
provided_permissions,
fc::microseconds(delay_us),
std::bind(&apply_context::checktime, &context, std::placeholders::_1),
false
);
return true;
} catch( const authorization_exception& e ) {}
return -1;
return false;
}
int64_t get_permission_last_used( account_name account, permission_name permission) {
......@@ -830,7 +830,7 @@ class permission_api : public context_aware_api {
int64_t get_account_creation_date( account_name account ) {
return time_point(context.db.get<account_object, by_name>(account).creation_date).time_since_epoch().count();
}
private:
void unpack_provided_keys( flat_set<public_key_type>& keys, const char* pubkeys_data, size_t pubkeys_size ) {
......@@ -1691,8 +1691,8 @@ REGISTER_INTRINSICS(crypto_api,
REGISTER_INTRINSICS(permission_api,
(check_transaction_authorization, int64_t(int, int, int, int, int, int) )
(check_permission_authorization, int64_t(int64_t, int64_t, int, int, int, int, int64_t) )
(check_transaction_authorization, int(int, int, int, int, int, int) )
(check_permission_authorization, int(int64_t, int64_t, int, int, int, int, int64_t) )
(get_permission_last_used, int64_t(int64_t, int64_t) )
(get_account_creation_date, int64_t(int64_t) )
);
......
......@@ -116,10 +116,14 @@ namespace eosio { namespace testing {
uint32_t expiration = DEFAULT_EXPIRATION_DELTA,
uint32_t delay_sec = 0)const;
vector<transaction_trace_ptr> create_accounts( vector<account_name> names, bool multisig = false ) {
vector<transaction_trace_ptr> create_accounts( vector<account_name> names,
bool multisig = false,
bool include_code = true
)
{
vector<transaction_trace_ptr> traces;
traces.reserve(names.size());
for( auto n : names ) traces.emplace_back(create_account(n, config::system_account_name, multisig ));
for( auto n : names ) traces.emplace_back( create_account( n, config::system_account_name, multisig, include_code ) );
return traces;
}
......@@ -136,7 +140,11 @@ namespace eosio { namespace testing {
void delete_authority( account_name account, permission_name perm, const vector<permission_level>& auths, const vector<private_key_type>& keys );
void delete_authority( account_name account, permission_name perm );
transaction_trace_ptr create_account( account_name name, account_name creator = config::system_account_name, bool multisig = false );
transaction_trace_ptr create_account( account_name name,
account_name creator = config::system_account_name,
bool multisig = false,
bool include_code = true
);
transaction_trace_ptr push_reqauth( account_name from, const vector<permission_level>& auths, const vector<private_key_type>& keys );
transaction_trace_ptr push_reqauth(account_name from, string role, bool multi_sig = false);
......@@ -173,7 +181,7 @@ namespace eosio { namespace testing {
void set_code( account_name name, const char* wast, const private_key_type* signer = nullptr );
void set_code( account_name name, const vector<uint8_t> wasm, const private_key_type* signer = nullptr );
void set_abi( account_name name, const char* abi_json, const private_key_type* signer = nullptr );
void set_abi( account_name name, const char* abi_json, const private_key_type* signer = nullptr );
bool chain_has_transaction( const transaction_id_type& txid ) const;
const transaction_receipt& get_transaction_receipt( const transaction_id_type& txid ) const;
......
......@@ -183,24 +183,45 @@ namespace eosio { namespace testing {
}
transaction_trace_ptr base_tester::create_account( account_name a, account_name creator, bool multisig ) {
transaction_trace_ptr base_tester::create_account( account_name a, account_name creator, bool multisig, bool include_code ) {
signed_transaction trx;
set_transaction_headers(trx);
authority owner_auth;
if (multisig) {
if( multisig ) {
// multisig between account's owner key and creators active permission
owner_auth = authority(2, {key_weight{get_public_key( a, "owner" ), 1}}, {permission_level_weight{{creator, config::active_name}, 1}});
} else {
owner_auth = authority( get_public_key( a, "owner" ) );
}
authority active_auth( get_public_key( a, "active" ) );
auto sort_permissions = []( authority& auth ) {
std::sort( auth.accounts.begin(), auth.accounts.end(),
[]( const permission_level_weight& lhs, const permission_level_weight& rhs ) {
return lhs.permission < rhs.permission;
}
);
};
if( include_code ) {
FC_ASSERT( owner_auth.threshold <= std::numeric_limits<weight_type>::max(), "threshold is too high" );
FC_ASSERT( active_auth.threshold <= std::numeric_limits<weight_type>::max(), "threshold is too high" );
owner_auth.accounts.push_back( permission_level_weight{ {a, config::eosio_code_name},
static_cast<weight_type>(owner_auth.threshold) } );
sort_permissions(owner_auth);
active_auth.accounts.push_back( permission_level_weight{ {a, config::eosio_code_name},
static_cast<weight_type>(active_auth.threshold) } );
sort_permissions(active_auth);
}
trx.actions.emplace_back( vector<permission_level>{{creator,config::active_name}},
newaccount{
.creator = creator,
.name = a,
.owner = owner_auth,
.active = authority( get_public_key( a, "active" ) )
.active = active_auth,
});
set_transaction_headers(trx);
......
......@@ -35,7 +35,7 @@ void chain_api_plugin::plugin_initialize(const variables_map&) {}
if (body.empty()) body = "{}"; \
auto result = api_handle.call_name(fc::json::from_string(body).as<api_namespace::call_name ## _params>()); \
cb(http_response_code, fc::json::to_string(result)); \
} catch (chain::tx_missing_sigs& e) { \
} catch (chain::unsatisfied_authorization& e) { \
error_results results{401, "UnAuthorized", e}; \
cb(401, fc::json::to_string(results)); \
} catch (chain::tx_duplicate& e) { \
......
......@@ -972,7 +972,7 @@ BOOST_AUTO_TEST_CASE(irrelevant_sig_soft_check) {
trx.signatures.clear();
trx.sign( chain.get_private_key( config::system_account_name, "active" ), chain_id_type() );
trx.sign( chain.get_private_key( config::system_account_name, "active" ), chain_id_type() );
BOOST_REQUIRE_THROW(chain.push_transaction( trx ), tx_irrelevant_sig);
BOOST_REQUIRE_THROW(chain.push_transaction( trx ), tx_duplicate_sig);
// Sign the transaction properly and push to the block
trx.signatures.clear();
......@@ -1128,7 +1128,7 @@ BOOST_AUTO_TEST_CASE(get_required_keys)
});
chain.set_transaction_headers(trx);
BOOST_REQUIRE_THROW(chain.push_transaction(trx), tx_missing_sigs);
BOOST_REQUIRE_THROW(chain.push_transaction(trx), unsatisfied_authorization);
const auto priv_key_not_needed_1 = chain.get_private_key("alice", "blah");
const auto priv_key_not_needed_2 = chain.get_private_key("alice", "owner");
......
......@@ -1871,20 +1871,20 @@ BOOST_AUTO_TEST_CASE(general)
"keyweight_arr": [{"key":"EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", "weight":"100"},{"key":"EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", "weight":"200"}],
"authority": {
"threshold":"10",
"delay_sec":"0",
"keys":[{"key":"EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", "weight":100},{"key":"EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", "weight":200}],
"accounts":[{"permission":{"actor":"acc1","permission":"permname1"},"weight":"1"},{"permission":{"actor":"acc2","permission":"permname2"},"weight":"2"}]
"accounts":[{"permission":{"actor":"acc1","permission":"permname1"},"weight":"1"},{"permission":{"actor":"acc2","permission":"permname2"},"weight":"2"}],
"waits":[]
},
"authority_arr": [{
"threshold":"10",
"delay_sec":"0",
"keys":[{"key":"EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", "weight":"100"},{"key":"EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", "weight":"200"}],
"accounts":[{"permission":{"actor":"acc1","permission":"permname1"},"weight":"1"},{"permission":{"actor":"acc2","permission":"permname2"},"weight":"2"}]
"accounts":[{"permission":{"actor":"acc1","permission":"permname1"},"weight":"1"},{"permission":{"actor":"acc2","permission":"permname2"},"weight":"2"}],
"waits":[]
},{
"threshold":"10",
"delay_sec":"0",
"keys":[{"key":"EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", "weight":"100"},{"key":"EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", "weight":"200"}],
"accounts":[{"permission":{"actor":"acc1","permission":"permname1"},"weight":"1"},{"permission":{"actor":"acc2","permission":"permname2"},"weight":"2"}]
"accounts":[{"permission":{"actor":"acc1","permission":"permname1"},"weight":"1"},{"permission":{"actor":"acc2","permission":"permname2"},"weight":"2"}],
"waits":[]
}],
"typedef" : {"new_type_name":"new", "type":"old"},
"typedef_arr": [{"new_type_name":"new", "type":"old"},{"new_type_name":"new", "type":"old"}],
......@@ -2056,11 +2056,11 @@ BOOST_AUTO_TEST_CASE(updateauth_test)
"parent" : "updauth.prnt",
"auth" : {
"threshold" : "2147483145",
"delay_sec" : "0",
"keys" : [ {"key" : "EOS65rXebLhtk2aTTzP4e9x1AQZs7c5NNXJp89W8R3HyaA6Zyd4im", "weight" : 57005},
{"key" : "EOS5eVr9TVnqwnUBNwf9kwMTbrHvX5aPyyEG97dz2b2TNeqWRzbJf", "weight" : 57605} ],
"accounts" : [ {"permission" : {"actor" : "prm.acct1", "permission" : "prm.prm1"}, "weight" : 53005 },
{"permission" : {"actor" : "prm.acct2", "permission" : "prm.prm2"}, "weight" : 53405 }]
{"permission" : {"actor" : "prm.acct2", "permission" : "prm.prm2"}, "weight" : 53405 } ],
"waits" : []
}
}
)=====";
......@@ -2153,7 +2153,6 @@ BOOST_AUTO_TEST_CASE(newaccount_test)
"name" : "newacct.name",
"owner" : {
"threshold" : 2147483145,
"delay_sec" : 0,
"keys" : [ {"key" : "EOS65rXebLhtk2aTTzP4e9x1AQZs7c5NNXJp89W8R3HyaA6Zyd4im", "weight" : 57005},
{"key" : "EOS5eVr9TVnqwnUBNwf9kwMTbrHvX5aPyyEG97dz2b2TNeqWRzbJf", "weight" : 57605} ],
"accounts" : [ {"permission" : {"actor" : "prm.acct1", "permission" : "prm.prm1"}, "weight" : 53005 },
......@@ -2161,7 +2160,6 @@ BOOST_AUTO_TEST_CASE(newaccount_test)
},
"active" : {
"threshold" : 2146483145,
"delay_sec" : 0,
"keys" : [ {"key" : "EOS65rXebLhtk2aTTzP4e9x1AQZs7c5NNXJp89W8R3HyaA6Zyd4im", "weight" : 57005},
{"key" : "EOS5eVr9TVnqwnUBNwf9kwMTbrHvX5aPyyEG97dz2b2TNeqWRzbJf", "weight" : 57605} ],
"accounts" : [ {"permission" : {"actor" : "prm.acct1", "permission" : "prm.prm1"}, "weight" : 53005 },
......
......@@ -196,7 +196,7 @@ bool is_access_violation(const Runtime::Exception& e) { return true; }
bool is_assert_exception(fc::assert_exception const & e) { return true; }
bool is_page_memory_error(page_memory_error const &e) { return true; }
bool is_tx_missing_sigs(tx_missing_sigs const & e) { return true;}
bool is_unsatisfied_authorization(unsatisfied_authorization const & e) { return true;}
bool is_wasm_execution_error(eosio::chain::wasm_execution_error const& e) {return true;}
bool is_tx_net_usage_exceeded(const tx_net_usage_exceeded& e) { return true; }
bool is_tx_cpu_usage_exceeded(const tx_cpu_usage_exceeded& e) { return true; }
......@@ -294,8 +294,8 @@ BOOST_FIXTURE_TEST_CASE(action_tests, TESTER) { try {
auto res = test.push_transaction(trx);
BOOST_CHECK_EQUAL(res->receipt->status, transaction_receipt::executed);
};
BOOST_CHECK_EXCEPTION(test_require_notice(*this, raw_bytes, scope), tx_missing_sigs,
[](const tx_missing_sigs& e) {
BOOST_CHECK_EXCEPTION(test_require_notice(*this, raw_bytes, scope), unsatisfied_authorization,
[](const unsatisfied_authorization& e) {
return expect_assert_message(e, "transaction declares authority");
}
);
......@@ -901,7 +901,7 @@ BOOST_FIXTURE_TEST_CASE(deferred_transaction_tests, TESTER) { try {
dtt_action dtt_act1;
dtt_act1.payer = N(alice);
BOOST_CHECK_THROW(CALL_TEST_FUNCTION(*this, "test_transaction", "send_deferred_tx_with_dtt_action", fc::raw::pack(dtt_act1)), missing_auth_exception);
// Send a tx which in turn sends a deferred tx with the deferred tx's receiver != this tx receiver
// This will include the authorization of the receiver, and impose any related delay associated with the authority
// We set the authorization delay to be 10 sec here, and since the deferred tx delay is set to be 5 sec, so this tx should fail
......@@ -909,18 +909,27 @@ BOOST_FIXTURE_TEST_CASE(deferred_transaction_tests, TESTER) { try {
dtt_act2.deferred_account = N(testapi2);
dtt_act2.permission_name = N(additional);
dtt_act2.delay_sec = 5;
auto auth = authority(get_public_key("testapi", name(dtt_act2.permission_name).to_string()), 10);
auth.accounts.push_back( permission_level_weight{{N(testapi), config::eosio_code_name}, 1} );
push_action(config::system_account_name, updateauth::get_name(), "testapi", fc::mutable_variant_object()
("account", "testapi")
("permission", name(dtt_act2.permission_name))
("parent", "active")
("auth", authority(get_public_key("testapi", name(dtt_act2.permission_name).to_string()), 10)));
("auth", auth)
);
push_action(config::system_account_name, linkauth::get_name(), "testapi", fc::mutable_variant_object()
("account", "testapi")
("code", name(dtt_act2.deferred_account))
("type", name(dtt_act2.deferred_action))
("requirement", name(dtt_act2.permission_name)));
BOOST_CHECK_THROW(CALL_TEST_FUNCTION(*this, "test_transaction", "send_deferred_tx_with_dtt_action", fc::raw::pack(dtt_act2)), insufficient_delay_exception);
BOOST_CHECK_THROW(CALL_TEST_FUNCTION(*this, "test_transaction", "send_deferred_tx_with_dtt_action", fc::raw::pack(dtt_act2)), unsatisfied_authorization);
// But if the deferred transaction has a sufficient delay, then it should work.
dtt_act2.delay_sec = 10;
CALL_TEST_FUNCTION(*this, "test_transaction", "send_deferred_tx_with_dtt_action", fc::raw::pack(dtt_act2));
// Meanwhile, if the deferred tx receiver == this tx receiver, the delay will be ignored, this tx should succeed
dtt_action dtt_act3;
dtt_act3.deferred_account = N(testapi);
......@@ -937,11 +946,11 @@ BOOST_FIXTURE_TEST_CASE(deferred_transaction_tests, TESTER) { try {
// - the deferred transaction will not care about the delay of the authorization
push_action(config::system_account_name, N(setpriv), config::system_account_name, mutable_variant_object()
("account", "testapi")
("is_priv", 1));
("is_priv", 1));
CALL_TEST_FUNCTION(*this, "test_transaction", "send_deferred_tx_with_dtt_action", fc::raw::pack(dtt_act1));
CALL_TEST_FUNCTION(*this, "test_transaction", "send_deferred_tx_with_dtt_action", fc::raw::pack(dtt_act2));
}
BOOST_REQUIRE_EQUAL( validate(), true );
} FC_LOG_AND_RETHROW() }
......@@ -1528,7 +1537,7 @@ BOOST_FIXTURE_TEST_CASE(permission_tests, TESTER) { try {
}
})
);
BOOST_CHECK_EQUAL( int64_t(0), get_result_int64() );
BOOST_CHECK_EQUAL( int64_t(1), get_result_int64() );
CALL_TEST_FUNCTION( *this, "test_permission", "check_authorization",
fc::raw::pack( check_auth {
......@@ -1539,7 +1548,7 @@ BOOST_FIXTURE_TEST_CASE(permission_tests, TESTER) { try {
}
})
);
BOOST_CHECK_EQUAL( int64_t(-1), get_result_int64() );
BOOST_CHECK_EQUAL( int64_t(0), get_result_int64() );
CALL_TEST_FUNCTION( *this, "test_permission", "check_authorization",
fc::raw::pack( check_auth {
......@@ -1551,7 +1560,7 @@ BOOST_FIXTURE_TEST_CASE(permission_tests, TESTER) { try {
}
})
);
BOOST_CHECK_EQUAL( int64_t(-1), get_result_int64() ); // Failure due to irrelevant signatures
BOOST_CHECK_EQUAL( int64_t(0), get_result_int64() ); // Failure due to irrelevant signatures
CALL_TEST_FUNCTION( *this, "test_permission", "check_authorization",
fc::raw::pack( check_auth {
......@@ -1562,7 +1571,7 @@ BOOST_FIXTURE_TEST_CASE(permission_tests, TESTER) { try {
}
})
);
BOOST_CHECK_EQUAL( int64_t(-1), get_result_int64() );
BOOST_CHECK_EQUAL( int64_t(0), get_result_int64() );
CALL_TEST_FUNCTION( *this, "test_permission", "check_authorization",
fc::raw::pack( check_auth {
......@@ -1571,7 +1580,7 @@ BOOST_FIXTURE_TEST_CASE(permission_tests, TESTER) { try {
.pubkeys = {}
})
);
BOOST_CHECK_EQUAL( int64_t(-1), get_result_int64() );
BOOST_CHECK_EQUAL( int64_t(0), get_result_int64() );
CALL_TEST_FUNCTION( *this, "test_permission", "check_authorization",
fc::raw::pack( check_auth {
......@@ -1582,7 +1591,7 @@ BOOST_FIXTURE_TEST_CASE(permission_tests, TESTER) { try {
}
})
);
BOOST_CHECK_EQUAL( int64_t(-1), get_result_int64() );
BOOST_CHECK_EQUAL( int64_t(0), get_result_int64() );
/*
BOOST_CHECK_EXCEPTION(CALL_TEST_FUNCTION( *this, "test_permission", "check_authorization",
......
......@@ -26,7 +26,7 @@ BOOST_FIXTURE_TEST_CASE( missing_sigs, TESTER ) { try {
create_accounts( {N(alice)} );
produce_block();
BOOST_REQUIRE_THROW( push_reqauth( N(alice), {permission_level{N(alice), config::active_name}}, {} ), tx_missing_sigs );
BOOST_REQUIRE_THROW( push_reqauth( N(alice), {permission_level{N(alice), config::active_name}}, {} ), unsatisfied_authorization );
auto trace = push_reqauth(N(alice), "owner");
produce_block();
......@@ -39,7 +39,7 @@ BOOST_FIXTURE_TEST_CASE( missing_multi_sigs, TESTER ) { try {
create_account(N(alice), config::system_account_name, true);
produce_block();
BOOST_REQUIRE_THROW(push_reqauth(N(alice), "owner"), tx_missing_sigs); // without multisig
BOOST_REQUIRE_THROW(push_reqauth(N(alice), "owner"), unsatisfied_authorization); // without multisig
auto trace = push_reqauth(N(alice), "owner", true); // with multisig
produce_block();
......@@ -288,7 +288,7 @@ BOOST_AUTO_TEST_CASE(link_then_update_auth) { try {
// Update "first" auth public key
chain.set_authority("alice", "first", second_pub_key, "active");
// Authority updated, using previous "first" auth should fail on linked auth
BOOST_CHECK_THROW(chain.push_reqauth("alice", { permission_level{N(alice), "first"} }, { first_priv_key }), tx_missing_sigs);
BOOST_CHECK_THROW(chain.push_reqauth("alice", { permission_level{N(alice), "first"} }, { first_priv_key }), unsatisfied_authorization);
// Using updated authority, should succeed
chain.push_reqauth("alice", { permission_level{N(alice), "first"} }, { second_priv_key });
......@@ -303,14 +303,14 @@ try {
// Verify account created properly
const auto& joe_owner_authority = chain.get<permission_object, by_owner>(boost::make_tuple("joe", "owner"));
BOOST_TEST(joe_owner_authority.auth.threshold == 1);
BOOST_TEST(joe_owner_authority.auth.accounts.size() == 0);
BOOST_TEST(joe_owner_authority.auth.accounts.size() == 1);
BOOST_TEST(joe_owner_authority.auth.keys.size() == 1);
BOOST_TEST(string(joe_owner_authority.auth.keys[0].key) == string(chain.get_public_key("joe", "owner")));
BOOST_TEST(joe_owner_authority.auth.keys[0].weight == 1);
const auto& joe_active_authority = chain.get<permission_object, by_owner>(boost::make_tuple("joe", "active"));
BOOST_TEST(joe_active_authority.auth.threshold == 1);
BOOST_TEST(joe_active_authority.auth.accounts.size() == 0);
BOOST_TEST(joe_active_authority.auth.accounts.size() == 1);
BOOST_TEST(joe_active_authority.auth.keys.size() == 1);
BOOST_TEST(string(joe_active_authority.auth.keys[0].key) == string(chain.get_public_key("joe", "active")));
BOOST_TEST(joe_active_authority.auth.keys[0].weight == 1);
......
......@@ -1137,18 +1137,22 @@ BOOST_AUTO_TEST_CASE( link_delay_link_change_test ) { try {
BOOST_REQUIRE_EQUAL(asset::from_string("0.0000 CUR"), liquid_balance);
BOOST_REQUIRE_EXCEPTION(
chain.push_action(config::system_account_name, linkauth::get_name(), tester_account, fc::mutable_variant_object()
chain.push_action( config::system_account_name, linkauth::get_name(),
vector<permission_level>{permission_level{tester_account, N(first)}},
fc::mutable_variant_object()
("account", "tester")
("code", eosio_token)
("type", "transfer")
("requirement", "second"),
30, 3),
insufficient_delay_exception,
fc_exception_message_starts_with("authorization imposes a delay")
unsatisfied_authorization,
fc_exception_message_starts_with("transaction declares authority")
);
// this transaction will be delayed 20 blocks
chain.push_action(config::system_account_name, linkauth::get_name(), tester_account, fc::mutable_variant_object()
chain.push_action( config::system_account_name, linkauth::get_name(),
vector<permission_level>{{tester_account, N(first)}},
fc::mutable_variant_object()
("account", "tester")
("code", eosio_token)
("type", "transfer")
......@@ -1330,13 +1334,16 @@ BOOST_AUTO_TEST_CASE( link_delay_unlink_test ) { try {
BOOST_REQUIRE_EQUAL(asset::from_string("0.0000 CUR"), liquid_balance);
BOOST_REQUIRE_EXCEPTION(
chain.push_action(config::system_account_name, unlinkauth::get_name(), tester_account, fc::mutable_variant_object()
("account", "tester")
("code", eosio_token)
("type", "transfer"),
30, 7),
insufficient_delay_exception,
fc_exception_message_starts_with("authorization imposes a delay")
chain.push_action( config::system_account_name, unlinkauth::get_name(),
vector<permission_level>{{tester_account, N(first)}},
fc::mutable_variant_object()
("account", "tester")
("code", eosio_token)
("type", "transfer"),
30, 7
),
unsatisfied_authorization,
fc_exception_message_starts_with("transaction declares authority")
);
// this transaction will be delayed 20 blocks
......@@ -1854,15 +1861,18 @@ BOOST_AUTO_TEST_CASE( canceldelay_test ) { try {
BOOST_REQUIRE_EQUAL(asset::from_string("0.0000 CUR"), liquid_balance);
BOOST_REQUIRE_EXCEPTION(
chain.push_action(config::system_account_name, updateauth::get_name(), tester_account, fc::mutable_variant_object()
chain.push_action( config::system_account_name,
updateauth::get_name(),
vector<permission_level>{{tester_account, N(first)}},
fc::mutable_variant_object()
("account", "tester")
("permission", "first")
("parent", "active")
("auth", authority(chain.get_public_key(tester_account, "first"))),
30, 7
),
insufficient_delay_exception,
fc_exception_message_starts_with("authorization imposes a delay")
unsatisfied_authorization,
fc_exception_message_starts_with("transaction declares authority")
);
// this transaction will be delayed 20 blocks
......
......@@ -224,24 +224,23 @@ BOOST_AUTO_TEST_CASE(authority_checker)
auto GetNullAuthority = [](auto){abort(); return authority();};
permission_visitor pv;
auto A = authority(2, {key_weight{a, 1}, key_weight{b, 1}});
{
auto checker = make_auth_checker(GetNullAuthority, pv, 2, {a, b});
auto checker = make_auth_checker(GetNullAuthority, 2, {a, b});
BOOST_TEST(checker.satisfied(A));
BOOST_TEST(checker.all_keys_used());
BOOST_TEST(checker.used_keys().size() == 2);
BOOST_TEST(checker.unused_keys().size() == 0);
}
{
auto checker = make_auth_checker(GetNullAuthority, pv, 2, {a, c});
auto checker = make_auth_checker(GetNullAuthority, 2, {a, c});
BOOST_TEST(!checker.satisfied(A));
BOOST_TEST(!checker.all_keys_used());
BOOST_TEST(checker.used_keys().size() == 0);
BOOST_TEST(checker.unused_keys().size() == 2);
}
{
auto checker = make_auth_checker(GetNullAuthority, pv, 2, {a, b, c});
auto checker = make_auth_checker(GetNullAuthority, 2, {a, b, c});
BOOST_TEST(checker.satisfied(A));
BOOST_TEST(!checker.all_keys_used());
BOOST_TEST(checker.used_keys().size() == 2);
......@@ -251,27 +250,27 @@ BOOST_AUTO_TEST_CASE(authority_checker)
BOOST_TEST(checker.unused_keys().count(c) == 1);
}
{
auto checker = make_auth_checker(GetNullAuthority, pv, 2, {b, c});
auto checker = make_auth_checker(GetNullAuthority, 2, {b, c});
BOOST_TEST(!checker.satisfied(A));
BOOST_TEST(!checker.all_keys_used());
BOOST_TEST(checker.used_keys().size() == 0);
}
A = authority(3, {key_weight{a, 1}, key_weight{b, 1}, key_weight{c, 1}});
BOOST_TEST(make_auth_checker(GetNullAuthority, pv, 2, {c, b, a}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetNullAuthority, pv, 2, {a, b}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetNullAuthority, pv, 2, {a, c}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetNullAuthority, pv, 2, {b, c}).satisfied(A));
BOOST_TEST(make_auth_checker(GetNullAuthority, 2, {c, b, a}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetNullAuthority, 2, {a, b}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetNullAuthority, 2, {a, c}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetNullAuthority, 2, {b, c}).satisfied(A));
A = authority(1, {key_weight{a, 1}, key_weight{b, 1}});
BOOST_TEST(make_auth_checker(GetNullAuthority, pv, 2, {a}).satisfied(A));
BOOST_TEST(make_auth_checker(GetNullAuthority, pv, 2, {b}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetNullAuthority, pv, 2, {c}).satisfied(A));
BOOST_TEST(make_auth_checker(GetNullAuthority, 2, {a}).satisfied(A));
BOOST_TEST(make_auth_checker(GetNullAuthority, 2, {b}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetNullAuthority, 2, {c}).satisfied(A));
A = authority(1, {key_weight{a, 2}, key_weight{b, 1}});
BOOST_TEST(make_auth_checker(GetNullAuthority, pv, 2, {a}).satisfied(A));
BOOST_TEST(make_auth_checker(GetNullAuthority, pv, 2, {b}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetNullAuthority, pv, 2, {c}).satisfied(A));
BOOST_TEST(make_auth_checker(GetNullAuthority, 2, {a}).satisfied(A));
BOOST_TEST(make_auth_checker(GetNullAuthority, 2, {b}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetNullAuthority, 2, {c}).satisfied(A));
auto GetCAuthority = [c](auto){
return authority(1, {key_weight{c, 1}});
......@@ -279,26 +278,26 @@ BOOST_AUTO_TEST_CASE(authority_checker)
A = authority(2, {key_weight{a, 2}, key_weight{b, 1}}, {permission_level_weight{{"hello", "world"}, 1}});
{
auto checker = make_auth_checker(GetCAuthority, pv, 2, {a});
auto checker = make_auth_checker(GetCAuthority, 2, {a});
BOOST_TEST(checker.satisfied(A));
BOOST_TEST(checker.all_keys_used());
}
{
auto checker = make_auth_checker(GetCAuthority, pv, 2, {b});
auto checker = make_auth_checker(GetCAuthority, 2, {b});
BOOST_TEST(!checker.satisfied(A));
BOOST_TEST(checker.used_keys().size() == 0);
BOOST_TEST(checker.unused_keys().size() == 1);
BOOST_TEST(checker.unused_keys().count(b) == 1);
}
{
auto checker = make_auth_checker(GetCAuthority, pv, 2, {c});
auto checker = make_auth_checker(GetCAuthority, 2, {c});
BOOST_TEST(!checker.satisfied(A));
BOOST_TEST(checker.used_keys().size() == 0);
BOOST_TEST(checker.unused_keys().size() == 1);
BOOST_TEST(checker.unused_keys().count(c) == 1);
}
{
auto checker = make_auth_checker(GetCAuthority, pv, 2, {b, c});
auto checker = make_auth_checker(GetCAuthority, 2, {b, c});
BOOST_TEST(checker.satisfied(A));
BOOST_TEST(checker.all_keys_used());
BOOST_TEST(checker.used_keys().size() == 2);
......@@ -307,7 +306,7 @@ BOOST_AUTO_TEST_CASE(authority_checker)
BOOST_TEST(checker.used_keys().count(c) == 1);
}
{
auto checker = make_auth_checker(GetCAuthority, pv, 2, {b, c, a});
auto checker = make_auth_checker(GetCAuthority, 2, {b, c, a});
BOOST_TEST(checker.satisfied(A));
BOOST_TEST(!checker.all_keys_used());
BOOST_TEST(checker.used_keys().size() == 1);
......@@ -318,19 +317,13 @@ BOOST_AUTO_TEST_CASE(authority_checker)
}
A = authority(3, {key_weight{a, 2}, key_weight{b, 1}}, {permission_level_weight{{"hello", "world"}, 3}});
pv._log = true;
{
pv.permissions.clear();
pv.size_stack.clear();
auto checker = make_auth_checker(GetCAuthority, pv, 2, {a, b});
auto checker = make_auth_checker(GetCAuthority, 2, {a, b});
BOOST_TEST(checker.satisfied(A));
BOOST_TEST(checker.all_keys_used());
BOOST_TEST(pv.permissions.size() == 0);
}
{
pv.permissions.clear();
pv.size_stack.clear();
auto checker = make_auth_checker(GetCAuthority, pv, 2, {a, b, c});
auto checker = make_auth_checker(GetCAuthority, 2, {a, b, c});
BOOST_TEST(checker.satisfied(A));
BOOST_TEST(!checker.all_keys_used());
BOOST_TEST(checker.used_keys().size() == 1);
......@@ -338,21 +331,17 @@ BOOST_AUTO_TEST_CASE(authority_checker)
BOOST_TEST(checker.unused_keys().size() == 2);
BOOST_TEST(checker.unused_keys().count(a) == 1);
BOOST_TEST(checker.unused_keys().count(b) == 1);
BOOST_TEST(pv.permissions.size() == 1);
BOOST_TEST(pv.permissions.back().actor == "hello");
BOOST_TEST(pv.permissions.back().permission == "world");
}
pv._log = false;
A = authority(2, {key_weight{a, 1}, key_weight{b, 1}}, {permission_level_weight{{"hello", "world"}, 1}});
BOOST_TEST(!make_auth_checker(GetCAuthority, pv, 2, {a}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetCAuthority, pv, 2, {b}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetCAuthority, pv, 2, {c}).satisfied(A));
BOOST_TEST(make_auth_checker(GetCAuthority, pv, 2, {a, b}).satisfied(A));
BOOST_TEST(make_auth_checker(GetCAuthority, pv, 2, {b, c}).satisfied(A));
BOOST_TEST(make_auth_checker(GetCAuthority, pv, 2, {a, c}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetCAuthority, 2, {a}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetCAuthority, 2, {b}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetCAuthority, 2, {c}).satisfied(A));
BOOST_TEST(make_auth_checker(GetCAuthority, 2, {a, b}).satisfied(A));
BOOST_TEST(make_auth_checker(GetCAuthority, 2, {b, c}).satisfied(A));
BOOST_TEST(make_auth_checker(GetCAuthority, 2, {a, c}).satisfied(A));
{
auto checker = make_auth_checker(GetCAuthority, pv, 2, {a, b, c});
auto checker = make_auth_checker(GetCAuthority, 2, {a, b, c});
BOOST_TEST(checker.satisfied(A));
BOOST_TEST(!checker.all_keys_used());
BOOST_TEST(checker.used_keys().size() == 2);
......@@ -361,12 +350,12 @@ BOOST_AUTO_TEST_CASE(authority_checker)
}
A = authority(2, {key_weight{a, 1}, key_weight{b, 1}}, {permission_level_weight{{"hello", "world"}, 2}});
BOOST_TEST(make_auth_checker(GetCAuthority, pv, 2, {a, b}).satisfied(A));
BOOST_TEST(make_auth_checker(GetCAuthority, pv, 2, {c}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetCAuthority, pv, 2, {a}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetCAuthority, pv, 2, {b}).satisfied(A));
BOOST_TEST(make_auth_checker(GetCAuthority, 2, {a, b}).satisfied(A));
BOOST_TEST(make_auth_checker(GetCAuthority, 2, {c}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetCAuthority, 2, {a}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetCAuthority, 2, {b}).satisfied(A));
{
auto checker = make_auth_checker(GetCAuthority, pv, 2, {a, b, c});
auto checker = make_auth_checker(GetCAuthority, 2, {a, b, c});
BOOST_TEST(checker.satisfied(A));
BOOST_TEST(!checker.all_keys_used());
BOOST_TEST(checker.used_keys().size() == 1);
......@@ -385,12 +374,12 @@ BOOST_AUTO_TEST_CASE(authority_checker)
A = authority(5, {key_weight{a, 2}, key_weight{b, 2}, key_weight{c, 2}}, {permission_level_weight{{"top", "top"}, 5}});
{
auto checker = make_auth_checker(GetAuthority, pv, 2, {d, e});
auto checker = make_auth_checker(GetAuthority, 2, {d, e});
BOOST_TEST(checker.satisfied(A));
BOOST_TEST(checker.all_keys_used());
}
{
auto checker = make_auth_checker(GetAuthority, pv, 2, {a, b, c, d, e});
auto checker = make_auth_checker(GetAuthority, 2, {a, b, c, d, e});
BOOST_TEST(checker.satisfied(A));
BOOST_TEST(!checker.all_keys_used());
BOOST_TEST(checker.used_keys().size() == 2);
......@@ -399,7 +388,7 @@ BOOST_AUTO_TEST_CASE(authority_checker)
BOOST_TEST(checker.used_keys().count(e) == 1);
}
{
auto checker = make_auth_checker(GetAuthority, pv, 2, {a, b, c, e});
auto checker = make_auth_checker(GetAuthority, 2, {a, b, c, e});
BOOST_TEST(checker.satisfied(A));
BOOST_TEST(!checker.all_keys_used());
BOOST_TEST(checker.used_keys().size() == 3);
......@@ -408,28 +397,28 @@ BOOST_AUTO_TEST_CASE(authority_checker)
BOOST_TEST(checker.used_keys().count(b) == 1);
BOOST_TEST(checker.used_keys().count(c) == 1);
}
BOOST_TEST(make_auth_checker(GetAuthority, pv, 1, {a, b, c}).satisfied(A));
BOOST_TEST(make_auth_checker(GetAuthority, 1, {a, b, c}).satisfied(A));
// Fails due to short recursion depth limit
BOOST_TEST(!make_auth_checker(GetAuthority, pv, 1, {d, e}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetAuthority, 1, {d, e}).satisfied(A));
BOOST_TEST(b < a);
BOOST_TEST(b < c);
BOOST_TEST(a < c);
{
// valid key order: c > a > b
A = authority(2, {key_weight{c, 1}, key_weight{a, 1}, key_weight{b, 1}});
// valid key order: c > b
auto B = authority(1, {key_weight{c, 1}, key_weight{b, 1}});
// invalid key order: b < c
auto C = authority(1, {key_weight{c, 1}, key_weight{b, 1}, key_weight{c, 1}});
// valid key order: b < a < c
A = authority(2, {key_weight{b, 1}, key_weight{a, 1}, key_weight{c, 1}});
// valid key order: b < c
auto B = authority(1, {key_weight{b, 1}, key_weight{c, 1}});
// invalid key order: c > b
auto C = authority(1, {key_weight{b, 1}, key_weight{c, 1}, key_weight{b, 1}});
// invalid key order: duplicate c
auto D = authority(1, {key_weight{c, 1}, key_weight{c, 1}, key_weight{b, 1}});
auto D = authority(1, {key_weight{b, 1}, key_weight{c, 1}, key_weight{c, 1}});
// invalid key order: duplicate b
auto E = authority(1, {key_weight{c, 1}, key_weight{b, 1}, key_weight{b, 1}});
auto E = authority(1, {key_weight{b, 1}, key_weight{b, 1}, key_weight{c, 1}});
// unvalid: insufficient weight
auto F = authority(4, {key_weight{c, 1}, key_weight{a, 1}, key_weight{b, 1}});
auto F = authority(4, {key_weight{b, 1}, key_weight{a, 1}, key_weight{c, 1}});
auto checker = make_auth_checker(GetNullAuthority, pv, 2, {a, b, c});
auto checker = make_auth_checker(GetNullAuthority, 2, {a, b, c});
BOOST_TEST(validate(A));
BOOST_TEST(validate(B));
BOOST_TEST(!validate(C));
......@@ -438,51 +427,51 @@ BOOST_AUTO_TEST_CASE(authority_checker)
BOOST_TEST(!validate(F));
BOOST_TEST(!checker.all_keys_used());
BOOST_TEST(checker.unused_keys().count(c) == 1);
BOOST_TEST(checker.unused_keys().count(a) == 1);
BOOST_TEST(checker.unused_keys().count(b) == 1);
BOOST_TEST(checker.unused_keys().count(a) == 1);
BOOST_TEST(checker.unused_keys().count(c) == 1);
BOOST_TEST(checker.satisfied(A));
BOOST_TEST(checker.satisfied(B));
BOOST_TEST(!checker.all_keys_used());
BOOST_TEST(checker.unused_keys().count(c) == 0);
BOOST_TEST(checker.unused_keys().count(b) == 0);
BOOST_TEST(checker.unused_keys().count(a) == 0);
BOOST_TEST(checker.unused_keys().count(b) == 1);
BOOST_TEST(checker.unused_keys().count(c) == 1);
}
{
auto A2 = authority(4, {key_weight{c, 1}, key_weight{a, 1}, key_weight{b, 1}},
{permission_level_weight{{"hi", "world"}, 1},
permission_level_weight{{"hello", "world"}, 1},
permission_level_weight{{"a", "world"}, 1}
auto A2 = authority(4, {key_weight{b, 1}, key_weight{a, 1}, key_weight{c, 1}},
{ permission_level_weight{{"a", "world"}, 1},
permission_level_weight{{"hello", "world"}, 1},
permission_level_weight{{"hi", "world"}, 1}
});
auto B2 = authority(4, {key_weight{c, 1}, key_weight{a, 1}, key_weight{b, 1}},
auto B2 = authority(4, {key_weight{b, 1}, key_weight{a, 1}, key_weight{c, 1}},
{permission_level_weight{{"hello", "world"}, 1}
});
auto C2 = authority(4, {key_weight{c, 1}, key_weight{a, 1}, key_weight{b, 1}},
{permission_level_weight{{"hello", "world"}, 1},
permission_level_weight{{"hello", "there"}, 1}
auto C2 = authority(4, {key_weight{b, 1}, key_weight{a, 1}, key_weight{c, 1}},
{ permission_level_weight{{"hello", "there"}, 1},
permission_level_weight{{"hello", "world"}, 1}
});
// invalid: duplicate
auto D2 = authority(4, {key_weight{c, 1}, key_weight{a, 1}, key_weight{b, 1}},
{permission_level_weight{{"hello", "world"}, 1},
permission_level_weight{{"hello", "world"}, 2}
auto D2 = authority(4, {key_weight{b, 1}, key_weight{a, 1}, key_weight{c, 1}},
{ permission_level_weight{{"hello", "world"}, 1},
permission_level_weight{{"hello", "world"}, 2}
});
// invalid: wrong order
auto E2 = authority(4, {key_weight{c, 1}, key_weight{a, 1}, key_weight{b, 1}},
{permission_level_weight{{"hello", "there"}, 1},
permission_level_weight{{"hello", "world"}, 2}
auto E2 = authority(4, {key_weight{b, 1}, key_weight{a, 1}, key_weight{c, 1}},
{ permission_level_weight{{"hello", "world"}, 2},
permission_level_weight{{"hello", "there"}, 1}
});
// invalid: wrong order
auto F2 = authority(4, {key_weight{c, 1}, key_weight{a, 1}, key_weight{b, 1}},
{permission_level_weight{{"hello", "world"}, 1},
permission_level_weight{{"hi", "world"}, 2}
auto F2 = authority(4, {key_weight{b, 1}, key_weight{a, 1}, key_weight{c, 1}},
{ permission_level_weight{{"hi", "world"}, 2},
permission_level_weight{{"hello", "world"}, 1}
});
// invalid: insufficient weight
auto G2 = authority(7, {key_weight{c, 1}, key_weight{a, 1}, key_weight{b, 1}},
{permission_level_weight{{"hi", "world"}, 1},
permission_level_weight{{"hello", "world"}, 1},
permission_level_weight{{"a", "world"}, 1}
});
auto G2 = authority(7, {key_weight{b, 1}, key_weight{a, 1}, key_weight{c, 1}},
{ permission_level_weight{{"a", "world"}, 1},
permission_level_weight{{"hello", "world"}, 1},
permission_level_weight{{"hi", "world"}, 1}
});
BOOST_TEST(validate(A2));
BOOST_TEST(validate(B2));
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册