[TOC]
先上代码
func ask(ctx context.Context, api *eos.API, privKey string) {
act := &eos.Action{
Account: eos.AccountName("b3"), //部署者
Name: eos.ActionName("updateipfs"), //调用函数名
Authorization: []eos.PermissionLevel{ //设置权限
{
Actor: eos.AccountName("b3"), //调用人
Permission: eos.PermissionName("ipfs"), //active权限
},
},
ActionData: eos.NewActionData(QuestParam{ //设置参数(顺序按照dapp的顺序)
Acc: eos.AccountName("b3"), //参数1
Version: "0.0.1",
Os: "win10",
Arch: "x86",
Hash: "hash",
}),
}
api.Signer.ImportPrivateKey(ctx, privKey) //签名器导入私钥
_, err := api.SignPushActions(ctx, act) //签名并推送action
if err != nil {
fmt.Println("ask err:", err)
}
}此处: Account: eos.AccountName("b3"); 把部署者写成了b3,实际是b;
导致提交的是ipfs权限,但是找到的却是默认的权限 active;报错!
Internal Service Error: Irrelevant authority included: action declares irrelevant authority '{"actor":"b3","permission":"ipfs"}'; minimum authority is {"actor":"b3","permission":"active"}
看一下eos验证权限的源码顺序:
- push_transaction函数调用: check_authorization检查权限
void
authorization_manager::check_authorization( const vector<action>& actions,
const flat_set<public_key_type>& provided_keys,
const flat_set<permission_level>& provided_permissions,
fc::microseconds provided_delay,
const std::function<void()>& _checktime,
bool allow_unused_keys,
const flat_set<permission_level>& satisfied_authorizations
)const
{
const auto& checktime = ( static_cast<bool>(_checktime) ? _checktime : _noop_checktime );
auto delay_max_limit = fc::seconds( _control.get_global_properties().configuration.max_transaction_delay );
auto effective_provided_delay = (provided_delay >= delay_max_limit) ? fc::microseconds::maximum() : provided_delay;
auto checker = make_auth_checker( [&](const permission_level& p){ return get_permission(p).auth; },
_control.get_global_properties().configuration.max_authority_depth,
provided_keys,
provided_permissions,
effective_provided_delay,
checktime
);
map<permission_level, fc::microseconds> permissions_to_satisfy;
for( const auto& act : actions ) {
bool special_case = false;
fc::microseconds delay = effective_provided_delay;
if( act.account == config::system_account_name ) {
special_case = true;
if( act.name == updateauth::get_name() ) {
check_updateauth_authorization( act.data_as<updateauth>(), act.authorization );
} else if( act.name == deleteauth::get_name() ) {
check_deleteauth_authorization( act.data_as<deleteauth>(), act.authorization );
} else if( act.name == linkauth::get_name() ) {
check_linkauth_authorization( act.data_as<linkauth>(), act.authorization );
} else if( act.name == unlinkauth::get_name() ) {
check_unlinkauth_authorization( act.data_as<unlinkauth>(), act.authorization );
} else if( act.name == canceldelay::get_name() ) {
delay = std::max( delay, check_canceldelay_authorization(act.data_as<canceldelay>(), act.authorization) );
} else {
special_case = false;
}
}
for( const auto& declared_auth : act.authorization ) {
checktime();
if( !special_case ) {
auto min_permission_name = lookup_minimum_permission(declared_auth.actor, act.account, act.name);
if( min_permission_name ) { // since special cases were already handled, it should only be false if the permission is eosio.any
const auto& min_permission = get_permission({declared_auth.actor, *min_permission_name});
EOS_ASSERT( get_permission(declared_auth).satisfies( min_permission,
_db.get_index<permission_index>().indices() ),
irrelevant_auth_exception,
"action declares irrelevant authority '${auth}'; minimum authority is ${min}",
("auth", declared_auth)("min", permission_level{min_permission.owner, min_permission.name}) );
}
}
if( satisfied_authorizations.find( declared_auth ) == satisfied_authorizations.end() ) {
auto res = permissions_to_satisfy.emplace( declared_auth, delay );
if( !res.second && res.first->second > delay) { // if the declared_auth was already in the map and with a higher delay
res.first->second = delay;
}
}
}
}
// Now verify that all the declared authorizations are satisfied:
// Although this can be made parallel (especially for input transactions) with the optimistic assumption that the
// CPU limit is not reached, because of the CPU limit the protocol must officially specify a sequential algorithm
// for checking the set of declared authorizations.
// The permission_levels are traversed in ascending order, which is:
// ascending order of the actor name with ties broken by ascending order of the permission name.
for( const auto& p : permissions_to_satisfy ) {
checktime(); // TODO: this should eventually move into authority_checker instead
EOS_ASSERT( checker.satisfied( p.first, p.second ), unsatisfied_authorization,
"transaction declares authority '${auth}', "
"but does not have signatures for it under a provided delay of ${provided_delay} ms, "
"provided permissions ${provided_permissions}, provided keys ${provided_keys}, "
"and a delay max limit of ${delay_max_limit_ms} ms",
("auth", p.first)
("provided_delay", provided_delay.count()/1000)
("provided_permissions", provided_permissions)
("provided_keys", provided_keys)
("delay_max_limit_ms", delay_max_limit.count()/1000)
);
}
if( !allow_unused_keys ) {
EOS_ASSERT( checker.all_keys_used(), tx_irrelevant_sig,
"transaction bears irrelevant signatures from these keys: ${keys}",
("keys", checker.unused_keys()) );
}
}- check_authorization函数调用: lookup_minimum_permission拿到最小权限
//如果scope是系统账号,不允许link到最小账号,报错;
//然后调用: lookup_linked_permission拿到预先链接在action上的权限;如果找不到,默认使用 active 权限(这里正是报错的原因);
optional<permission_name> authorization_manager::lookup_minimum_permission( account_name authorizer_account,
account_name scope,
action_name act_name
)const
{
// Special case native actions cannot be linked to a minimum permission, so there is no need to check.
if( scope == config::system_account_name ) {
EOS_ASSERT( act_name != updateauth::get_name() &&
act_name != deleteauth::get_name() &&
act_name != linkauth::get_name() &&
act_name != unlinkauth::get_name() &&
act_name != canceldelay::get_name(),
unlinkable_min_permission_action,
"cannot call lookup_minimum_permission on native actions that are not allowed to be linked to minimum permissions" );
}
try {
optional<permission_name> linked_permission = lookup_linked_permission(authorizer_account, scope, act_name);
if( !linked_permission )
return config::active_name;
if( *linked_permission == config::eosio_any_name )
return optional<permission_name>();
return linked_permission;
} FC_CAPTURE_AND_RETHROW((authorizer_account)(scope)(act_name))
}- 根据验证者账号,scope和部署者账号找到link在action上的权限
optional<permission_name> authorization_manager::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);
auto link = _db.find<permission_link_object, by_action_name>(key);
// If no specific link found, check for a contract-wide default
if (link == nullptr) {
boost::get<2>(key) = {};
link = _db.find<permission_link_object, by_action_name>(key);
}
// If no specific or default link found, use active permission
if (link != nullptr) {
return link->required_permission;
}
return optional<permission_name>();
// return optional<permission_name>();
} FC_CAPTURE_AND_RETHROW((authorizer_account)(scope)(act_name))
}