未验证 提交 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() { ...@@ -45,7 +45,7 @@ void multisig::propose() {
(const char*)0, 0, (const char*)0, 0,
packed_requested.data(), packed_requested.size() 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 ) { proptable.emplace( proposer, [&]( auto& prop ) {
prop.proposal_name = proposal_name; prop.proposal_name = proposal_name;
...@@ -123,7 +123,7 @@ void multisig::exec( account_name proposer, name proposal_name, account_name exe ...@@ -123,7 +123,7 @@ void multisig::exec( account_name proposer, name proposal_name, account_name exe
(const char*)0, 0, (const char*)0, 0,
packed_provided_approvals.data(), packed_provided_approvals.size() 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() ); 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" { ...@@ -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_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 * @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, check_transaction_authorization( const char* trx_data, uint32_t trx_size,
const char* pubkeys_data, uint32_t pubkeys_size, const char* pubkeys_data, uint32_t pubkeys_size,
const char* perms_data, uint32_t perms_size const char* perms_data, uint32_t perms_size
...@@ -35,9 +35,9 @@ extern "C" { ...@@ -35,9 +35,9 @@ extern "C" {
* @param perms_size - size (in bytes) of the serialized vector of provided permissions * @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) * @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, check_permission_authorization( account_name account,
permission_name permission, permission_name permission,
const char* pubkeys_data, uint32_t pubkeys_size, const char* pubkeys_data, uint32_t pubkeys_size,
......
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
#include <eosiolib/permission.h> #include <eosiolib/permission.h>
#include <eosiolib/transaction.hpp> #include <eosiolib/transaction.hpp>
#include <eosiolib/optional.hpp>
#include <set> #include <set>
#include <limits> #include <limits>
...@@ -20,9 +19,9 @@ namespace eosio { ...@@ -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_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 * @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, check_transaction_authorization( const transaction& trx,
const std::set<permission_level>& provided_permissions , const std::set<permission_level>& provided_permissions ,
const std::set<public_key>& provided_keys = std::set<public_key>() const std::set<public_key>& provided_keys = std::set<public_key>()
...@@ -50,10 +49,7 @@ namespace eosio { ...@@ -50,10 +49,7 @@ namespace eosio {
(nperms > 0) ? packed_perms.size() : 0 (nperms > 0) ? packed_perms.size() : 0
); );
if( res >= 0 ) return (res > 0);
return static_cast<uint64_t>(res);
else
return optional<uint64_t>();
} }
/** /**
...@@ -65,9 +61,9 @@ namespace eosio { ...@@ -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_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) * @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, check_permission_authorization( account_name account,
permission_name permission, permission_name permission,
const std::set<public_key>& provided_keys, const std::set<public_key>& provided_keys,
...@@ -96,10 +92,7 @@ namespace eosio { ...@@ -96,10 +92,7 @@ namespace eosio {
provided_delay_us provided_delay_us
); );
if( res >= 0 ) return (res > 0);
return static_cast<uint64_t>(res);
else
return optional<uint64_t>();
} }
} }
...@@ -211,7 +211,7 @@ void apply_context::execute_inline( action&& a ) { ...@@ -211,7 +211,7 @@ void apply_context::execute_inline( action&& a ) {
control.get_authorization_manager() control.get_authorization_manager()
.check_authorization( {a}, .check_authorization( {a},
{}, {},
{{receiver, permission_name()}}, {{receiver, config::eosio_code_name}},
control.pending_block_time() - trx_context.published, control.pending_block_time() - trx_context.published,
std::bind(&apply_context::checktime, this, std::placeholders::_1), std::bind(&apply_context::checktime, this, std::placeholders::_1),
false false
...@@ -269,7 +269,7 @@ void apply_context::schedule_deferred_transaction( const uint128_t& sender_id, a ...@@ -269,7 +269,7 @@ void apply_context::schedule_deferred_transaction( const uint128_t& sender_id, a
control.get_authorization_manager() control.get_authorization_manager()
.check_authorization( trx.actions, .check_authorization( trx.actions,
{}, {},
{{receiver, permission_name()}}, {{receiver, config::eosio_code_name}},
delay, delay,
std::bind(&apply_context::checktime, this, std::placeholders::_1), std::bind(&apply_context::checktime, this, std::placeholders::_1),
false false
...@@ -421,17 +421,6 @@ int apply_context::get_context_free_data( uint32_t index, char* buffer, size_t b ...@@ -421,17 +421,6 @@ int apply_context::get_context_free_data( uint32_t index, char* buffer, size_t b
return copy_size; 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 ) { 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); return db_store_i64( receiver, scope, table, payer, id, buffer, buffer_size);
} }
......
...@@ -345,11 +345,6 @@ struct controller_impl { ...@@ -345,11 +345,6 @@ struct controller_impl {
majority_permission.id, majority_permission.id,
active_producers_authority, active_producers_authority,
conf.genesis.initial_timestamp ); 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 { ...@@ -931,9 +926,6 @@ struct controller_impl {
config::minority_producers_permission_name}), config::minority_producers_permission_name}),
calculate_threshold( 1, 3 ) /* more than one-third */ ); 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 //TODO: Add tests
} }
......
...@@ -14,7 +14,6 @@ ...@@ -14,7 +14,6 @@
#include <eosio/chain/account_object.hpp> #include <eosio/chain/account_object.hpp>
#include <eosio/chain/permission_object.hpp> #include <eosio/chain/permission_object.hpp>
#include <eosio/chain/permission_link_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/global_property_object.hpp>
#include <eosio/chain/contract_types.hpp> #include <eosio/chain/contract_types.hpp>
#include <eosio/chain/producer_object.hpp> #include <eosio/chain/producer_object.hpp>
...@@ -36,8 +35,26 @@ uint128_t transaction_id_to_sender_id( const transaction_id_type& tid ) { ...@@ -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 ) { void validate_authority_precondition( const apply_context& context, const authority& auth ) {
for(const auto& a : auth.accounts) { for(const auto& a : auth.accounts) {
context.db.get<account_object, by_name>(a.permission.actor); auto* acct = context.db.find<account_object, by_name>(a.permission.actor);
context.control.get_authorization_manager().get_permission({a.permission.actor, a.permission.permission}); 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) { ...@@ -73,10 +90,6 @@ void apply_eosio_newaccount(apply_context& context) {
"Cannot create account named ${name}, as that name is already taken", "Cannot create account named ${name}, as that name is already taken",
("name", create.name)); ("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) { const auto& new_account = db.create<account_object>([&](auto& a) {
a.name = create.name; a.name = create.name;
a.creation_date = context.control.pending_block_time(); a.creation_date = context.control.pending_block_time();
...@@ -86,6 +99,10 @@ void apply_eosio_newaccount(apply_context& context) { ...@@ -86,6 +99,10 @@ void apply_eosio_newaccount(apply_context& context) {
a.name = create.name; 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, const auto& owner_permission = authorization.create_permission( create.name, config::owner_name, 0,
std::move(create.owner) ); std::move(create.owner) );
const auto& active_permission = authorization.create_permission( create.name, config::active_name, owner_permission.id, 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) { ...@@ -196,10 +213,12 @@ void apply_eosio_updateauth(apply_context& context) {
else else
EOS_ASSERT(!update.parent.empty(), action_validate_exception, "Only owner permission can have empty parent" ); 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; if( update.auth.waits.size() > 0 ) {
EOS_ASSERT( update.auth.delay_sec <= max_delay, action_validate_exception, auto max_delay = context.control.get_global_properties().configuration.max_transaction_delay;
"Cannot set delay longer than max_transacton_delay, which is ${max_delay} seconds", EOS_ASSERT( update.auth.waits.back().wait_sec <= max_delay, action_validate_exception,
("max_delay", max_delay) ); "Cannot set delay longer than max_transacton_delay, which is ${max_delay} seconds",
("max_delay", max_delay) );
}
validate_authority_precondition(context, update.auth); validate_authority_precondition(context, update.auth);
...@@ -227,7 +246,6 @@ void apply_eosio_updateauth(apply_context& context) { ...@@ -227,7 +246,6 @@ void apply_eosio_updateauth(apply_context& context) {
po.auth = update.auth; po.auth = update.auth;
po.parent = parent_id; po.parent = parent_id;
po.last_updated = context.control.pending_block_time(); 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()); 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) { ...@@ -364,28 +382,6 @@ void apply_eosio_canceldelay(apply_context& context) {
const auto& trx_id = cancel.trx_id; 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.cancel_deferred_transaction(transaction_id_to_sender_id(trx_id), account_name());
context.checktime( 1000 ); context.checktime( 1000 );
......
...@@ -173,13 +173,6 @@ abi_def eosio_contract_abi(const abi_def& eosio_system_abi) ...@@ -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 { eos_abi.structs.emplace_back( struct_def {
"transaction_header", "", { "transaction_header", "", {
{"expiration", "time_point_sec"}, {"expiration", "time_point_sec"},
...@@ -212,12 +205,26 @@ abi_def eosio_contract_abi(const abi_def& eosio_system_abi) ...@@ -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 { eos_abi.structs.emplace_back( struct_def {
"authority", "", { "authority", "", {
{"threshold", "uint32"}, {"threshold", "uint32"},
{"delay_sec", "uint32"},
{"keys", "key_weight[]"}, {"keys", "key_weight[]"},
{"accounts", "permission_level_weight[]"} {"accounts", "permission_level_weight[]"},
{"waits", "wait_weight[]"}
} }
}); });
......
...@@ -13,9 +13,28 @@ namespace eosio { namespace chain { ...@@ -13,9 +13,28 @@ namespace eosio { namespace chain {
permission_name permission; permission_name permission;
}; };
inline bool operator== (const permission_level& lhs, const permission_level& rhs) inline bool operator== (const permission_level& lhs, const permission_level& rhs) {
{ return std::tie(lhs.actor, lhs.permission) == std::tie(rhs.actor, rhs.permission);
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);
} }
/** /**
......
...@@ -516,9 +516,6 @@ class apply_context { ...@@ -516,9 +516,6 @@ class apply_context {
bool all_authorizations_used()const; bool all_authorizations_used()const;
vector<permission_level> unused_authorizations()const; vector<permission_level> unused_authorizations()const;
void check_auth( const transaction& trx, const vector<permission_level>& perm );
/// Console methods: /// Console methods:
public: public:
......
...@@ -30,6 +30,15 @@ struct key_weight { ...@@ -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 { namespace config {
template<> template<>
struct billable_size<permission_level_weight> { struct billable_size<permission_level_weight> {
...@@ -40,74 +49,85 @@ namespace config { ...@@ -40,74 +49,85 @@ namespace config {
struct billable_size<key_weight> { struct billable_size<key_weight> {
static const uint64_t value = 8; ///< over value of weight for safety, dynamically sizing key 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 { struct authority {
authority( public_key_type k, uint32_t delay = 0 ):threshold(1),delay_sec(delay),keys({{k,1}}){} authority( public_key_type k, uint32_t delay_sec = 0 )
authority( uint32_t t, vector<key_weight> k, vector<permission_level_weight> p = {}, uint32_t delay = 0 ) :threshold(1),keys({{k,1}})
:threshold(t),delay_sec(delay),keys(move(k)),accounts(move(p)){} {
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(){} authority(){}
uint32_t threshold = 0; uint32_t threshold = 0;
uint32_t delay_sec = 0;
vector<key_weight> keys; vector<key_weight> keys;
vector<permission_level_weight> accounts; vector<permission_level_weight> accounts;
vector<wait_weight> waits;
friend bool operator == ( const authority& lhs, const authority& rhs ) { 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 ) { 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 { struct shared_authority {
shared_authority( chainbase::allocator<char> alloc ) shared_authority( chainbase::allocator<char> alloc )
:keys(alloc),accounts(alloc){} :keys(alloc),accounts(alloc),waits(alloc){}
shared_authority& operator=(const authority& a) { shared_authority& operator=(const authority& a) {
threshold = a.threshold; threshold = a.threshold;
delay_sec = a.delay_sec;
keys = decltype(keys)(a.keys.begin(), a.keys.end(), keys.get_allocator()); keys = decltype(keys)(a.keys.begin(), a.keys.end(), keys.get_allocator());
accounts = decltype(accounts)(a.accounts.begin(), a.accounts.end(), accounts.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; return *this;
} }
uint32_t threshold = 0; uint32_t threshold = 0;
uint32_t delay_sec = 0;
shared_vector<key_weight> keys; shared_vector<key_weight> keys;
shared_vector<permission_level_weight> accounts; shared_vector<permission_level_weight> accounts;
shared_vector<wait_weight> waits;
operator authority()const { return to_authority(); } operator authority()const { return to_authority(); }
authority to_authority()const { authority to_authority()const {
authority auth; authority auth;
auth.threshold = threshold; auth.threshold = threshold;
auth.delay_sec = delay_sec;
auth.keys.reserve(keys.size()); auth.keys.reserve(keys.size());
auth.accounts.reserve(accounts.size()); auth.accounts.reserve(accounts.size());
auth.waits.reserve(waits.size());
for( const auto& k : keys ) { auth.keys.emplace_back( k ); } for( const auto& k : keys ) { auth.keys.emplace_back( k ); }
for( const auto& a : accounts ) { auth.accounts.emplace_back( a ); } for( const auto& a : accounts ) { auth.accounts.emplace_back( a ); }
for( const auto& w : waits ) { auth.waits.emplace_back( w ); }
return auth; return auth;
} }
size_t get_billable_size() const { size_t get_billable_size() const {
size_t accounts_size = accounts.size() * config::billable_size_v<permission_level_weight>; 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; size_t keys_size = 0;
for (const auto& k: keys) { for (const auto& k: keys) {
keys_size += config::billable_size_v<key_weight>; keys_size += config::billable_size_v<key_weight>;
keys_size += fc::raw::pack_size(k.key); ///< serialized size of the key 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 * Makes sure all keys are unique and sorted and all account permissions are unique and sorted and that authority can
* be satisfied * be satisfied
...@@ -119,25 +139,44 @@ inline bool validate( const Authority& auth ) { ...@@ -119,25 +139,44 @@ inline bool validate( const Authority& auth ) {
static_assert( std::is_same<decltype(auth.threshold), uint32_t>::value && static_assert( std::is_same<decltype(auth.threshold), uint32_t>::value &&
std::is_same<weight_type, uint16_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.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" ); "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) return false; // overflow protection (assumes weight_type is uint16_t and threshold is of type uint32_t)
const key_weight* prev = nullptr; if( auth.threshold == 0 )
for( const auto& k : auth.keys ) { return false;
if( prev && ( prev->key < k.key || prev->key == k.key ) ) return false;
total_weight += k.weight; {
prev = &k; 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 ) { const permission_level_weight* prev = nullptr;
if(pa && ( pa->permission < a.permission || pa->permission == a.permission ) ) return false; for( const auto& a : auth.accounts ) {
total_weight += a.weight; 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)
pa = &a; 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 } } // namespace eosio::chain
...@@ -145,5 +184,6 @@ inline bool validate( const Authority& auth ) { ...@@ -145,5 +184,6 @@ inline bool validate( const Authority& auth ) {
FC_REFLECT(eosio::chain::permission_level_weight, (permission)(weight) ) FC_REFLECT(eosio::chain::permission_level_weight, (permission)(weight) )
FC_REFLECT(eosio::chain::key_weight, (key)(weight) ) FC_REFLECT(eosio::chain::key_weight, (key)(weight) )
FC_REFLECT(eosio::chain::authority, (threshold)(delay_sec)(keys)(accounts)) FC_REFLECT(eosio::chain::wait_weight, (wait_sec)(weight) )
FC_REFLECT(eosio::chain::shared_authority, (threshold)(delay_sec)(keys)(accounts)) 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 { ...@@ -21,21 +21,26 @@ namespace eosio { namespace chain {
namespace detail { 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 { struct get_weight_visitor {
using result_type = uint32_t; using result_type = uint32_t;
template<typename Permission> 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 { 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; get_weight_visitor scale;
if (a.visit(scale) > b.visit(scale)) return true; auto lhs_weight = lhs.visit(scale);
return a.contains<key_weight>() && b.contains<permission_level_weight>(); 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 { ...@@ -53,34 +58,31 @@ namespace detail {
* @tparam F A callable which takes a single argument of type @ref AccountPermission and returns the corresponding * @tparam F A callable which takes a single argument of type @ref AccountPermission and returns the corresponding
* authority * authority
*/ */
template<typename PermissionToAuthorityFunc, typename PermissionVisitor> template<typename PermissionToAuthorityFunc>
class authority_checker { class authority_checker {
private: private:
PermissionToAuthorityFunc permission_to_authority; PermissionToAuthorityFunc permission_to_authority;
PermissionVisitor permission_visitor;
const std::function<void(uint32_t)>& checktime; 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; flat_set<permission_level> provided_permissions;
vector<bool> _used_keys; vector<bool> _used_keys;
fc::microseconds delay_threshold; fc::microseconds provided_delay;
uint16_t recursion_depth_limit; uint16_t recursion_depth_limit;
public: public:
authority_checker( PermissionToAuthorityFunc permission_to_authority, authority_checker( PermissionToAuthorityFunc permission_to_authority,
PermissionVisitor permission_visitor,
uint16_t recursion_depth_limit, 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, const flat_set<permission_level>& provided_permissions,
fc::microseconds delay_threshold, fc::microseconds provided_delay,
const std::function<void(uint32_t)>& checktime const std::function<void(uint32_t)>& checktime
) )
:permission_to_authority(permission_to_authority) :permission_to_authority(permission_to_authority)
,permission_visitor(permission_visitor)
,checktime( checktime ) ,checktime( checktime )
,signing_keys(signing_keys.begin(), signing_keys.end()) ,provided_keys(provided_keys.begin(), provided_keys.end())
,provided_permissions(provided_permissions) ,provided_permissions(provided_permissions)
,_used_keys(signing_keys.size(), false) ,_used_keys(provided_keys.size(), false)
,delay_threshold(delay_threshold) ,provided_delay(provided_delay)
,recursion_depth_limit(recursion_depth_limit) ,recursion_depth_limit(recursion_depth_limit)
{ {
FC_ASSERT( static_cast<bool>(checktime), "checktime cannot be empty" ); FC_ASSERT( static_cast<bool>(checktime), "checktime cannot be empty" );
...@@ -88,7 +90,6 @@ namespace detail { ...@@ -88,7 +90,6 @@ namespace detail {
enum permission_cache_status { enum permission_cache_status {
being_evaluated, being_evaluated,
permission_provided,
permission_unsatisfied, permission_unsatisfied,
permission_satisfied permission_satisfied
}; };
...@@ -96,15 +97,15 @@ namespace detail { ...@@ -96,15 +97,15 @@ namespace detail {
typedef map<permission_level, permission_cache_status> permission_cache_type; typedef map<permission_level, permission_cache_status> permission_cache_type;
bool satisfied( const permission_level& permission, bool satisfied( const permission_level& permission,
fc::microseconds override_delay_threshold, fc::microseconds override_provided_delay,
permission_cache_type* cached_perms = nullptr permission_cache_type* cached_perms = nullptr
) )
{ {
auto delay_threshold_reverter = fc::make_scoped_exit([this, delay = delay_threshold] () mutable { auto delay_reverter = fc::make_scoped_exit( [this, delay = provided_delay] () mutable {
delay_threshold = delay; provided_delay = delay;
}); });
delay_threshold = override_delay_threshold; provided_delay = override_provided_delay;
return satisfied( permission, cached_perms ); return satisfied( permission, cached_perms );
} }
...@@ -115,23 +116,21 @@ namespace detail { ...@@ -115,23 +116,21 @@ namespace detail {
if( cached_perms == nullptr ) if( cached_perms == nullptr )
cached_perms = initialize_permission_cache( cached_permissions ); cached_perms = initialize_permission_cache( cached_permissions );
weight_tally_visitor visitor(*this, *cached_perms, 0); weight_tally_visitor visitor(*this, *cached_perms, 0);
return ( visitor(permission_level_weight{permission, 1}) > 0 ); return ( visitor(permission_level_weight{permission, 1}) > 0 );
} }
template<typename AuthorityType> template<typename AuthorityType>
bool satisfied( const AuthorityType& authority, bool satisfied( const AuthorityType& authority,
fc::microseconds override_delay_threshold, fc::microseconds override_provided_delay,
permission_cache_type* cached_perms = nullptr permission_cache_type* cached_perms = nullptr
) )
{ {
auto delay_threshold_reverter = fc::make_scoped_exit([this, delay = delay_threshold] () mutable { auto delay_reverter = fc::make_scoped_exit( [this, delay = provided_delay] () mutable {
delay_threshold = delay; provided_delay = delay;
}); });
delay_threshold = override_delay_threshold; provided_delay = override_provided_delay;
return satisfied( authority, cached_perms ); return satisfied( authority, cached_perms );
} }
...@@ -149,18 +148,14 @@ namespace detail { ...@@ -149,18 +148,14 @@ namespace detail {
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 { 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()}; return {range.begin(), range.end()};
} }
flat_set<public_key_type> unused_keys() const { 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()}; return {range.begin(), range.end()};
} }
PermissionVisitor& get_permission_visitor() {
return permission_visitor;
}
static optional<permission_cache_status> static optional<permission_cache_status>
permission_status_in_cache( const permission_cache_type& permissions, permission_status_in_cache( const permission_cache_type& permissions,
const permission_level& level ) const permission_level& level )
...@@ -179,7 +174,7 @@ namespace detail { ...@@ -179,7 +174,7 @@ namespace detail {
private: private:
permission_cache_type* initialize_permission_cache( permission_cache_type& cached_permissions ) { permission_cache_type* initialize_permission_cache( permission_cache_type& cached_permissions ) {
for( const auto& p : provided_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; return &cached_permissions;
} }
...@@ -194,10 +189,11 @@ namespace detail { ...@@ -194,10 +189,11 @@ namespace detail {
// Sort key permissions and account permissions together into a single set of meta_permissions // Sort key permissions and account permissions together into a single set of meta_permissions
detail::meta_permission_set 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.keys.begin(), authority.keys.end());
permissions.insert(authority.accounts.begin(), authority.accounts.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); weight_tally_visitor visitor(*this, cached_permissions, depth);
for( const auto& permission : permissions ) for( const auto& permission : permissions )
// If we've got enough weight, to satisfy the authority, return! // If we've got enough weight, to satisfy the authority, return!
...@@ -222,18 +218,23 @@ namespace detail { ...@@ -222,18 +218,23 @@ namespace detail {
,recursion_depth(recursion_depth) ,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) { uint32_t operator()(const key_weight& permission) {
auto itr = boost::find( checker.signing_keys, permission.key ); auto itr = boost::find( checker.provided_keys, permission.key );
if (itr != checker.signing_keys.end()) { if( itr != checker.provided_keys.end() ) {
checker._used_keys[itr - checker.signing_keys.begin()] = true; checker._used_keys[itr - checker.provided_keys.begin()] = true;
total_weight += permission.weight; total_weight += permission.weight;
} }
return total_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 ); auto status = authority_checker::permission_status_in_cache( cached_permissions, permission.permission );
if( !status ) { if( !status ) {
if( recursion_depth < checker.recursion_depth_limit ) { if( recursion_depth < checker.recursion_depth_limit ) {
...@@ -244,53 +245,25 @@ namespace detail { ...@@ -244,53 +245,25 @@ namespace detail {
try { try {
auto&& auth = checker.permission_to_authority( permission.permission ); auto&& auth = checker.permission_to_authority( permission.permission );
propagate_error = true; 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 ); auto res = cached_permissions.emplace( permission.permission, being_evaluated );
itr = res.first; itr = res.first;
r = checker.satisfied( std::forward<decltype(auth)>(auth), cached_permissions, recursion_depth + 1 ); r = checker.satisfied( std::forward<decltype(auth)>(auth), cached_permissions, recursion_depth + 1 );
} catch( const permission_query_exception& ) { } catch( const permission_query_exception& ) {
checker.permission_visitor.pop_undo();
if( propagate_error ) if( propagate_error )
throw; throw;
else else
return total_weight; // if the permission doesn't exist, continue without it return total_weight; // if the permission doesn't exist, continue without it
} catch( ... ) {
checker.permission_visitor.pop_undo();
throw;
} }
if( r ) { if( r ) {
total_weight += permission.weight; total_weight += permission.weight;
itr->second = permission_satisfied; itr->second = permission_satisfied;
checker.permission_visitor( permission.permission, false );
checker.permission_visitor.squash_undo();
} else { } else {
itr->second = permission_unsatisfied; itr->second = permission_unsatisfied;
checker.permission_visitor.pop_undo();
} }
} }
} else if( *status == permission_satisfied ) { } else if( *status == permission_satisfied ) {
total_weight += permission.weight; 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; return total_weight;
} }
...@@ -298,39 +271,23 @@ namespace detail { ...@@ -298,39 +271,23 @@ namespace detail {
}; /// authority_checker }; /// authority_checker
template<typename PermissionToAuthorityFunc, typename PermissionVisitor> template<typename PermissionToAuthorityFunc>
auto make_auth_checker( PermissionToAuthorityFunc&& pta, auto make_auth_checker( PermissionToAuthorityFunc&& pta,
PermissionVisitor&& permission_visitor,
uint16_t recursion_depth_limit, 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>(), 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)>() const std::function<void(uint32_t)>& _checktime = std::function<void(uint32_t)>()
) )
{ {
auto noop_checktime = []( uint32_t ) {}; auto noop_checktime = []( uint32_t ) {};
const auto& checktime = ( static_cast<bool>(_checktime) ? _checktime : noop_checktime ); const auto& checktime = ( static_cast<bool>(_checktime) ? _checktime : noop_checktime );
return authority_checker< PermissionToAuthorityFunc, return authority_checker< PermissionToAuthorityFunc>( std::forward<PermissionToAuthorityFunc>(pta),
PermissionVisitor >( std::forward<PermissionToAuthorityFunc>(pta), recursion_depth_limit,
std::forward<PermissionVisitor>(permission_visitor), provided_keys,
recursion_depth_limit, provided_permissions,
signing_keys, provided_delay,
provided_permissions, checktime );
delay_threshold,
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 } } // namespace eosio::chain
...@@ -66,10 +66,8 @@ namespace eosio { namespace chain { ...@@ -66,10 +66,8 @@ namespace eosio { namespace chain {
* @param provided_delay - the delay satisfied by the transaction * @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 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 * @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, check_authorization( const vector<action>& actions,
const flat_set<public_key_type>& provided_keys, const flat_set<public_key_type>& provided_keys,
const flat_set<permission_level>& provided_permissions = flat_set<permission_level>(), const flat_set<permission_level>& provided_permissions = flat_set<permission_level>(),
...@@ -89,10 +87,8 @@ namespace eosio { namespace chain { ...@@ -89,10 +87,8 @@ namespace eosio { namespace chain {
* @param provided_delay - the delay considered to be satisfied for the authorization check * @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 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 * @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, check_authorization( account_name account,
permission_name permission, permission_name permission,
const flat_set<public_key_type>& provided_keys, const flat_set<public_key_type>& provided_keys,
...@@ -104,7 +100,7 @@ namespace eosio { namespace chain { ...@@ -104,7 +100,7 @@ namespace eosio { namespace chain {
flat_set<public_key_type> get_required_keys( const transaction& trx, flat_set<public_key_type> get_required_keys( const transaction& trx,
const flat_set<public_key_type>& candidate_keys, const flat_set<public_key_type>& candidate_keys,
fc::microseconds delay_threshold = fc::microseconds(0) fc::microseconds provided_delay = fc::microseconds(0)
)const; )const;
...@@ -114,11 +110,11 @@ namespace eosio { namespace chain { ...@@ -114,11 +110,11 @@ namespace eosio { namespace chain {
const controller& _control; const controller& _control;
chainbase::database& _db; chainbase::database& _db;
optional<fc::microseconds> check_updateauth_authorization( const updateauth& update, const vector<permission_level>& auths )const; void 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; void 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; void 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_unlinkauth_authorization( const unlinkauth& unlink, const vector<permission_level>& auths )const;
void check_canceldelay_authorization( const canceldelay& cancel, 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, optional<permission_name> lookup_linked_permission( account_name authorizer_account,
scope_name code_account, scope_name code_account,
......
...@@ -23,7 +23,6 @@ const static uint64_t producers_account_name = N(producers); ...@@ -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 // 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 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 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_auth_scope = N(eosio.auth);
const static uint64_t eosio_all_scope = N(eosio.all); const static uint64_t eosio_all_scope = N(eosio.all);
...@@ -31,6 +30,7 @@ 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 active_name = N(active);
const static uint64_t owner_name = N(owner); const static uint64_t owner_name = N(owner);
const static uint64_t eosio_any_name = N(eosio.any); 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_ms = 500;
const static int block_interval_us = block_interval_ms*1000; const static int block_interval_us = block_interval_ms*1000;
......
...@@ -212,12 +212,12 @@ namespace eosio { namespace chain { ...@@ -212,12 +212,12 @@ namespace eosio { namespace chain {
FC_DECLARE_DERIVED_EXCEPTION( authorization_exception, chain_exception, FC_DECLARE_DERIVED_EXCEPTION( authorization_exception, chain_exception,
3090000, "Authorization 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, 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, 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, FC_DECLARE_DERIVED_EXCEPTION( missing_auth_exception, authorization_exception,
3090004, "missing required authority" ) 3090004, "missing required authority" )
FC_DECLARE_DERIVED_EXCEPTION( irrelevant_auth_exception, authorization_exception, FC_DECLARE_DERIVED_EXCEPTION( irrelevant_auth_exception, authorization_exception,
......
...@@ -17,45 +17,38 @@ namespace eosio { namespace chain { ...@@ -17,45 +17,38 @@ namespace eosio { namespace chain {
permission_name name; ///< human-readable name for the permission permission_name name; ///< human-readable name for the permission
shared_authority auth; ///< authority required to execute this permission shared_authority auth; ///< authority required to execute this permission
time_point last_updated; ///< the last time this authority was updated 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 * @brief Checks if this permission is equivalent or greater than other
* @tparam Index The permission_index * @tparam Index The permission_index
* @return a fc::microseconds set to the maximum delay encountered between this and the permission that is other; * @return true if this permission is equivalent or greater than other, false otherwise
* empty optional otherwise
* *
* Permissions are organized hierarchically such that a parent permission is strictly more powerful than its * 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 * 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 * satisfying) permission @ref other.
* maximum delay encountered walking the hierarchy between this permission and other, if this satisfies other,
* otherwise an empty optional is returned.
*/ */
template <typename Index> 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 the owners are not the same, this permission cannot satisfy other
if( owner != other.owner ) 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 this permission matches other, or is the immediate parent of other, then this permission satisfies other
if( id == other.id || id == other.parent ) 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 // 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); const permission_object* parent = &*permission_index.template get<by_id>().find(other.parent);
while( parent ) { while( parent ) {
if( max_delay < parent->delay )
max_delay = parent->delay;
if( id == parent->parent ) if( id == parent->parent )
return optional<fc::microseconds>(max_delay); return true;
if( parent->parent._id == 0 ) if( parent->parent._id == 0 )
return optional<fc::microseconds>(); return false;
parent = &*permission_index.template get<by_id>().find(parent->parent); parent = &*permission_index.template get<by_id>().find(parent->parent);
} }
// This permission is not a parent of other, and so does not satisfy other // 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 ...@@ -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) 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(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(chainbase::oid<eosio::chain::permission_usage_object>, (_id))
FC_REFLECT(eosio::chain::permission_usage_object, (id)(account)(permission)(last_used)) 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 ...@@ -103,7 +103,7 @@ flat_set<public_key_type> transaction::get_signature_keys( const vector<signatur
} }
bool successful_insertion = false; bool successful_insertion = false;
std::tie(std::ignore, successful_insertion) = recovered_pub_keys.insert(recov); 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}", "transaction includes more than one signature signed using the same key associated with public key: ${key}",
("key", recov) ("key", recov)
); );
......
...@@ -762,10 +762,10 @@ class permission_api : public context_aware_api { ...@@ -762,10 +762,10 @@ class permission_api : public context_aware_api {
public: public:
using context_aware_api::context_aware_api; using context_aware_api::context_aware_api;
int64_t check_transaction_authorization( array_ptr<char> trx_data, size_t trx_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> pubkeys_data, size_t pubkeys_size,
array_ptr<char> perms_data, size_t perms_size array_ptr<char> perms_data, size_t perms_size
) )
{ {
transaction trx = fc::raw::unpack<transaction>( trx_data, trx_size ); transaction trx = fc::raw::unpack<transaction>( trx_data, trx_size );
...@@ -776,26 +776,26 @@ class permission_api : public context_aware_api { ...@@ -776,26 +776,26 @@ class permission_api : public context_aware_api {
unpack_provided_permissions( provided_permissions, perms_data, perms_size ); unpack_provided_permissions( provided_permissions, perms_data, perms_size );
try { try {
auto delay = context.control context.control
.get_authorization_manager() .get_authorization_manager()
.check_authorization( trx.actions, .check_authorization( trx.actions,
provided_keys, provided_keys,
provided_permissions, provided_permissions,
fc::seconds(trx.delay_sec), fc::seconds(trx.delay_sec),
std::bind(&apply_context::checktime, &context, std::placeholders::_1), std::bind(&apply_context::checktime, &context, std::placeholders::_1),
false false
); );
return delay.count(); return true;
} catch( const authorization_exception& e ) {} } catch( const authorization_exception& e ) {}
return -1; return false;
} }
int64_t check_permission_authorization( account_name account, permission_name permission, bool check_permission_authorization( account_name account, permission_name permission,
array_ptr<char> pubkeys_data, size_t pubkeys_size, array_ptr<char> pubkeys_data, size_t pubkeys_size,
array_ptr<char> perms_data, size_t perms_size, array_ptr<char> perms_data, size_t perms_size,
uint64_t delay_us uint64_t delay_us
) )
{ {
EOS_ASSERT( delay_us <= static_cast<uint64_t>(std::numeric_limits<int64_t>::max()), EOS_ASSERT( delay_us <= static_cast<uint64_t>(std::numeric_limits<int64_t>::max()),
action_validate_exception, "provided delay is too large" ); action_validate_exception, "provided delay is too large" );
...@@ -807,20 +807,20 @@ class permission_api : public context_aware_api { ...@@ -807,20 +807,20 @@ class permission_api : public context_aware_api {
unpack_provided_permissions( provided_permissions, perms_data, perms_size ); unpack_provided_permissions( provided_permissions, perms_data, perms_size );
try { try {
auto delay = context.control context.control
.get_authorization_manager() .get_authorization_manager()
.check_authorization( account, .check_authorization( account,
permission, permission,
provided_keys, provided_keys,
provided_permissions, provided_permissions,
fc::microseconds(delay_us), fc::microseconds(delay_us),
std::bind(&apply_context::checktime, &context, std::placeholders::_1), std::bind(&apply_context::checktime, &context, std::placeholders::_1),
false false
); );
return delay.count(); return true;
} catch( const authorization_exception& e ) {} } catch( const authorization_exception& e ) {}
return -1; return false;
} }
int64_t get_permission_last_used( account_name account, permission_name permission) { int64_t get_permission_last_used( account_name account, permission_name permission) {
...@@ -830,7 +830,7 @@ class permission_api : public context_aware_api { ...@@ -830,7 +830,7 @@ class permission_api : public context_aware_api {
int64_t get_account_creation_date( account_name account ) { 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(); return time_point(context.db.get<account_object, by_name>(account).creation_date).time_since_epoch().count();
} }
private: private:
void unpack_provided_keys( flat_set<public_key_type>& keys, const char* pubkeys_data, size_t pubkeys_size ) { 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, ...@@ -1691,8 +1691,8 @@ REGISTER_INTRINSICS(crypto_api,
REGISTER_INTRINSICS(permission_api, REGISTER_INTRINSICS(permission_api,
(check_transaction_authorization, int64_t(int, int, int, int, int, int) ) (check_transaction_authorization, int(int, int, int, int, int, int) )
(check_permission_authorization, int64_t(int64_t, int64_t, int, int, int, int, int64_t) ) (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_permission_last_used, int64_t(int64_t, int64_t) )
(get_account_creation_date, int64_t(int64_t) ) (get_account_creation_date, int64_t(int64_t) )
); );
......
...@@ -116,10 +116,14 @@ namespace eosio { namespace testing { ...@@ -116,10 +116,14 @@ namespace eosio { namespace testing {
uint32_t expiration = DEFAULT_EXPIRATION_DELTA, uint32_t expiration = DEFAULT_EXPIRATION_DELTA,
uint32_t delay_sec = 0)const; 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; vector<transaction_trace_ptr> traces;
traces.reserve(names.size()); 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; return traces;
} }
...@@ -136,7 +140,11 @@ namespace eosio { namespace testing { ...@@ -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, const vector<permission_level>& auths, const vector<private_key_type>& keys );
void delete_authority( account_name account, permission_name perm ); 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, 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); transaction_trace_ptr push_reqauth(account_name from, string role, bool multi_sig = false);
...@@ -173,7 +181,7 @@ namespace eosio { namespace testing { ...@@ -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 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_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; bool chain_has_transaction( const transaction_id_type& txid ) const;
const transaction_receipt& get_transaction_receipt( 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 { ...@@ -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; signed_transaction trx;
set_transaction_headers(trx); set_transaction_headers(trx);
authority owner_auth; authority owner_auth;
if (multisig) { if( multisig ) {
// multisig between account's owner key and creators active permission // 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}}); owner_auth = authority(2, {key_weight{get_public_key( a, "owner" ), 1}}, {permission_level_weight{{creator, config::active_name}, 1}});
} else { } else {
owner_auth = authority( get_public_key( a, "owner" ) ); 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}}, trx.actions.emplace_back( vector<permission_level>{{creator,config::active_name}},
newaccount{ newaccount{
.creator = creator, .creator = creator,
.name = a, .name = a,
.owner = owner_auth, .owner = owner_auth,
.active = authority( get_public_key( a, "active" ) ) .active = active_auth,
}); });
set_transaction_headers(trx); set_transaction_headers(trx);
......
...@@ -35,7 +35,7 @@ void chain_api_plugin::plugin_initialize(const variables_map&) {} ...@@ -35,7 +35,7 @@ void chain_api_plugin::plugin_initialize(const variables_map&) {}
if (body.empty()) body = "{}"; \ if (body.empty()) body = "{}"; \
auto result = api_handle.call_name(fc::json::from_string(body).as<api_namespace::call_name ## _params>()); \ 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)); \ 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}; \ error_results results{401, "UnAuthorized", e}; \
cb(401, fc::json::to_string(results)); \ cb(401, fc::json::to_string(results)); \
} catch (chain::tx_duplicate& e) { \ } catch (chain::tx_duplicate& e) { \
......
...@@ -972,7 +972,7 @@ BOOST_AUTO_TEST_CASE(irrelevant_sig_soft_check) { ...@@ -972,7 +972,7 @@ BOOST_AUTO_TEST_CASE(irrelevant_sig_soft_check) {
trx.signatures.clear(); 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() );
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 // Sign the transaction properly and push to the block
trx.signatures.clear(); trx.signatures.clear();
...@@ -1128,7 +1128,7 @@ BOOST_AUTO_TEST_CASE(get_required_keys) ...@@ -1128,7 +1128,7 @@ BOOST_AUTO_TEST_CASE(get_required_keys)
}); });
chain.set_transaction_headers(trx); 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_1 = chain.get_private_key("alice", "blah");
const auto priv_key_not_needed_2 = chain.get_private_key("alice", "owner"); const auto priv_key_not_needed_2 = chain.get_private_key("alice", "owner");
......
...@@ -1871,20 +1871,20 @@ BOOST_AUTO_TEST_CASE(general) ...@@ -1871,20 +1871,20 @@ BOOST_AUTO_TEST_CASE(general)
"keyweight_arr": [{"key":"EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", "weight":"100"},{"key":"EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", "weight":"200"}], "keyweight_arr": [{"key":"EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", "weight":"100"},{"key":"EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", "weight":"200"}],
"authority": { "authority": {
"threshold":"10", "threshold":"10",
"delay_sec":"0",
"keys":[{"key":"EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", "weight":100},{"key":"EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", "weight":200}], "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": [{ "authority_arr": [{
"threshold":"10", "threshold":"10",
"delay_sec":"0",
"keys":[{"key":"EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", "weight":"100"},{"key":"EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", "weight":"200"}], "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", "threshold":"10",
"delay_sec":"0",
"keys":[{"key":"EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", "weight":"100"},{"key":"EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", "weight":"200"}], "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" : {"new_type_name":"new", "type":"old"},
"typedef_arr": [{"new_type_name":"new", "type":"old"},{"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) ...@@ -2056,11 +2056,11 @@ BOOST_AUTO_TEST_CASE(updateauth_test)
"parent" : "updauth.prnt", "parent" : "updauth.prnt",
"auth" : { "auth" : {
"threshold" : "2147483145", "threshold" : "2147483145",
"delay_sec" : "0",
"keys" : [ {"key" : "EOS65rXebLhtk2aTTzP4e9x1AQZs7c5NNXJp89W8R3HyaA6Zyd4im", "weight" : 57005}, "keys" : [ {"key" : "EOS65rXebLhtk2aTTzP4e9x1AQZs7c5NNXJp89W8R3HyaA6Zyd4im", "weight" : 57005},
{"key" : "EOS5eVr9TVnqwnUBNwf9kwMTbrHvX5aPyyEG97dz2b2TNeqWRzbJf", "weight" : 57605} ], {"key" : "EOS5eVr9TVnqwnUBNwf9kwMTbrHvX5aPyyEG97dz2b2TNeqWRzbJf", "weight" : 57605} ],
"accounts" : [ {"permission" : {"actor" : "prm.acct1", "permission" : "prm.prm1"}, "weight" : 53005 }, "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) ...@@ -2153,7 +2153,6 @@ BOOST_AUTO_TEST_CASE(newaccount_test)
"name" : "newacct.name", "name" : "newacct.name",
"owner" : { "owner" : {
"threshold" : 2147483145, "threshold" : 2147483145,
"delay_sec" : 0,
"keys" : [ {"key" : "EOS65rXebLhtk2aTTzP4e9x1AQZs7c5NNXJp89W8R3HyaA6Zyd4im", "weight" : 57005}, "keys" : [ {"key" : "EOS65rXebLhtk2aTTzP4e9x1AQZs7c5NNXJp89W8R3HyaA6Zyd4im", "weight" : 57005},
{"key" : "EOS5eVr9TVnqwnUBNwf9kwMTbrHvX5aPyyEG97dz2b2TNeqWRzbJf", "weight" : 57605} ], {"key" : "EOS5eVr9TVnqwnUBNwf9kwMTbrHvX5aPyyEG97dz2b2TNeqWRzbJf", "weight" : 57605} ],
"accounts" : [ {"permission" : {"actor" : "prm.acct1", "permission" : "prm.prm1"}, "weight" : 53005 }, "accounts" : [ {"permission" : {"actor" : "prm.acct1", "permission" : "prm.prm1"}, "weight" : 53005 },
...@@ -2161,7 +2160,6 @@ BOOST_AUTO_TEST_CASE(newaccount_test) ...@@ -2161,7 +2160,6 @@ BOOST_AUTO_TEST_CASE(newaccount_test)
}, },
"active" : { "active" : {
"threshold" : 2146483145, "threshold" : 2146483145,
"delay_sec" : 0,
"keys" : [ {"key" : "EOS65rXebLhtk2aTTzP4e9x1AQZs7c5NNXJp89W8R3HyaA6Zyd4im", "weight" : 57005}, "keys" : [ {"key" : "EOS65rXebLhtk2aTTzP4e9x1AQZs7c5NNXJp89W8R3HyaA6Zyd4im", "weight" : 57005},
{"key" : "EOS5eVr9TVnqwnUBNwf9kwMTbrHvX5aPyyEG97dz2b2TNeqWRzbJf", "weight" : 57605} ], {"key" : "EOS5eVr9TVnqwnUBNwf9kwMTbrHvX5aPyyEG97dz2b2TNeqWRzbJf", "weight" : 57605} ],
"accounts" : [ {"permission" : {"actor" : "prm.acct1", "permission" : "prm.prm1"}, "weight" : 53005 }, "accounts" : [ {"permission" : {"actor" : "prm.acct1", "permission" : "prm.prm1"}, "weight" : 53005 },
......
...@@ -196,7 +196,7 @@ bool is_access_violation(const Runtime::Exception& e) { return true; } ...@@ -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_assert_exception(fc::assert_exception const & e) { return true; }
bool is_page_memory_error(page_memory_error 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_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_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; } 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 { ...@@ -294,8 +294,8 @@ BOOST_FIXTURE_TEST_CASE(action_tests, TESTER) { try {
auto res = test.push_transaction(trx); auto res = test.push_transaction(trx);
BOOST_CHECK_EQUAL(res->receipt->status, transaction_receipt::executed); BOOST_CHECK_EQUAL(res->receipt->status, transaction_receipt::executed);
}; };
BOOST_CHECK_EXCEPTION(test_require_notice(*this, raw_bytes, scope), tx_missing_sigs, BOOST_CHECK_EXCEPTION(test_require_notice(*this, raw_bytes, scope), unsatisfied_authorization,
[](const tx_missing_sigs& e) { [](const unsatisfied_authorization& e) {
return expect_assert_message(e, "transaction declares authority"); return expect_assert_message(e, "transaction declares authority");
} }
); );
...@@ -901,7 +901,7 @@ BOOST_FIXTURE_TEST_CASE(deferred_transaction_tests, TESTER) { try { ...@@ -901,7 +901,7 @@ BOOST_FIXTURE_TEST_CASE(deferred_transaction_tests, TESTER) { try {
dtt_action dtt_act1; dtt_action dtt_act1;
dtt_act1.payer = N(alice); 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); 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 // 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 // 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 // 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 { ...@@ -909,18 +909,27 @@ BOOST_FIXTURE_TEST_CASE(deferred_transaction_tests, TESTER) { try {
dtt_act2.deferred_account = N(testapi2); dtt_act2.deferred_account = N(testapi2);
dtt_act2.permission_name = N(additional); dtt_act2.permission_name = N(additional);
dtt_act2.delay_sec = 5; 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() push_action(config::system_account_name, updateauth::get_name(), "testapi", fc::mutable_variant_object()
("account", "testapi") ("account", "testapi")
("permission", name(dtt_act2.permission_name)) ("permission", name(dtt_act2.permission_name))
("parent", "active") ("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() push_action(config::system_account_name, linkauth::get_name(), "testapi", fc::mutable_variant_object()
("account", "testapi") ("account", "testapi")
("code", name(dtt_act2.deferred_account)) ("code", name(dtt_act2.deferred_account))
("type", name(dtt_act2.deferred_action)) ("type", name(dtt_act2.deferred_action))
("requirement", name(dtt_act2.permission_name))); ("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 // Meanwhile, if the deferred tx receiver == this tx receiver, the delay will be ignored, this tx should succeed
dtt_action dtt_act3; dtt_action dtt_act3;
dtt_act3.deferred_account = N(testapi); dtt_act3.deferred_account = N(testapi);
...@@ -937,11 +946,11 @@ BOOST_FIXTURE_TEST_CASE(deferred_transaction_tests, TESTER) { try { ...@@ -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 // - 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() push_action(config::system_account_name, N(setpriv), config::system_account_name, mutable_variant_object()
("account", "testapi") ("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_act1));
CALL_TEST_FUNCTION(*this, "test_transaction", "send_deferred_tx_with_dtt_action", fc::raw::pack(dtt_act2)); CALL_TEST_FUNCTION(*this, "test_transaction", "send_deferred_tx_with_dtt_action", fc::raw::pack(dtt_act2));
} }
BOOST_REQUIRE_EQUAL( validate(), true ); BOOST_REQUIRE_EQUAL( validate(), true );
} FC_LOG_AND_RETHROW() } } FC_LOG_AND_RETHROW() }
...@@ -1528,7 +1537,7 @@ BOOST_FIXTURE_TEST_CASE(permission_tests, TESTER) { try { ...@@ -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", CALL_TEST_FUNCTION( *this, "test_permission", "check_authorization",
fc::raw::pack( check_auth { fc::raw::pack( check_auth {
...@@ -1539,7 +1548,7 @@ BOOST_FIXTURE_TEST_CASE(permission_tests, TESTER) { try { ...@@ -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", CALL_TEST_FUNCTION( *this, "test_permission", "check_authorization",
fc::raw::pack( check_auth { fc::raw::pack( check_auth {
...@@ -1551,7 +1560,7 @@ BOOST_FIXTURE_TEST_CASE(permission_tests, TESTER) { try { ...@@ -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", CALL_TEST_FUNCTION( *this, "test_permission", "check_authorization",
fc::raw::pack( check_auth { fc::raw::pack( check_auth {
...@@ -1562,7 +1571,7 @@ BOOST_FIXTURE_TEST_CASE(permission_tests, TESTER) { try { ...@@ -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", CALL_TEST_FUNCTION( *this, "test_permission", "check_authorization",
fc::raw::pack( check_auth { fc::raw::pack( check_auth {
...@@ -1571,7 +1580,7 @@ BOOST_FIXTURE_TEST_CASE(permission_tests, TESTER) { try { ...@@ -1571,7 +1580,7 @@ BOOST_FIXTURE_TEST_CASE(permission_tests, TESTER) { try {
.pubkeys = {} .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", CALL_TEST_FUNCTION( *this, "test_permission", "check_authorization",
fc::raw::pack( check_auth { fc::raw::pack( check_auth {
...@@ -1582,7 +1591,7 @@ BOOST_FIXTURE_TEST_CASE(permission_tests, TESTER) { try { ...@@ -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", BOOST_CHECK_EXCEPTION(CALL_TEST_FUNCTION( *this, "test_permission", "check_authorization",
......
...@@ -26,7 +26,7 @@ BOOST_FIXTURE_TEST_CASE( missing_sigs, TESTER ) { try { ...@@ -26,7 +26,7 @@ BOOST_FIXTURE_TEST_CASE( missing_sigs, TESTER ) { try {
create_accounts( {N(alice)} ); create_accounts( {N(alice)} );
produce_block(); 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"); auto trace = push_reqauth(N(alice), "owner");
produce_block(); produce_block();
...@@ -39,7 +39,7 @@ BOOST_FIXTURE_TEST_CASE( missing_multi_sigs, TESTER ) { try { ...@@ -39,7 +39,7 @@ BOOST_FIXTURE_TEST_CASE( missing_multi_sigs, TESTER ) { try {
create_account(N(alice), config::system_account_name, true); create_account(N(alice), config::system_account_name, true);
produce_block(); 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 auto trace = push_reqauth(N(alice), "owner", true); // with multisig
produce_block(); produce_block();
...@@ -288,7 +288,7 @@ BOOST_AUTO_TEST_CASE(link_then_update_auth) { try { ...@@ -288,7 +288,7 @@ BOOST_AUTO_TEST_CASE(link_then_update_auth) { try {
// Update "first" auth public key // Update "first" auth public key
chain.set_authority("alice", "first", second_pub_key, "active"); chain.set_authority("alice", "first", second_pub_key, "active");
// Authority updated, using previous "first" auth should fail on linked auth // 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 // Using updated authority, should succeed
chain.push_reqauth("alice", { permission_level{N(alice), "first"} }, { second_priv_key }); chain.push_reqauth("alice", { permission_level{N(alice), "first"} }, { second_priv_key });
...@@ -303,14 +303,14 @@ try { ...@@ -303,14 +303,14 @@ try {
// Verify account created properly // Verify account created properly
const auto& joe_owner_authority = chain.get<permission_object, by_owner>(boost::make_tuple("joe", "owner")); 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.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(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(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); 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")); 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.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(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(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); BOOST_TEST(joe_active_authority.auth.keys[0].weight == 1);
......
...@@ -1137,18 +1137,22 @@ BOOST_AUTO_TEST_CASE( link_delay_link_change_test ) { try { ...@@ -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_EQUAL(asset::from_string("0.0000 CUR"), liquid_balance);
BOOST_REQUIRE_EXCEPTION( 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") ("account", "tester")
("code", eosio_token) ("code", eosio_token)
("type", "transfer") ("type", "transfer")
("requirement", "second"), ("requirement", "second"),
30, 3), 30, 3),
insufficient_delay_exception, unsatisfied_authorization,
fc_exception_message_starts_with("authorization imposes a delay") fc_exception_message_starts_with("transaction declares authority")
); );
// this transaction will be delayed 20 blocks // 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") ("account", "tester")
("code", eosio_token) ("code", eosio_token)
("type", "transfer") ("type", "transfer")
...@@ -1330,13 +1334,16 @@ BOOST_AUTO_TEST_CASE( link_delay_unlink_test ) { try { ...@@ -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_EQUAL(asset::from_string("0.0000 CUR"), liquid_balance);
BOOST_REQUIRE_EXCEPTION( BOOST_REQUIRE_EXCEPTION(
chain.push_action(config::system_account_name, unlinkauth::get_name(), tester_account, fc::mutable_variant_object() chain.push_action( config::system_account_name, unlinkauth::get_name(),
("account", "tester") vector<permission_level>{{tester_account, N(first)}},
("code", eosio_token) fc::mutable_variant_object()
("type", "transfer"), ("account", "tester")
30, 7), ("code", eosio_token)
insufficient_delay_exception, ("type", "transfer"),
fc_exception_message_starts_with("authorization imposes a delay") 30, 7
),
unsatisfied_authorization,
fc_exception_message_starts_with("transaction declares authority")
); );
// this transaction will be delayed 20 blocks // this transaction will be delayed 20 blocks
...@@ -1854,15 +1861,18 @@ BOOST_AUTO_TEST_CASE( canceldelay_test ) { try { ...@@ -1854,15 +1861,18 @@ BOOST_AUTO_TEST_CASE( canceldelay_test ) { try {
BOOST_REQUIRE_EQUAL(asset::from_string("0.0000 CUR"), liquid_balance); BOOST_REQUIRE_EQUAL(asset::from_string("0.0000 CUR"), liquid_balance);
BOOST_REQUIRE_EXCEPTION( 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") ("account", "tester")
("permission", "first") ("permission", "first")
("parent", "active") ("parent", "active")
("auth", authority(chain.get_public_key(tester_account, "first"))), ("auth", authority(chain.get_public_key(tester_account, "first"))),
30, 7 30, 7
), ),
insufficient_delay_exception, unsatisfied_authorization,
fc_exception_message_starts_with("authorization imposes a delay") fc_exception_message_starts_with("transaction declares authority")
); );
// this transaction will be delayed 20 blocks // this transaction will be delayed 20 blocks
......
...@@ -224,24 +224,23 @@ BOOST_AUTO_TEST_CASE(authority_checker) ...@@ -224,24 +224,23 @@ BOOST_AUTO_TEST_CASE(authority_checker)
auto GetNullAuthority = [](auto){abort(); return authority();}; auto GetNullAuthority = [](auto){abort(); return authority();};
permission_visitor pv;
auto A = authority(2, {key_weight{a, 1}, key_weight{b, 1}}); 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.satisfied(A));
BOOST_TEST(checker.all_keys_used()); BOOST_TEST(checker.all_keys_used());
BOOST_TEST(checker.used_keys().size() == 2); BOOST_TEST(checker.used_keys().size() == 2);
BOOST_TEST(checker.unused_keys().size() == 0); 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.satisfied(A));
BOOST_TEST(!checker.all_keys_used()); BOOST_TEST(!checker.all_keys_used());
BOOST_TEST(checker.used_keys().size() == 0); BOOST_TEST(checker.used_keys().size() == 0);
BOOST_TEST(checker.unused_keys().size() == 2); 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.satisfied(A));
BOOST_TEST(!checker.all_keys_used()); BOOST_TEST(!checker.all_keys_used());
BOOST_TEST(checker.used_keys().size() == 2); BOOST_TEST(checker.used_keys().size() == 2);
...@@ -251,27 +250,27 @@ BOOST_AUTO_TEST_CASE(authority_checker) ...@@ -251,27 +250,27 @@ BOOST_AUTO_TEST_CASE(authority_checker)
BOOST_TEST(checker.unused_keys().count(c) == 1); 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.satisfied(A));
BOOST_TEST(!checker.all_keys_used()); BOOST_TEST(!checker.all_keys_used());
BOOST_TEST(checker.used_keys().size() == 0); BOOST_TEST(checker.used_keys().size() == 0);
} }
A = authority(3, {key_weight{a, 1}, key_weight{b, 1}, key_weight{c, 1}}); 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, 2, {c, b, a}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetNullAuthority, pv, 2, {a, b}).satisfied(A)); BOOST_TEST(!make_auth_checker(GetNullAuthority, 2, {a, b}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetNullAuthority, pv, 2, {a, c}).satisfied(A)); BOOST_TEST(!make_auth_checker(GetNullAuthority, 2, {a, c}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetNullAuthority, pv, 2, {b, 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}}); 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, 2, {a}).satisfied(A));
BOOST_TEST(make_auth_checker(GetNullAuthority, pv, 2, {b}).satisfied(A)); BOOST_TEST(make_auth_checker(GetNullAuthority, 2, {b}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetNullAuthority, pv, 2, {c}).satisfied(A)); BOOST_TEST(!make_auth_checker(GetNullAuthority, 2, {c}).satisfied(A));
A = authority(1, {key_weight{a, 2}, key_weight{b, 1}}); 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, 2, {a}).satisfied(A));
BOOST_TEST(make_auth_checker(GetNullAuthority, pv, 2, {b}).satisfied(A)); BOOST_TEST(make_auth_checker(GetNullAuthority, 2, {b}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetNullAuthority, pv, 2, {c}).satisfied(A)); BOOST_TEST(!make_auth_checker(GetNullAuthority, 2, {c}).satisfied(A));
auto GetCAuthority = [c](auto){ auto GetCAuthority = [c](auto){
return authority(1, {key_weight{c, 1}}); return authority(1, {key_weight{c, 1}});
...@@ -279,26 +278,26 @@ BOOST_AUTO_TEST_CASE(authority_checker) ...@@ -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}}); 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.satisfied(A));
BOOST_TEST(checker.all_keys_used()); 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.satisfied(A));
BOOST_TEST(checker.used_keys().size() == 0); BOOST_TEST(checker.used_keys().size() == 0);
BOOST_TEST(checker.unused_keys().size() == 1); BOOST_TEST(checker.unused_keys().size() == 1);
BOOST_TEST(checker.unused_keys().count(b) == 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.satisfied(A));
BOOST_TEST(checker.used_keys().size() == 0); BOOST_TEST(checker.used_keys().size() == 0);
BOOST_TEST(checker.unused_keys().size() == 1); BOOST_TEST(checker.unused_keys().size() == 1);
BOOST_TEST(checker.unused_keys().count(c) == 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.satisfied(A));
BOOST_TEST(checker.all_keys_used()); BOOST_TEST(checker.all_keys_used());
BOOST_TEST(checker.used_keys().size() == 2); BOOST_TEST(checker.used_keys().size() == 2);
...@@ -307,7 +306,7 @@ BOOST_AUTO_TEST_CASE(authority_checker) ...@@ -307,7 +306,7 @@ BOOST_AUTO_TEST_CASE(authority_checker)
BOOST_TEST(checker.used_keys().count(c) == 1); 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.satisfied(A));
BOOST_TEST(!checker.all_keys_used()); BOOST_TEST(!checker.all_keys_used());
BOOST_TEST(checker.used_keys().size() == 1); BOOST_TEST(checker.used_keys().size() == 1);
...@@ -318,19 +317,13 @@ BOOST_AUTO_TEST_CASE(authority_checker) ...@@ -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}}); A = authority(3, {key_weight{a, 2}, key_weight{b, 1}}, {permission_level_weight{{"hello", "world"}, 3}});
pv._log = true;
{ {
pv.permissions.clear(); auto checker = make_auth_checker(GetCAuthority, 2, {a, b});
pv.size_stack.clear();
auto checker = make_auth_checker(GetCAuthority, pv, 2, {a, b});
BOOST_TEST(checker.satisfied(A)); BOOST_TEST(checker.satisfied(A));
BOOST_TEST(checker.all_keys_used()); BOOST_TEST(checker.all_keys_used());
BOOST_TEST(pv.permissions.size() == 0);
} }
{ {
pv.permissions.clear(); auto checker = make_auth_checker(GetCAuthority, 2, {a, b, c});
pv.size_stack.clear();
auto checker = make_auth_checker(GetCAuthority, pv, 2, {a, b, c});
BOOST_TEST(checker.satisfied(A)); BOOST_TEST(checker.satisfied(A));
BOOST_TEST(!checker.all_keys_used()); BOOST_TEST(!checker.all_keys_used());
BOOST_TEST(checker.used_keys().size() == 1); BOOST_TEST(checker.used_keys().size() == 1);
...@@ -338,21 +331,17 @@ BOOST_AUTO_TEST_CASE(authority_checker) ...@@ -338,21 +331,17 @@ BOOST_AUTO_TEST_CASE(authority_checker)
BOOST_TEST(checker.unused_keys().size() == 2); BOOST_TEST(checker.unused_keys().size() == 2);
BOOST_TEST(checker.unused_keys().count(a) == 1); BOOST_TEST(checker.unused_keys().count(a) == 1);
BOOST_TEST(checker.unused_keys().count(b) == 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}}); 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, 2, {a}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetCAuthority, pv, 2, {b}).satisfied(A)); BOOST_TEST(!make_auth_checker(GetCAuthority, 2, {b}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetCAuthority, pv, 2, {c}).satisfied(A)); BOOST_TEST(!make_auth_checker(GetCAuthority, 2, {c}).satisfied(A));
BOOST_TEST(make_auth_checker(GetCAuthority, pv, 2, {a, b}).satisfied(A)); BOOST_TEST(make_auth_checker(GetCAuthority, 2, {a, b}).satisfied(A));
BOOST_TEST(make_auth_checker(GetCAuthority, pv, 2, {b, c}).satisfied(A)); BOOST_TEST(make_auth_checker(GetCAuthority, 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, 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.satisfied(A));
BOOST_TEST(!checker.all_keys_used()); BOOST_TEST(!checker.all_keys_used());
BOOST_TEST(checker.used_keys().size() == 2); BOOST_TEST(checker.used_keys().size() == 2);
...@@ -361,12 +350,12 @@ BOOST_AUTO_TEST_CASE(authority_checker) ...@@ -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}}); 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, 2, {a, b}).satisfied(A));
BOOST_TEST(make_auth_checker(GetCAuthority, pv, 2, {c}).satisfied(A)); BOOST_TEST(make_auth_checker(GetCAuthority, 2, {c}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetCAuthority, pv, 2, {a}).satisfied(A)); BOOST_TEST(!make_auth_checker(GetCAuthority, 2, {a}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetCAuthority, pv, 2, {b}).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.satisfied(A));
BOOST_TEST(!checker.all_keys_used()); BOOST_TEST(!checker.all_keys_used());
BOOST_TEST(checker.used_keys().size() == 1); BOOST_TEST(checker.used_keys().size() == 1);
...@@ -385,12 +374,12 @@ BOOST_AUTO_TEST_CASE(authority_checker) ...@@ -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}}); 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.satisfied(A));
BOOST_TEST(checker.all_keys_used()); 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.satisfied(A));
BOOST_TEST(!checker.all_keys_used()); BOOST_TEST(!checker.all_keys_used());
BOOST_TEST(checker.used_keys().size() == 2); BOOST_TEST(checker.used_keys().size() == 2);
...@@ -399,7 +388,7 @@ BOOST_AUTO_TEST_CASE(authority_checker) ...@@ -399,7 +388,7 @@ BOOST_AUTO_TEST_CASE(authority_checker)
BOOST_TEST(checker.used_keys().count(e) == 1); 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.satisfied(A));
BOOST_TEST(!checker.all_keys_used()); BOOST_TEST(!checker.all_keys_used());
BOOST_TEST(checker.used_keys().size() == 3); BOOST_TEST(checker.used_keys().size() == 3);
...@@ -408,28 +397,28 @@ BOOST_AUTO_TEST_CASE(authority_checker) ...@@ -408,28 +397,28 @@ BOOST_AUTO_TEST_CASE(authority_checker)
BOOST_TEST(checker.used_keys().count(b) == 1); BOOST_TEST(checker.used_keys().count(b) == 1);
BOOST_TEST(checker.used_keys().count(c) == 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 // 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 < a);
BOOST_TEST(b < c); BOOST_TEST(b < c);
BOOST_TEST(a < c); BOOST_TEST(a < c);
{ {
// valid key order: c > a > b // valid key order: b < a < c
A = authority(2, {key_weight{c, 1}, key_weight{a, 1}, key_weight{b, 1}}); A = authority(2, {key_weight{b, 1}, key_weight{a, 1}, key_weight{c, 1}});
// valid key order: c > b // valid key order: b < c
auto B = authority(1, {key_weight{c, 1}, key_weight{b, 1}}); auto B = authority(1, {key_weight{b, 1}, key_weight{c, 1}});
// invalid key order: b < c // invalid key order: c > b
auto C = authority(1, {key_weight{c, 1}, key_weight{b, 1}, key_weight{c, 1}}); auto C = authority(1, {key_weight{b, 1}, key_weight{c, 1}, key_weight{b, 1}});
// invalid key order: duplicate c // 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 // 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 // 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(A));
BOOST_TEST(validate(B)); BOOST_TEST(validate(B));
BOOST_TEST(!validate(C)); BOOST_TEST(!validate(C));
...@@ -438,51 +427,51 @@ BOOST_AUTO_TEST_CASE(authority_checker) ...@@ -438,51 +427,51 @@ BOOST_AUTO_TEST_CASE(authority_checker)
BOOST_TEST(!validate(F)); BOOST_TEST(!validate(F));
BOOST_TEST(!checker.all_keys_used()); 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(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(A));
BOOST_TEST(checker.satisfied(B)); BOOST_TEST(checker.satisfied(B));
BOOST_TEST(!checker.all_keys_used()); 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(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}}, auto A2 = authority(4, {key_weight{b, 1}, key_weight{a, 1}, key_weight{c, 1}},
{permission_level_weight{{"hi", "world"}, 1}, { permission_level_weight{{"a", "world"}, 1},
permission_level_weight{{"hello", "world"}, 1}, permission_level_weight{{"hello", "world"}, 1},
permission_level_weight{{"a", "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} {permission_level_weight{{"hello", "world"}, 1}
}); });
auto C2 = authority(4, {key_weight{c, 1}, key_weight{a, 1}, key_weight{b, 1}}, auto C2 = authority(4, {key_weight{b, 1}, key_weight{a, 1}, key_weight{c, 1}},
{permission_level_weight{{"hello", "world"}, 1}, { permission_level_weight{{"hello", "there"}, 1},
permission_level_weight{{"hello", "there"}, 1} permission_level_weight{{"hello", "world"}, 1}
}); });
// invalid: duplicate // invalid: duplicate
auto D2 = authority(4, {key_weight{c, 1}, key_weight{a, 1}, key_weight{b, 1}}, 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"}, 1},
permission_level_weight{{"hello", "world"}, 2} permission_level_weight{{"hello", "world"}, 2}
}); });
// invalid: wrong order // invalid: wrong order
auto E2 = authority(4, {key_weight{c, 1}, key_weight{a, 1}, key_weight{b, 1}}, auto E2 = 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"}, 2},
permission_level_weight{{"hello", "world"}, 2} permission_level_weight{{"hello", "there"}, 1}
}); });
// invalid: wrong order // invalid: wrong order
auto F2 = authority(4, {key_weight{c, 1}, key_weight{a, 1}, key_weight{b, 1}}, auto F2 = authority(4, {key_weight{b, 1}, key_weight{a, 1}, key_weight{c, 1}},
{permission_level_weight{{"hello", "world"}, 1}, { permission_level_weight{{"hi", "world"}, 2},
permission_level_weight{{"hi", "world"}, 2} permission_level_weight{{"hello", "world"}, 1}
}); });
// invalid: insufficient weight // invalid: insufficient weight
auto G2 = authority(7, {key_weight{c, 1}, key_weight{a, 1}, key_weight{b, 1}}, auto G2 = authority(7, {key_weight{b, 1}, key_weight{a, 1}, key_weight{c, 1}},
{permission_level_weight{{"hi", "world"}, 1}, { permission_level_weight{{"a", "world"}, 1},
permission_level_weight{{"hello", "world"}, 1}, permission_level_weight{{"hello", "world"}, 1},
permission_level_weight{{"a", "world"}, 1} permission_level_weight{{"hi", "world"}, 1}
}); });
BOOST_TEST(validate(A2)); BOOST_TEST(validate(A2));
BOOST_TEST(validate(B2)); BOOST_TEST(validate(B2));
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册