未验证 提交 a30932df 编写于 作者: G Greg Lee 提交者: GitHub

Merge pull request #1574 from EOSIO/permission-api

Add permission api
......@@ -141,6 +141,29 @@ class datastream<size_t> {
size_t _size;
};
/**
* Serialize a public_key into a stream
* @brief Serialize a public_key
* @param ds stream to write
* @param pubkey value to serialize
*/
template<typename Stream>
inline datastream<Stream>& operator<<(datastream<Stream>& ds, const public_key pubkey) {
ds.write( (const char*)&pubkey, sizeof(pubkey));
return ds;
}
/**
* Deserialize a public_key from a stream
* @brief Deserialize a public_key
* @param ds stream to read
* @param pubkey destination for deserialized value
*/
template<typename Stream>
inline datastream<Stream>& operator>>(datastream<Stream>& ds, public_key& pubkey) {
ds.read((char*)&pubkey, sizeof(pubkey));
return ds;
}
/**
* Serialize a key256 into a stream
* @brief Serialize a key256
......@@ -639,18 +662,6 @@ bytes pack( const T& value ) {
return result;
}
template<typename Stream>
inline eosio::datastream<Stream>& operator<<(eosio::datastream<Stream>& ds, const public_key pk) {
ds.write((const char*)&pk, sizeof(pk));
return ds;
}
template<typename Stream>
inline eosio::datastream<Stream>& operator>>(eosio::datastream<Stream>& ds, public_key& pk) {
ds.read((char*)&pk, sizeof(pk));
return ds;
}
template<typename Stream>
inline datastream<Stream>& operator<<(datastream<Stream>& ds, const checksum160& cs) {
ds.write((const char*)&cs, sizeof(cs));
......
/**
* @file
* @copyright defined in eos/LICENSE.txt
*/
#pragma once
#include <eosiolib/types.h>
extern "C" {
/**
* Checks if a set of public keys can authorize an account permission
* @brief Checks if a set of public keys can authorize an account permission
* @param account - the account owner of the permission
* @param permission - the permission name to check for authorization
* @param pubkeys - a pointer to an array of public keys (public_key)
* @param pubkeys_len - the lenght of the array of public keys
* @return 1 if the account permission can be authorized, 0 otherwise
*/
int32_t check_authorization( account_name account, permission_name permission, char* pubkeys, uint32_t pubkeys_len );
}
......@@ -16,6 +16,7 @@
#include "test_chain.cpp"
#include "test_transaction.cpp"
#include "test_checktime.cpp"
#include "test_permission.cpp"
extern "C" {
......@@ -148,6 +149,7 @@ extern "C" {
// test checktime
WASM_TEST_HANDLER(test_checktime, checktime_pass);
WASM_TEST_HANDLER(test_checktime, checktime_failure);
/*
// test softfloat
WASM_TEST_HANDLER(test_softfloat, test_f32_add);
......@@ -157,6 +159,9 @@ extern "C" {
WASM_TEST_HANDLER(test_softfloat, test_f32_min);
*/
// test permission
WASM_TEST_HANDLER_EX(test_permission, check_authorization);
//unhandled test call
eosio_assert(false, "Unknown Test");
......
......@@ -277,3 +277,7 @@ struct test_softfloat {
static void test_f32_min();
};
*/
struct test_permission {
static void check_authorization(uint64_t receiver, uint64_t code, uint64_t action);
};
/**
* @file action_test.cpp
* @copyright defined in eos/LICENSE.txt
*/
#include <eosiolib/permission.h>
#include <eosiolib/db.h>
#include <eosiolib/eosio.hpp>
#include <eosiolib/print.hpp>
#include <eosiolib/compiler_builtins.h>
#include <eosiolib/serialize.hpp>
#include <eosiolib/action.hpp>
#include "test_api.hpp"
struct check_auth {
account_name account;
permission_name permission;
std::vector<public_key> pubkeys;
EOSLIB_SERIALIZE( check_auth, (account)(permission)(pubkeys) )
};
void test_permission::check_authorization(uint64_t receiver, uint64_t code, uint64_t action) {
(void)code;
(void)action;
using namespace eosio;
auto self = receiver;
auto params = unpack_action_data<check_auth>();
uint64_t res64 = (uint64_t)::check_authorization( params.account, params.permission,
(char*)params.pubkeys.data(), params.pubkeys.size()*sizeof(public_key) );
auto itr = db_lowerbound_i64(self, self, self, 1);
if(itr == -1) {
db_store_i64(self, self, self, 1, &res64, sizeof(uint64_t));
} else {
db_update_i64(itr, self, &res64, sizeof(uint64_t));
}
}
......@@ -867,6 +867,7 @@ flat_set<public_key_type> chain_controller::get_required_keys(const transaction&
return checker.used_keys();
}
class permission_visitor {
public:
permission_visitor(const chain_controller& controller) : _chain_controller(controller) {}
......@@ -888,6 +889,7 @@ 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),
......@@ -988,6 +990,26 @@ time_point chain_controller::check_authorization( const vector<action>& actions,
return max_delay;
}
bool chain_controller::check_authorization( account_name account, permission_name permission,
flat_set<public_key_type> provided_keys,
bool allow_unused_signatures)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,
provided_keys);
auto satisfied = checker.satisfied({account, permission});
if (satisfied && !allow_unused_signatures) {
EOS_ASSERT(checker.all_keys_used(), tx_irrelevant_sig,
"irrelevant signatures from these keys: ${keys}",
("keys", checker.unused_keys()));
}
return satisfied;
}
time_point chain_controller::check_transaction_authorization(const transaction& trx,
const vector<signature_type>& signatures,
const vector<bytes>& cfd,
......
......@@ -116,7 +116,7 @@ namespace eosio { namespace chain {
* This signal is emitted any time a new transaction is added to the pending
* block state.
*/
signal<void(const transaction_metadata&, const packed_transaction&)> on_pending_transaction;
signal<void(const transaction_metadata&, const packed_transaction&)> on_pending_transaction;
......@@ -299,6 +299,16 @@ namespace eosio { namespace chain {
flat_set<account_name> provided_accounts = flat_set<account_name>()
)const;
/**
* @param account - the account owner of the permission
* @param permission - the permission name to check for authorization
* @param provided_keys - a set of public keys
*
* @return true if the provided keys are sufficient to authorize the account permission
*/
bool check_authorization( account_name account, permission_name permission,
flat_set<public_key_type> provided_keys,
bool allow_unused_signatures)const;
private:
const apply_handler* find_apply_handler( account_name contract, scope_name scope, action_name act )const;
......
......@@ -747,6 +747,28 @@ class crypto_api : public context_aware_api {
}
};
class permission_api : public context_aware_api {
public:
using context_aware_api::context_aware_api;
bool check_authorization( account_name account, permission_name permission, array_ptr<char> packed_pubkeys, size_t datalen) {
vector<public_key_type> pub_keys;
datastream<const char*> ds( packed_pubkeys, datalen );
while(ds.remaining()) {
public_key_type pub;
fc::raw::unpack(ds, pub);
pub_keys.emplace_back(pub);
}
return context.controller.check_authorization(
account, permission,
{pub_keys.begin(), pub_keys.end()},
false
);
}
};
class string_api : public context_aware_api {
public:
using context_aware_api::context_aware_api;
......@@ -1506,6 +1528,10 @@ REGISTER_INTRINSICS(crypto_api,
(ripemd160, void(int, int, int) )
);
REGISTER_INTRINSICS(permission_api,
(check_authorization, int(int64_t, int64_t, int, int))
);
REGISTER_INTRINSICS(string_api,
(assert_is_utf8, void(int, int, int) )
);
......
......@@ -81,7 +81,13 @@ struct test_chain_action {
FC_REFLECT_TEMPLATE((uint64_t T), test_chain_action<T>, BOOST_PP_SEQ_NIL);
struct check_auth {
account_name account;
permission_name permission;
vector<public_key_type> pubkeys;
};
FC_REFLECT(check_auth, (account)(permission)(pubkeys) );
bool expect_assert_message(const fc::exception& ex, string expected) {
BOOST_TEST_MESSAGE("LOG : " << "expected: " << expected << ", actual: " << ex.get_log().at(0).get_message());
......@@ -1227,6 +1233,105 @@ BOOST_FIXTURE_TEST_CASE(types_tests, tester) { try {
CALL_TEST_FUNCTION( *this, "test_types", "name_class", {});
} FC_LOG_AND_RETHROW() }
/*************************************************************************************
* permission_tests test case
*************************************************************************************/
BOOST_FIXTURE_TEST_CASE(permission_tests, tester) { try {
produce_blocks(1);
create_account( N(testapi) );
produce_blocks(1);
set_code( N(testapi), test_api_wast );
produce_blocks(1);
auto get_result_uint64 = [&]() -> uint64_t {
const auto& db = control->get_database();
const auto* t_id = db.find<table_id_object, by_code_scope_table>(boost::make_tuple(N(testapi), N(testapi), N(testapi)));
FC_ASSERT(t_id != 0, "Table id not found");
const auto& idx = db.get_index<key_value_index, by_scope_primary>();
auto itr = idx.lower_bound(boost::make_tuple(t_id->id));
FC_ASSERT( itr != idx.end() && itr->t_id == t_id->id, "lower_bound failed");
FC_ASSERT( 0 != itr->value.size(), "unexpected result size");
return *reinterpret_cast<const uint64_t *>(itr->value.data());
};
CALL_TEST_FUNCTION( *this, "test_permission", "check_authorization",
fc::raw::pack( check_auth {
.account = N(testapi),
.permission = N(active),
.pubkeys = {
get_public_key(N(testapi), "active")
}
})
);
BOOST_CHECK_EQUAL( uint64_t(1), get_result_uint64() );
CALL_TEST_FUNCTION( *this, "test_permission", "check_authorization",
fc::raw::pack( check_auth {
.account = N(testapi),
.permission = N(active),
.pubkeys = {
public_key_type(string("EOS7GfRtyDWWgxV88a5TRaYY59XmHptyfjsFmHHfioGNJtPjpSmGX"))
}
})
);
BOOST_CHECK_EQUAL( uint64_t(0), get_result_uint64() );
BOOST_CHECK_EXCEPTION(CALL_TEST_FUNCTION( *this, "test_permission", "check_authorization",
fc::raw::pack( check_auth {
.account = N(testapi),
.permission = N(active),
.pubkeys = {
get_public_key(N(testapi), "active"),
public_key_type(string("EOS7GfRtyDWWgxV88a5TRaYY59XmHptyfjsFmHHfioGNJtPjpSmGX"))
}
})), tx_irrelevant_sig,
[](const tx_irrelevant_sig& e) {
return expect_assert_message(e, "irrelevant signatures from these keys: [\"EOS7GfRtyDWWgxV88a5TRaYY59XmHptyfjsFmHHfioGNJtPjpSmGX\"]");
}
);
BOOST_CHECK_EXCEPTION(CALL_TEST_FUNCTION( *this, "test_permission", "check_authorization",
fc::raw::pack( check_auth {
.account = N(noname),
.permission = N(active),
.pubkeys = {
get_public_key(N(testapi), "active")
}
})), fc::exception,
[](const fc::exception& e) {
return expect_assert_message(e, "unknown key");
}
);
CALL_TEST_FUNCTION( *this, "test_permission", "check_authorization",
fc::raw::pack( check_auth {
.account = N(testapi),
.permission = N(active),
.pubkeys = {}
})
);
BOOST_CHECK_EQUAL( uint64_t(0), get_result_uint64() );
BOOST_CHECK_EXCEPTION(CALL_TEST_FUNCTION( *this, "test_permission", "check_authorization",
fc::raw::pack( check_auth {
.account = N(testapi),
.permission = N(noname),
.pubkeys = {
get_public_key(N(testapi), "active")
}
})), fc::exception,
[](const fc::exception& e) {
return expect_assert_message(e, "unknown key");
}
);
} FC_LOG_AND_RETHROW() }
#if 0
/*************************************************************************************
* privileged_tests test case
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册