未验证 提交 331d4218 编写于 作者: D Daniel Larimer 提交者: GitHub

Merge pull request #1859 from brianjohnson5972/398-delayed-signed-txn

Delayed Transactions
......@@ -198,7 +198,10 @@ void apply_context::require_recipient( account_name code ) {
void apply_context::execute_inline( action&& a ) {
if ( !privileged ) {
if( a.account != receiver ) {
controller.check_authorization({a}, flat_set<public_key_type>(), false, {receiver});
const auto delay = controller.check_authorization({a}, flat_set<public_key_type>(), false, {receiver});
FC_ASSERT( trx_meta.published + delay <= controller.head_block_time(),
"inline action uses a permission that imposes a delay that is not met, add an action of mindelay with delay of atleast ${delay}",
("delay", delay.sec_since_epoch()) );
}
}
_inline_actions.emplace_back( move(a) );
......@@ -232,8 +235,12 @@ void apply_context::execute_deferred( deferred_transaction&& trx ) {
break;
}
}
if( check_auth )
controller.check_authorization(trx.actions, flat_set<public_key_type>(), false, {receiver});
if( check_auth ) {
const auto delay = controller.check_authorization(trx.actions, flat_set<public_key_type>(), false, {receiver});
FC_ASSERT( trx_meta.published + delay <= controller.head_block_time(),
"deferred transaction uses a permission that imposes a delay that is not met, add an action of mindelay with delay of atleast ${delay}",
("delay", delay.sec_since_epoch()) );
}
}
trx.sender = receiver; // "Attempting to send from another account"
......@@ -355,10 +362,31 @@ int apply_context::get_context_free_data( uint32_t index, char* buffer, size_t b
return s;
}
uint32_t apply_context::get_next_sender_id() {
const uint64_t id = N(config::eosio_auth_scope);
const auto table = N(deferred.seq);
const auto payer = config::system_account_name;
const auto iter = db_find_i64(config::system_account_name, config::eosio_auth_scope, table, id);
if (iter == -1) {
const uint32_t next_serial = 1;
db_store_i64(config::system_account_name, config::eosio_auth_scope, table, payer, id, (const char*)&next_serial, sizeof(next_serial));
return 0;
}
uint32_t next_serial = 0;
db_get_i64(iter, (char*)&next_serial, sizeof(next_serial));
const auto result = next_serial++;
db_update_i64(iter, payer, (const char*)&next_serial, sizeof(next_serial));
return result;
}
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);
}
int apply_context::db_store_i64( uint64_t code, uint64_t scope, uint64_t table, const account_name& payer, uint64_t id, const char* buffer, size_t buffer_size ) {
require_write_lock( scope );
const auto& tab = find_or_create_table( receiver, scope, table );
const auto& tab = find_or_create_table( code, scope, table );
auto tableid = tab.id;
FC_ASSERT( payer != account_name(), "must specify a valid account to pay for new record" );
......
......@@ -256,18 +256,55 @@ transaction_trace chain_controller::push_transaction(const packed_transaction& t
});
} FC_CAPTURE_AND_RETHROW() }
transaction_trace chain_controller::_push_transaction(const packed_transaction& trx)
transaction_trace chain_controller::_push_transaction(const packed_transaction& packed_trx)
{ try {
transaction_metadata mtrx( trx, get_chain_id(), head_block_time());
transaction_metadata mtrx( packed_trx, get_chain_id(), head_block_time());
const auto delay = check_transaction_authorization(mtrx.trx(), packed_trx.signatures, packed_trx.context_free_data);
transaction_trace result(mtrx.id);
if (!delay.sec_since_epoch()) {
result = _push_transaction(std::move(mtrx));
// notify anyone listening to pending transactions
on_pending_transaction(_pending_transaction_metas.back(), packed_trx);
_pending_block->input_transactions.emplace_back(packed_trx);
} else {
result.status = transaction_trace::delayed;
const auto trx = mtrx.trx();
FC_ASSERT( !trx.actions.empty(), "transaction must have at least one action");
FC_ASSERT( trx.expiration > (head_block_time() + fc::milliseconds(2*config::block_interval_ms)),
"transaction is expired when created" );
// add in the system account authorization
action for_deferred = trx.actions[0];
bool found = false;
for (const auto& auth : for_deferred.authorization) {
if (auth.actor == config::system_account_name &&
auth.permission == config::active_name) {
found = true;
break;
}
}
if (!found)
for_deferred.authorization.push_back(permission_level{config::system_account_name, config::active_name});
apply_context context(*this, _db, for_deferred, mtrx);
check_transaction_authorization(mtrx.trx(), trx.signatures, trx.context_free_data);
auto result = _push_transaction(std::move(mtrx));
time_point_sec execute_after = head_block_time();
execute_after += time_point_sec(delay);
deferred_transaction dtrx(context.get_next_sender_id(), config::system_account_name, execute_after, trx);
FC_ASSERT( dtrx.execute_after < dtrx.expiration, "transaction expires before it can execute" );
// notify anyone listening to pending transactions
on_pending_transaction(_pending_transaction_metas.back(), trx);
result.deferred_transaction_requests.push_back(std::move(dtrx));
_pending_block->input_transactions.emplace_back(trx);
// notify anyone listening to pending transactions
on_pending_transaction(std::move(mtrx), packed_trx);
store_deferred_transaction(result.deferred_transaction_requests[0].get<deferred_transaction>());
}
return result;
} FC_CAPTURE_AND_RETHROW() }
......@@ -412,6 +449,21 @@ void chain_controller::_finalize_pending_cycle()
_pending_cycle_trace.reset();
}
void chain_controller::store_deferred_transaction(const deferred_transaction& dtrx)
{
_db.create<generated_transaction_object>([&](generated_transaction_object &obj) {
obj.trx_id = dtrx.id();
obj.sender = dtrx.sender;
obj.sender_id = dtrx.sender_id;
obj.expiration = dtrx.expiration;
obj.delay_until = dtrx.execute_after;
obj.published = head_block_time();
obj.packed_trx.resize(fc::raw::pack_size(dtrx));
fc::datastream<char *> ds(obj.packed_trx.data(), obj.packed_trx.size());
fc::raw::pack(ds, dtrx);
});
}
void chain_controller::_apply_cycle_trace( const cycle_trace& res )
{
auto &generated_transaction_idx = _db.get_mutable_index<generated_transaction_multi_index>();
......@@ -433,17 +485,7 @@ void chain_controller::_apply_cycle_trace( const cycle_trace& res )
fc::raw::pack(ds, dt);
});
} else {
_db.create<generated_transaction_object>([&](generated_transaction_object &obj) {
obj.trx_id = dt.id();
obj.sender = dt.sender;
obj.sender_id = dt.sender_id;
obj.expiration = dt.expiration;
obj.delay_until = dt.execute_after;
obj.published = head_block_time();
obj.packed_trx.resize(fc::raw::pack_size(dt));
fc::datastream<char *> ds(obj.packed_trx.data(), obj.packed_trx.size());
fc::raw::pack(ds, dt);
});
store_deferred_transaction(dt);
}
} else if ( req.contains<deferred_reference>() ) {
const auto& dr = req.get<deferred_reference>();
......@@ -782,6 +824,7 @@ flat_set<public_key_type> chain_controller::get_required_keys(const transaction&
const flat_set<public_key_type>& candidate_keys)const
{
auto checker = make_auth_checker( [&](const permission_level& p){ return get_permission(p).auth; },
[](const permission_level& ) {},
get_global_properties().configuration.max_authority_depth,
candidate_keys);
......@@ -798,64 +841,154 @@ flat_set<public_key_type> chain_controller::get_required_keys(const transaction&
return checker.used_keys();
}
void chain_controller::check_authorization( const vector<action>& actions,
const flat_set<public_key_type>& provided_keys,
bool allow_unused_signatures,
flat_set<account_name> provided_accounts )const
class permission_visitor {
public:
permission_visitor(const chain_controller& controller) : _chain_controller(controller) {}
void operator()(const permission_level& perm_level) {
const auto obj = _chain_controller.get_permission(perm_level);
if (_max_delay < obj.delay)
_max_delay = obj.delay;
}
const time_point& get_max_delay() const { return _max_delay; }
private:
const chain_controller& _chain_controller;
time_point _max_delay;
};
time_point chain_controller::check_authorization( const vector<action>& actions,
const flat_set<public_key_type>& provided_keys,
bool allow_unused_signatures,
flat_set<account_name> provided_accounts )const
{
auto checker = make_auth_checker( [&](const permission_level& p){ return get_permission(p).auth; },
permission_visitor(*this),
get_global_properties().configuration.max_authority_depth,
provided_keys, provided_accounts );
time_point max_delay;
for( const auto& act : actions ) {
for( const auto& declared_auth : act.authorization ) {
// check a minimum permission if one is set, otherwise assume the contract code will validate
auto min_permission_name = lookup_minimum_permission(declared_auth.actor, act.account, act.name);
bool min_permission_required = true;
if (!min_permission_name) {
// for updateauth actions, need to determine the permission that is changing
if (act.account == config::system_account_name && act.name == contracts::updateauth::get_name()) {
auto update = act.data_as<contracts::updateauth>();
const auto permission_to_change = _db.find<permission_object, by_owner>(boost::make_tuple(update.account, update.permission));
if (permission_to_change != nullptr) {
// only determining delay
min_permission_required = false;
min_permission_name = update.permission;
}
}
}
if (min_permission_name) {
const auto& min_permission = _db.get<permission_object, by_owner>(boost::make_tuple(declared_auth.actor, *min_permission_name));
if ((_skip_flags & skip_authority_check) == false) {
const auto &index = _db.get_index<permission_index>().indices();
EOS_ASSERT(get_permission(declared_auth).satisfies(min_permission, index),
const auto& index = _db.get_index<permission_index>().indices();
const optional<time_point> delay = get_permission(declared_auth).satisfies(min_permission, index);
EOS_ASSERT(!min_permission_required || delay.valid(),
tx_irrelevant_auth,
"action declares irrelevant authority '${auth}'; minimum authority is ${min}",
("auth", declared_auth)("min", min_permission.name));
if (max_delay < *delay)
max_delay = *delay;
}
}
if (act.account == config::system_account_name) {
// for link changes, we need to also determine the delay associated with an existing link that is being
// moved or removed
if (act.name == contracts::linkauth::get_name()) {
auto link = act.data_as<contracts::linkauth>();
if (declared_auth.actor == link.account) {
const auto linked_permission_name = lookup_linked_permission(link.account, link.code, link.type);
if (linked_permission_name.valid()) {
const auto& linked_permission = _db.get<permission_object, by_owner>(boost::make_tuple(link.account, *linked_permission_name));
const auto& index = _db.get_index<permission_index>().indices();
const optional<time_point> delay = get_permission(declared_auth).satisfies(linked_permission, index);
if (delay.valid() && max_delay < *delay)
max_delay = *delay;
} // else it is only a new link, so don't need to delay
}
} else if (act.name == contracts::unlinkauth::get_name()) {
auto unlink = act.data_as<contracts::unlinkauth>();
if (declared_auth.actor == unlink.account) {
const auto unlinked_permission_name = lookup_linked_permission(unlink.account, unlink.code, unlink.type);
if (unlinked_permission_name.valid()) {
const auto& unlinked_permission = _db.get<permission_object, by_owner>(boost::make_tuple(unlink.account, *unlinked_permission_name));
const auto& index = _db.get_index<permission_index>().indices();
const optional<time_point> delay = get_permission(declared_auth).satisfies(unlinked_permission, index);
if (delay.valid() && max_delay < *delay)
max_delay = *delay;
}
}
}
}
if ((_skip_flags & skip_transaction_signatures) == false) {
EOS_ASSERT(checker.satisfied(declared_auth), tx_missing_sigs,
"transaction declares authority '${auth}', but does not have signatures for it.",
("auth", declared_auth));
}
}
if (act.account == config::system_account_name && act.name == contracts::mindelay::get_name()) {
const auto mindelay = act.data_as<contracts::mindelay>();
const time_point delay = time_point_sec{mindelay.delay.convert_to<uint32_t>()};
if (max_delay < delay)
max_delay = delay;
}
}
if (!allow_unused_signatures && (_skip_flags & skip_transaction_signatures) == false)
EOS_ASSERT(checker.all_keys_used(), tx_irrelevant_sig,
"transaction bears irrelevant signatures from these keys: ${keys}",
("keys", checker.unused_keys()));
const auto checker_max_delay = checker.get_permission_visitor().get_max_delay();
if (max_delay < checker_max_delay)
max_delay = checker_max_delay;
return max_delay;
}
void chain_controller::check_transaction_authorization(const transaction& trx,
const vector<signature_type>& signatures,
const vector<bytes>& cfd,
bool allow_unused_signatures)const
time_point chain_controller::check_transaction_authorization(const transaction& trx,
const vector<signature_type>& signatures,
const vector<bytes>& cfd,
bool allow_unused_signatures)const
{
check_authorization( trx.actions, trx.get_signature_keys( signatures, chain_id_type{}, cfd ), allow_unused_signatures );
return check_authorization( trx.actions, trx.get_signature_keys( signatures, chain_id_type{}, cfd ), allow_unused_signatures );
}
optional<permission_name> chain_controller::lookup_minimum_permission(account_name authorizer_account,
account_name scope,
action_name act_name) const {
#warning TODO: this comment sounds like it is expecting a check ("may") somewhere else, but I have not found anything else
// updateauth is a special case where any permission _may_ be suitable depending
// on the contents of the action
if (scope == config::system_account_name && act_name == N(updateauth)) {
if (scope == config::system_account_name && act_name == contracts::updateauth::get_name()) {
return optional<permission_name>();
}
try {
optional<permission_name> linked_permission = lookup_linked_permission(authorizer_account, scope, act_name);
if (!linked_permission)
return config::active_name;
return linked_permission;
} FC_CAPTURE_AND_RETHROW((authorizer_account)(scope)(act_name))
}
optional<permission_name> chain_controller::lookup_linked_permission(account_name authorizer_account,
account_name scope,
action_name act_name) const {
try {
// First look up a specific link for this message act_name
auto key = boost::make_tuple(authorizer_account, scope, act_name);
......@@ -869,8 +1002,8 @@ optional<permission_name> chain_controller::lookup_minimum_permission(account_na
// If no specific or default link found, use active permission
if (link != nullptr)
return link->required_permission;
else
return N(active);
return optional<permission_name>();
} FC_CAPTURE_AND_RETHROW((authorizer_account)(scope)(act_name))
}
......@@ -1614,7 +1747,7 @@ vector<transaction_trace> chain_controller::push_deferred_transactions( bool flu
auto& generated_index = generated_transaction_idx.indices().get<by_delay>();
vector<const generated_transaction_object*> candidates;
for( auto itr = generated_index.rbegin(); itr != generated_index.rend() && (head_block_time() >= itr->delay_until); ++itr) {
for( auto itr = generated_index.begin(); itr != generated_index.end() && (head_block_time() >= itr->delay_until); ++itr) {
const auto &gtrx = *itr;
candidates.emplace_back(&gtrx);
}
......
......@@ -46,6 +46,8 @@ void chain_initializer::register_types(chain_controller& chain, chainbase::datab
SET_APP_HANDLER( eosio, eosio, postrecovery, eosio );
SET_APP_HANDLER( eosio, eosio, passrecovery, eosio );
SET_APP_HANDLER( eosio, eosio, vetorecovery, eosio );
SET_APP_HANDLER( eosio, eosio, canceldelay, eosio );
SET_APP_HANDLER( eosio, eosio, mindelay, eosio );
}
......@@ -73,6 +75,8 @@ abi_def chain_initializer::eos_contract_abi(const abi_def& eosio_system_abi)
eos_abi.actions.push_back( action_def{name("vetorecovery"), "vetorecovery"} );
eos_abi.actions.push_back( action_def{name("onerror"), "onerror"} );
eos_abi.actions.push_back( action_def{name("onblock"), "onblock"} );
eos_abi.actions.push_back( action_def{name("canceldelay"), "canceldelay"} );
eos_abi.actions.push_back( action_def{name("mindelay"), "mindelay"} );
// ACTION PAYLOADS
......@@ -99,6 +103,7 @@ abi_def chain_initializer::eos_contract_abi(const abi_def& eosio_system_abi)
{"permission", "permission_name"},
{"parent", "permission_name"},
{"data", "authority"},
{"delay", "uint32"}
}
});
......@@ -156,6 +161,18 @@ abi_def chain_initializer::eos_contract_abi(const abi_def& eosio_system_abi)
}
});
eos_abi.structs.emplace_back( struct_def {
"canceldelay", "", {
{"sender_id", "uint32"},
}
});
eos_abi.structs.emplace_back( struct_def {
"mindelay", "", {
{"delay", "uint32"},
}
});
// DATABASE RECORDS
eos_abi.structs.emplace_back( struct_def {
......
......@@ -14,6 +14,7 @@
#include <eosio/chain/account_object.hpp>
#include <eosio/chain/permission_object.hpp>
#include <eosio/chain/permission_link_object.hpp>
#include <eosio/chain/generated_transaction_object.hpp>
#include <eosio/chain/global_property_object.hpp>
#include <eosio/chain/contracts/types.hpp>
#include <eosio/chain/producer_object.hpp>
......@@ -199,6 +200,7 @@ void apply_eosio_updateauth(apply_context& context) {
po.auth = update.data;
po.parent = parent_id;
po.last_updated = context.controller.head_block_time();
po.delay = time_point_sec(update.delay.convert_to<uint64_t>());
});
} else {
// TODO/QUESTION: If we are creating a new permission, should we check if the message declared
......@@ -209,6 +211,7 @@ void apply_eosio_updateauth(apply_context& context) {
po.auth = update.data;
po.parent = parent_id;
po.last_updated = context.controller.head_block_time();
po.delay = time_point_sec(update.delay.convert_to<uint64_t>());
});
}
}
......@@ -313,24 +316,6 @@ static optional<variant> get_pending_recovery(apply_context& context, account_na
return optional<variant_object>();
}
static uint32_t get_next_sender_id(apply_context& context) {
const uint64_t id = N(config::eosio_auth_scope);
const auto table = N(deferred.seq);
const auto payer = config::system_account_name;
const auto iter = context.db_find_i64(config::system_account_name, config::eosio_auth_scope, table, id);
if (iter == -1) {
const uint32_t next_serial = 1;
context.db_store_i64(config::eosio_auth_scope, table, payer, id, (const char*)&next_serial, sizeof(next_serial));
return 0;
}
uint32_t next_serial = 0;
context.db_get_i64(iter, (char*)&next_serial, sizeof(next_serial));
const auto result = next_serial++;
context.db_update_i64(iter, payer, (const char*)&next_serial, sizeof(next_serial));
return result;
}
static auto get_account_creation(const apply_context& context, const account_name& account) {
auto const& accnt = context.db.get<account_object, by_name>(account);
return (time_point)accnt.creation_date;
......@@ -393,7 +378,7 @@ void apply_eosio_postrecovery(apply_context& context) {
.data = recover_act.data
}, update);
uint32_t request_id = get_next_sender_id(context);
uint32_t request_id = context.get_next_sender_id();
auto record_data = mutable_variant_object()
("account", account)
......@@ -473,5 +458,38 @@ void apply_eosio_vetorecovery(apply_context& context) {
context.console_append_formatted("Recovery for account ${account} vetoed!\n", mutable_variant_object()("account", account));
}
void apply_eosio_canceldelay(apply_context& context) {
auto cancel = context.act.data_as<canceldelay>();
const auto sender_id = cancel.sender_id.convert_to<uint32_t>();
const auto& generated_transaction_idx = context.controller.get_database().get_index<generated_transaction_multi_index>();
const auto& generated_index = generated_transaction_idx.indices().get<by_sender_id>();
const auto& itr = generated_index.lower_bound(boost::make_tuple(config::system_account_name, sender_id));
FC_ASSERT (itr == generated_index.end() || itr->sender != config::system_account_name || itr->sender_id != sender_id,
"cannot cancel sender_id=${sid}, there is no deferred transaction with that sender_id",("sid",sender_id));
auto dtrx = fc::raw::unpack<deferred_transaction>(itr->packed_trx.data(), itr->packed_trx.size());
set<account_name> accounts;
for (const auto& act : dtrx.actions) {
for (const auto& auth : act.authorization) {
accounts.insert(auth.actor);
}
}
bool found = false;
for (const auto& auth : context.act.authorization) {
if (auth.permission == config::active_name && accounts.count(auth.actor)) {
found = true;
break;
}
}
FC_ASSERT (found, "canceldelay action must be signed with the \"active\" permission for one of the actors"
" provided in the authorizations on the original transaction");
context.cancel_deferred(sender_id);
}
void apply_eosio_mindelay(apply_context& context) {
// all processing is performed in chain_controller::check_authorization
}
} } } // namespace eosio::chain::contracts
......@@ -504,6 +504,8 @@ class apply_context {
const bytes& get_packed_transaction();
uint32_t get_next_sender_id();
const chain_controller& controller;
const chainbase::database& db; ///< database where state is stored
const action& act; ///< message being applied
......@@ -585,6 +587,8 @@ class apply_context {
const table_id_object* find_table( name code, name scope, name table );
const table_id_object& find_or_create_table( name code, name scope, name table );
int db_store_i64( uint64_t code, uint64_t scope, uint64_t table, const account_name& payer, uint64_t id, const char* buffer, size_t buffer_size );
vector<account_name> _notified; ///< keeps track of new accounts to be notifed of current message
vector<action> _inline_actions; ///< queued inline messages
vector<action> _cfa_inline_actions; ///< queued inline messages
......
......@@ -50,10 +50,11 @@ namespace detail {
* @tparam F A callable which takes a single argument of type @ref AccountPermission and returns the corresponding
* authority
*/
template<typename PermissionToAuthorityFunc>
template<typename PermissionToAuthorityFunc, typename PermissionVisitorFunc>
class authority_checker {
private:
PermissionToAuthorityFunc permission_to_authority;
PermissionVisitorFunc permission_visitor;
uint16_t recursion_depth_limit;
vector<public_key_type> signing_keys;
flat_set<account_name> _provided_auths; /// accounts which have authorized the transaction at owner level
......@@ -79,6 +80,8 @@ namespace detail {
}
uint32_t operator()(const permission_level_weight& permission) {
if (recursion_depth < checker.recursion_depth_limit) {
checker.permission_visitor(permission.permission);
if( checker.has_permission( permission.permission.actor ) )
total_weight += permission.weight;
else if( checker.satisfied(permission.permission, recursion_depth + 1) )
......@@ -93,10 +96,12 @@ namespace detail {
}
public:
authority_checker( PermissionToAuthorityFunc permission_to_authority,
authority_checker( PermissionToAuthorityFunc permission_to_authority,
PermissionVisitorFunc permission_visitor,
uint16_t recursion_depth_limit, const flat_set<public_key_type>& signing_keys,
flat_set<account_name> provided_auths = flat_set<account_name>() )
: permission_to_authority(permission_to_authority),
permission_visitor(permission_visitor),
recursion_depth_limit(recursion_depth_limit),
signing_keys(signing_keys.begin(), signing_keys.end()),
_provided_auths(provided_auths.begin(), provided_auths.end()),
......@@ -146,14 +151,19 @@ namespace detail {
auto range = utilities::filter_data_by_marker(signing_keys, _used_keys, false);
return {range.begin(), range.end()};
}
const PermissionVisitorFunc& get_permission_visitor() {
return permission_visitor;
}
}; /// authority_checker
template<typename PermissionToAuthorityFunc>
auto make_auth_checker(PermissionToAuthorityFunc&& pta,
template<typename PermissionToAuthorityFunc, typename PermissionVisitorFunc>
auto make_auth_checker(PermissionToAuthorityFunc&& pta,
PermissionVisitorFunc&& permission_visitor,
uint16_t recursion_depth_limit,
const flat_set<public_key_type>& signing_keys,
const flat_set<account_name>& accounts = flat_set<account_name>() ) {
return authority_checker<PermissionToAuthorityFunc>(std::forward<PermissionToAuthorityFunc>(pta), recursion_depth_limit, signing_keys, accounts);
return authority_checker<PermissionToAuthorityFunc, PermissionVisitorFunc>(std::forward<PermissionToAuthorityFunc>(pta), std::forward<PermissionVisitorFunc>(permission_visitor), recursion_depth_limit, signing_keys, accounts);
}
} } // namespace eosio::chain
......@@ -284,13 +284,13 @@ namespace eosio { namespace chain {
* @param allow_unused_signatures - true if method should not assert on unused signatures
* @param provided_accounts - the set of accounts which have authorized the transaction (presumed to be owner)
*
* @return true if the provided keys and accounts are sufficient to authorize actions of the transaction
* @return time_point set to the max delay that this authorization requires to complete
*/
void check_authorization( const vector<action>& actions,
const flat_set<public_key_type>& provided_keys,
bool allow_unused_signatures = false,
flat_set<account_name> provided_accounts = flat_set<account_name>()
)const;
time_point check_authorization( const vector<action>& actions,
const flat_set<public_key_type>& provided_keys,
bool allow_unused_signatures = false,
flat_set<account_name> provided_accounts = flat_set<account_name>()
)const;
private:
......@@ -337,10 +337,10 @@ namespace eosio { namespace chain {
return f();
}
void check_transaction_authorization(const transaction& trx,
const vector<signature_type>& signatures,
const vector<bytes>& cfd = vector<bytes>(),
bool allow_unused_signatures = false)const;
time_point check_transaction_authorization(const transaction& trx,
const vector<signature_type>& signatures,
const vector<bytes>& cfd = vector<bytes>(),
bool allow_unused_signatures = false)const;
void require_scope(const scope_name& name) const;
......@@ -381,6 +381,17 @@ namespace eosio { namespace chain {
scope_name code_account,
action_name type) const;
/**
* @brief Find the linked permission for the passed in parameters
* @param authorizer_account The account authorizing the message
* @param code_account The account which publishes the contract that handles the message
* @param type The type of message
* @return an optional<permission_name> for the linked permission if one exists; otherwise an invalid
* optional<permission_name>
*/
optional<permission_name> lookup_linked_permission( account_name authorizer_account,
scope_name code_account,
action_name type) const;
bool should_check_for_duplicate_transactions()const { return !(_skip_flags&skip_transaction_dupe_check); }
bool should_check_tapos()const { return !(_skip_flags&skip_tapos_check); }
......@@ -413,6 +424,7 @@ namespace eosio { namespace chain {
transaction _get_on_block_transaction();
void _apply_on_block_transaction();
void store_deferred_transaction(const deferred_transaction& dtrx);
// producer_schedule_type calculate_next_round( const signed_block& next_block );
......
......@@ -29,6 +29,9 @@ namespace eosio { namespace chain { namespace contracts {
void apply_eosio_setabi(apply_context&);
void apply_eosio_onerror(apply_context&);
void apply_eosio_canceldelay(apply_context&);
void apply_eosio_mindelay(apply_context&);
///@} end action handlers
} } } /// namespace eosio::contracts
......@@ -150,6 +150,7 @@ struct updateauth {
permission_name permission;
permission_name parent;
authority data;
uint32 delay;
static account_name get_account() {
return config::system_account_name;
......@@ -268,6 +269,29 @@ struct vetorecovery {
}
};
struct canceldelay {
uint32 sender_id;
static account_name get_account() {
return config::system_account_name;
}
static action_name get_name() {
return N(canceldelay);
}
};
struct mindelay {
uint32 delay;
static account_name get_account() {
return config::system_account_name;
}
static action_name get_name() {
return N(mindelay);
}
};
} } } /// namespace eosio::chain::contracts
......@@ -281,10 +305,12 @@ FC_REFLECT( eosio::chain::contracts::abi_def , (types)(
FC_REFLECT( eosio::chain::contracts::newaccount , (creator)(name)(owner)(active)(recovery) )
FC_REFLECT( eosio::chain::contracts::setcode , (account)(vmtype)(vmversion)(code) ) //abi
FC_REFLECT( eosio::chain::contracts::setabi , (account)(abi) )
FC_REFLECT( eosio::chain::contracts::updateauth , (account)(permission)(parent)(data) )
FC_REFLECT( eosio::chain::contracts::updateauth , (account)(permission)(parent)(data)(delay) )
FC_REFLECT( eosio::chain::contracts::deleteauth , (account)(permission) )
FC_REFLECT( eosio::chain::contracts::linkauth , (account)(code)(type)(requirement) )
FC_REFLECT( eosio::chain::contracts::unlinkauth , (account)(code)(type) )
FC_REFLECT( eosio::chain::contracts::postrecovery , (account)(data)(memo) )
FC_REFLECT( eosio::chain::contracts::passrecovery , (account) )
FC_REFLECT( eosio::chain::contracts::vetorecovery , (account) )
FC_REFLECT( eosio::chain::contracts::canceldelay , (sender_id) )
FC_REFLECT( eosio::chain::contracts::mindelay , (delay) )
......@@ -17,36 +17,45 @@ namespace eosio { namespace chain {
permission_name name; ///< human-readable name for the permission
shared_authority auth; ///< authority required to execute this permission
time_point last_updated; ///< the last time this authority was updated
time_point delay; ///< delay associated with this permission
/**
* @brief Checks if this permission is equivalent or greater than other
* @tparam Index The permission_index
* @return True if this permission matches, or is a parent of, other; false otherwise
* @return a time_point set to the maximum delay encountered between this and the permission that is other;
* empty optional otherwise
*
* Permissions are organized hierarchically such that a parent permission is strictly more powerful than its
* children/grandchildren. This method checks whether this permission is of greater or equal power (capable of
* satisfying) permission @ref other. This would be the case if this permission matches, or is some parent of,
* other.
* satisfying) permission @ref other. The returned value is an optional<time_point> that will indicate the
* maximum delay encountered walking the hierarchy between this permission and other, if other satisfies this,
* otherwise an empty optional is returned.
*/
template <typename Index>
bool satisfies(const permission_object& other, const Index& permission_index) const {
optional<time_point> satisfies(const permission_object& other, const Index& permission_index) const {
// If the owners are not the same, this permission cannot satisfy other
if (owner != other.owner)
return false;
return optional<time_point>();
// if other satisfies this permission, then other's delay and this delay will have to contribute
auto max_delay = other.delay > delay ? other.delay : delay;
// If this permission matches other, or is the immediate parent of other, then this permission satisfies other
if (id == other.id || id == other.parent)
return true;
return optional<time_point>(max_delay);
// Walk up other's parent tree, seeing if we find this permission. If so, this permission satisfies other
const permission_object* parent = &*permission_index.template get<by_id>().find(other.parent);
while (parent) {
if (max_delay < parent->delay)
max_delay = parent->delay;
if (id == parent->parent)
return true;
return optional<time_point>(max_delay);
if (parent->parent._id == 0)
return false;
return optional<time_point>();
parent = &*permission_index.template get<by_id>().find(parent->parent);
}
// This permission is not a parent of other, and so does not satisfy other
return false;
return optional<time_point>();
}
};
......@@ -108,7 +117,7 @@ CHAINBASE_SET_INDEX_TYPE(eosio::chain::permission_object, eosio::chain::permissi
CHAINBASE_SET_INDEX_TYPE(eosio::chain::permission_usage_object, eosio::chain::permission_usage_index)
FC_REFLECT(chainbase::oid<eosio::chain::permission_object>, (_id))
FC_REFLECT(eosio::chain::permission_object, (id)(owner)(parent)(name)(auth))
FC_REFLECT(eosio::chain::permission_object, (id)(owner)(parent)(name)(auth)(last_updated)(delay))
FC_REFLECT(chainbase::oid<eosio::chain::permission_usage_object>, (_id))
FC_REFLECT(eosio::chain::permission_usage_object, (id)(account)(permission)(last_used))
......@@ -82,7 +82,8 @@ namespace eosio { namespace chain {
enum status_enum {
executed = 0, ///< succeed, no error handler executed
soft_fail = 1, ///< objectively failed (not executed), error handler executed
hard_fail = 2 ///< objectively failed and error handler objectively failed thus no state change
hard_fail = 2, ///< objectively failed and error handler objectively failed thus no state change
delayed = 3 ///< transaction delayed
};
transaction_receipt() : status(hard_fail) {}
......@@ -216,6 +217,15 @@ namespace eosio { namespace chain {
uint64_t sender_id; /// ID assigned by sender of generated, accessible via WASM api when executing normal or error
account_name sender; /// receives error handler callback
time_point_sec execute_after; /// delayed exeuction
deferred_transaction() = default;
deferred_transaction(uint32_t sender_id, account_name sender, time_point_sec execute_after, const transaction& txn)
: transaction(txn),
sender_id(sender_id),
sender(sender),
execute_after(execute_after)
{}
};
struct deferred_reference {
......@@ -270,7 +280,7 @@ FC_REFLECT_ENUM( eosio::chain::data_access_info::access_type, (read)(write))
FC_REFLECT( eosio::chain::data_access_info, (type)(code)(scope)(sequence))
FC_REFLECT( eosio::chain::action_trace, (receiver)(act)(console)(region_id)(cycle_index)(data_access) )
FC_REFLECT( eosio::chain::transaction_receipt, (status)(id))
FC_REFLECT_ENUM( eosio::chain::transaction_receipt::status_enum, (executed)(soft_fail)(hard_fail))
FC_REFLECT_ENUM( eosio::chain::transaction_receipt::status_enum, (executed)(soft_fail)(hard_fail)(delayed) )
FC_REFLECT_DERIVED( eosio::chain::transaction_trace, (eosio::chain::transaction_receipt), (action_traces)(deferred_transaction_requests) )
......
......@@ -62,6 +62,7 @@ namespace fc {
time_point& operator += ( const microseconds& m) { elapsed+=m; return *this; }
time_point& operator -= ( const microseconds& m) { elapsed-=m; return *this; }
time_point operator + (const microseconds& m) const { return time_point(elapsed+m); }
time_point operator + (const time_point& m) const { return time_point(elapsed+m.elapsed); }
time_point operator - (const microseconds& m) const { return time_point(elapsed-m); }
microseconds operator - (const time_point& m) const { return microseconds(elapsed.count() - m.elapsed.count()); }
private:
......@@ -102,8 +103,10 @@ namespace fc {
friend bool operator != ( const time_point_sec& a, const time_point_sec& b ) { return a.utc_seconds != b.utc_seconds; }
time_point_sec& operator += ( uint32_t m ) { utc_seconds+=m; return *this; }
time_point_sec& operator += ( microseconds m ) { utc_seconds+=m.to_seconds(); return *this; }
time_point_sec& operator += ( time_point_sec m ) { utc_seconds+=m.utc_seconds; return *this; }
time_point_sec& operator -= ( uint32_t m ) { utc_seconds-=m; return *this; }
time_point_sec& operator -= ( microseconds m ) { utc_seconds-=m.to_seconds(); return *this; }
time_point_sec& operator -= ( time_point_sec m ) { utc_seconds-=m.utc_seconds; return *this; }
time_point_sec operator +( uint32_t offset )const { return time_point_sec(utc_seconds + offset); }
time_point_sec operator -( uint32_t offset )const { return time_point_sec(utc_seconds - offset); }
......
......@@ -64,6 +64,8 @@ namespace eosio { namespace testing {
public:
typedef string action_result;
static const uint32_t DEFAULT_EXPIRATION_DELTA = 6;
base_tester(chain_controller::runtime_limits limits = chain_controller::runtime_limits());
explicit base_tester(chain_controller::controller_config config);
......@@ -78,10 +80,10 @@ namespace eosio { namespace testing {
transaction_trace push_transaction( signed_transaction& trx, uint32_t skip_flag = skip_nothing );
action_result push_action(action&& cert_act, uint64_t authorizer);
transaction_trace push_action( const account_name& code, const action_name& act, const account_name& signer, const variant_object &data );
transaction_trace push_action( const account_name& code, const action_name& acttype, const account_name& actor, const variant_object& data, uint32_t expiration = DEFAULT_EXPIRATION_DELTA );
transaction_trace push_action( const account_name& code, const action_name& acttype, const vector<account_name>& actors, const variant_object& data, uint32_t expiration = DEFAULT_EXPIRATION_DELTA );
void set_tapos( signed_transaction& trx ) const;
void set_tapos( signed_transaction& trx, uint32_t expiration = DEFAULT_EXPIRATION_DELTA ) const;
void create_accounts( vector<account_name> names, bool multisig = false ) {
for( auto n : names ) create_account(n, config::system_account_name, multisig );
......@@ -96,7 +98,7 @@ 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 );
void create_account( account_name name, account_name creator = config::system_account_name, bool multisig = false );
void create_account( account_name name, account_name creator = config::system_account_name, bool multisig = false );
transaction_trace push_reqauth( account_name from, const vector<permission_level>& auths, const vector<private_key_type>& keys );
transaction_trace push_reqauth(account_name from, string role, bool multi_sig = false);
......
......@@ -106,8 +106,8 @@ namespace eosio { namespace testing {
}
}
void base_tester::set_tapos( signed_transaction& trx ) const {
trx.expiration = control->head_block_time() + fc::seconds(6);
void base_tester::set_tapos( signed_transaction& trx, uint32_t expiration ) const {
trx.expiration = control->head_block_time() + fc::seconds(expiration);
trx.set_reference_block( control->head_block_id() );
}
......@@ -169,7 +169,18 @@ namespace eosio { namespace testing {
transaction_trace base_tester::push_action( const account_name& code,
const action_name& acttype,
const account_name& actor,
const variant_object& data )
const variant_object& data,
uint32_t expiration)
{ try {
return push_action(code, acttype, vector<account_name>{ actor }, data, expiration);
} FC_CAPTURE_AND_RETHROW( (code)(acttype)(actor)(data)(expiration) ) }
transaction_trace base_tester::push_action( const account_name& code,
const action_name& acttype,
const vector<account_name>& actors,
const variant_object& data,
uint32_t expiration)
{ try {
const auto& acnt = control->get_database().get<account_object,by_name>(code);
......@@ -184,16 +195,20 @@ namespace eosio { namespace testing {
action act;
act.account = code;
act.name = acttype;
act.authorization = vector<permission_level>{{actor, config::active_name}};
for (const auto& actor : actors) {
act.authorization.push_back(permission_level{actor, config::active_name});
}
act.data = abis.variant_to_binary(action_type_name, data);
signed_transaction trx;
trx.actions.emplace_back(std::move(act));
set_tapos(trx);
trx.sign(get_private_key(actor, "active"), chain_id_type());
set_tapos(trx, expiration);
for (const auto& actor : actors) {
trx.sign(get_private_key(actor, "active"), chain_id_type());
}
return push_transaction(trx);
} FC_CAPTURE_AND_RETHROW( (code)(acttype)(actor)(data) ) }
} FC_CAPTURE_AND_RETHROW( (code)(acttype)(actors)(data)(expiration) ) }
transaction_trace base_tester::push_reqauth( account_name from, const vector<permission_level>& auths, const vector<private_key_type>& keys ) {
variant pretty_trx = fc::mutable_variant_object()
......
......@@ -2253,7 +2253,11 @@ namespace eosio {
}
}
break;
}}
}
case transaction_receipt::delayed:
#warning TODO: Not sure what should happen here
break;
}
}
}
}
......
......@@ -225,12 +225,14 @@ BOOST_AUTO_TEST_CASE(order_dependent_transactions)
("account", "tester")
("permission", "first")
("parent", "active")
("data", authority(chain.get_public_key(name("tester"), "first"))));
("data", authority(chain.get_public_key(name("tester"), "first")))
("delay", 0));
chain.push_action(name("eosio"), name("updateauth"), name("tester"), fc::mutable_variant_object()
("account", "tester")
("permission", "second")
("parent", "first")
("data", authority(chain.get_public_key(name("tester"), "second"))));
("data", authority(chain.get_public_key(name("tester"), "second")))
("delay", 0));
// Ensure the related auths are created
const auto* first_auth = chain.find<permission_object, by_owner>(boost::make_tuple(name("tester"), name("first")));
......
此差异已折叠。
......@@ -1937,7 +1937,8 @@ BOOST_AUTO_TEST_CASE(updateauth)
{"key" : "EOS5eVr9TVnqwnUBNwf9kwMTbrHvX5aPyyEG97dz2b2TNeqWRzbJf", "weight" : 57605} ],
"accounts" : [ {"permission" : {"actor" : "prm.acct1", "permission" : "prm.prm1"}, "weight" : 53005 },
{"permission" : {"actor" : "prm.acct2", "permission" : "prm.prm2"}, "weight" : 53405 }]
}
},
"delay" : 0
}
)=====";
......
......@@ -110,6 +110,13 @@ BOOST_AUTO_TEST_CASE(deterministic_distributions)
BOOST_TEST(std::equal(nums.begin(), nums.end(), c.begin()));
} FC_LOG_AND_RETHROW() }
struct permission_visitor {
std::vector<permission_level> permissions;
void operator()(const permission_level& permission) {
permissions.push_back(permission);
}
};
BOOST_AUTO_TEST_CASE(authority_checker)
{ try {
testing::tester test;
......@@ -119,23 +126,24 @@ BOOST_AUTO_TEST_CASE(authority_checker)
auto GetNullAuthority = [](auto){abort(); return authority();};
permission_visitor pv;
auto A = authority(2, {key_weight{a, 1}, key_weight{b, 1}});
{
auto checker = make_auth_checker(GetNullAuthority, 2, {a, b});
auto checker = make_auth_checker(GetNullAuthority, pv, 2, {a, b});
BOOST_TEST(checker.satisfied(A));
BOOST_TEST(checker.all_keys_used());
BOOST_TEST(checker.used_keys().size() == 2);
BOOST_TEST(checker.unused_keys().size() == 0);
}
{
auto checker = make_auth_checker(GetNullAuthority, 2, {a, c});
auto checker = make_auth_checker(GetNullAuthority, pv, 2, {a, c});
BOOST_TEST(!checker.satisfied(A));
BOOST_TEST(!checker.all_keys_used());
BOOST_TEST(checker.used_keys().size() == 0);
BOOST_TEST(checker.unused_keys().size() == 2);
}
{
auto checker = make_auth_checker(GetNullAuthority, 2, {a, b, c});
auto checker = make_auth_checker(GetNullAuthority, pv, 2, {a, b, c});
BOOST_TEST(checker.satisfied(A));
BOOST_TEST(!checker.all_keys_used());
BOOST_TEST(checker.used_keys().size() == 2);
......@@ -145,27 +153,27 @@ BOOST_AUTO_TEST_CASE(authority_checker)
BOOST_TEST(checker.unused_keys().count(c) == 1);
}
{
auto checker = make_auth_checker(GetNullAuthority, 2, {b, c});
auto checker = make_auth_checker(GetNullAuthority, pv, 2, {b, c});
BOOST_TEST(!checker.satisfied(A));
BOOST_TEST(!checker.all_keys_used());
BOOST_TEST(checker.used_keys().size() == 0);
}
A = authority(3, {key_weight{a, 1}, key_weight{b, 1}, key_weight{c, 1}});
BOOST_TEST(make_auth_checker(GetNullAuthority, 2, {c, b, a}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetNullAuthority, 2, {a, b}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetNullAuthority, 2, {a, c}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetNullAuthority, 2, {b, c}).satisfied(A));
BOOST_TEST(make_auth_checker(GetNullAuthority, pv, 2, {c, b, a}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetNullAuthority, pv, 2, {a, b}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetNullAuthority, pv, 2, {a, c}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetNullAuthority, pv, 2, {b, c}).satisfied(A));
A = authority(1, {key_weight{a, 1}, key_weight{b, 1}});
BOOST_TEST(make_auth_checker(GetNullAuthority, 2, {a}).satisfied(A));
BOOST_TEST(make_auth_checker(GetNullAuthority, 2, {b}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetNullAuthority, 2, {c}).satisfied(A));
BOOST_TEST(make_auth_checker(GetNullAuthority, pv, 2, {a}).satisfied(A));
BOOST_TEST(make_auth_checker(GetNullAuthority, pv, 2, {b}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetNullAuthority, pv, 2, {c}).satisfied(A));
A = authority(1, {key_weight{a, 2}, key_weight{b, 1}});
BOOST_TEST(make_auth_checker(GetNullAuthority, 2, {a}).satisfied(A));
BOOST_TEST(make_auth_checker(GetNullAuthority, 2, {b}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetNullAuthority, 2, {c}).satisfied(A));
BOOST_TEST(make_auth_checker(GetNullAuthority, pv, 2, {a}).satisfied(A));
BOOST_TEST(make_auth_checker(GetNullAuthority, pv, 2, {b}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetNullAuthority, pv, 2, {c}).satisfied(A));
auto GetCAuthority = [c](auto){
return authority(1, {key_weight{c, 1}});
......@@ -173,26 +181,26 @@ BOOST_AUTO_TEST_CASE(authority_checker)
A = authority(2, {key_weight{a, 2}, key_weight{b, 1}}, {permission_level_weight{{"hello", "world"}, 1}});
{
auto checker = make_auth_checker(GetCAuthority, 2, {a});
auto checker = make_auth_checker(GetCAuthority, pv, 2, {a});
BOOST_TEST(checker.satisfied(A));
BOOST_TEST(checker.all_keys_used());
}
{
auto checker = make_auth_checker(GetCAuthority, 2, {b});
auto checker = make_auth_checker(GetCAuthority, pv, 2, {b});
BOOST_TEST(!checker.satisfied(A));
BOOST_TEST(checker.used_keys().size() == 0);
BOOST_TEST(checker.unused_keys().size() == 1);
BOOST_TEST(checker.unused_keys().count(b) == 1);
}
{
auto checker = make_auth_checker(GetCAuthority, 2, {c});
auto checker = make_auth_checker(GetCAuthority, pv, 2, {c});
BOOST_TEST(!checker.satisfied(A));
BOOST_TEST(checker.used_keys().size() == 0);
BOOST_TEST(checker.unused_keys().size() == 1);
BOOST_TEST(checker.unused_keys().count(c) == 1);
}
{
auto checker = make_auth_checker(GetCAuthority, 2, {b, c});
auto checker = make_auth_checker(GetCAuthority, pv, 2, {b, c});
BOOST_TEST(checker.satisfied(A));
BOOST_TEST(checker.all_keys_used());
BOOST_TEST(checker.used_keys().size() == 2);
......@@ -201,7 +209,7 @@ BOOST_AUTO_TEST_CASE(authority_checker)
BOOST_TEST(checker.used_keys().count(c) == 1);
}
{
auto checker = make_auth_checker(GetCAuthority, 2, {b, c, a});
auto checker = make_auth_checker(GetCAuthority, pv, 2, {b, c, a});
BOOST_TEST(checker.satisfied(A));
BOOST_TEST(!checker.all_keys_used());
BOOST_TEST(checker.used_keys().size() == 1);
......@@ -212,14 +220,14 @@ BOOST_AUTO_TEST_CASE(authority_checker)
}
A = authority(2, {key_weight{a, 1}, key_weight{b, 1}}, {permission_level_weight{{"hello", "world"}, 1}});
BOOST_TEST(!make_auth_checker(GetCAuthority, 2, {a}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetCAuthority, 2, {b}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetCAuthority, 2, {c}).satisfied(A));
BOOST_TEST(make_auth_checker(GetCAuthority, 2, {a, b}).satisfied(A));
BOOST_TEST(make_auth_checker(GetCAuthority, 2, {b, c}).satisfied(A));
BOOST_TEST(make_auth_checker(GetCAuthority, 2, {a, c}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetCAuthority, pv, 2, {a}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetCAuthority, pv, 2, {b}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetCAuthority, pv, 2, {c}).satisfied(A));
BOOST_TEST(make_auth_checker(GetCAuthority, pv, 2, {a, b}).satisfied(A));
BOOST_TEST(make_auth_checker(GetCAuthority, pv, 2, {b, c}).satisfied(A));
BOOST_TEST(make_auth_checker(GetCAuthority, pv, 2, {a, c}).satisfied(A));
{
auto checker = make_auth_checker(GetCAuthority, 2, {a, b, c});
auto checker = make_auth_checker(GetCAuthority, pv, 2, {a, b, c});
BOOST_TEST(checker.satisfied(A));
BOOST_TEST(!checker.all_keys_used());
BOOST_TEST(checker.used_keys().size() == 2);
......@@ -228,12 +236,12 @@ BOOST_AUTO_TEST_CASE(authority_checker)
}
A = authority(2, {key_weight{a, 1}, key_weight{b, 1}}, {permission_level_weight{{"hello", "world"}, 2}});
BOOST_TEST(make_auth_checker(GetCAuthority, 2, {a, b}).satisfied(A));
BOOST_TEST(make_auth_checker(GetCAuthority, 2, {c}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetCAuthority, 2, {a}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetCAuthority, 2, {b}).satisfied(A));
BOOST_TEST(make_auth_checker(GetCAuthority, pv, 2, {a, b}).satisfied(A));
BOOST_TEST(make_auth_checker(GetCAuthority, pv, 2, {c}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetCAuthority, pv, 2, {a}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetCAuthority, pv, 2, {b}).satisfied(A));
{
auto checker = make_auth_checker(GetCAuthority, 2, {a, b, c});
auto checker = make_auth_checker(GetCAuthority, pv, 2, {a, b, c});
BOOST_TEST(checker.satisfied(A));
BOOST_TEST(!checker.all_keys_used());
BOOST_TEST(checker.used_keys().size() == 1);
......@@ -252,12 +260,12 @@ BOOST_AUTO_TEST_CASE(authority_checker)
A = authority(5, {key_weight{a, 2}, key_weight{b, 2}, key_weight{c, 2}}, {permission_level_weight{{"top", "top"}, 5}});
{
auto checker = make_auth_checker(GetAuthority, 2, {d, e});
auto checker = make_auth_checker(GetAuthority, pv, 2, {d, e});
BOOST_TEST(checker.satisfied(A));
BOOST_TEST(checker.all_keys_used());
}
{
auto checker = make_auth_checker(GetAuthority, 2, {a, b, c, d, e});
auto checker = make_auth_checker(GetAuthority, pv, 2, {a, b, c, d, e});
BOOST_TEST(checker.satisfied(A));
BOOST_TEST(!checker.all_keys_used());
BOOST_TEST(checker.used_keys().size() == 2);
......@@ -266,7 +274,7 @@ BOOST_AUTO_TEST_CASE(authority_checker)
BOOST_TEST(checker.used_keys().count(e) == 1);
}
{
auto checker = make_auth_checker(GetAuthority, 2, {a, b, c, e});
auto checker = make_auth_checker(GetAuthority, pv, 2, {a, b, c, e});
BOOST_TEST(checker.satisfied(A));
BOOST_TEST(!checker.all_keys_used());
BOOST_TEST(checker.used_keys().size() == 3);
......@@ -275,14 +283,14 @@ BOOST_AUTO_TEST_CASE(authority_checker)
BOOST_TEST(checker.used_keys().count(b) == 1);
BOOST_TEST(checker.used_keys().count(c) == 1);
}
BOOST_TEST(make_auth_checker(GetAuthority, 1, {a, b, c}).satisfied(A));
BOOST_TEST(make_auth_checker(GetAuthority, pv, 1, {a, b, c}).satisfied(A));
// Fails due to short recursion depth limit
BOOST_TEST(!make_auth_checker(GetAuthority, 1, {d, e}).satisfied(A));
BOOST_TEST(!make_auth_checker(GetAuthority, pv, 1, {d, e}).satisfied(A));
A = authority(2, {key_weight{c, 1}, key_weight{b, 1}, key_weight{a, 1}});
auto B = authority(1, {key_weight{c, 1}, key_weight{b, 1}});
{
auto checker = make_auth_checker(GetNullAuthority, 2, {a, b, c});
auto checker = make_auth_checker(GetNullAuthority, pv, 2, {a, b, c});
BOOST_TEST(validate(A));
BOOST_TEST(validate(B));
BOOST_TEST(checker.satisfied(A));
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册