This repository has been archived by the owner on Aug 2, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #151 from EOSIO/permission-links-123
Progress on #7, upgrades to testing framework
- Loading branch information
Showing
11 changed files
with
376 additions
and
88 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
127 changes: 127 additions & 0 deletions
127
libraries/chain/include/eos/chain/authority_checker.hpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
#pragma once | ||
|
||
#include <eos/chain/types.hpp> | ||
#include <eos/types/generated.hpp> | ||
|
||
#include <eos/utilities/parallel_markers.hpp> | ||
|
||
#include <fc/scoped_exit.hpp> | ||
|
||
#include <boost/range/algorithm/find.hpp> | ||
#include <boost/algorithm/cxx11/all_of.hpp> | ||
|
||
namespace eos { namespace chain { | ||
|
||
namespace detail { | ||
using MetaPermission = static_variant<types::KeyPermissionWeight, types::AccountPermissionWeight>; | ||
|
||
struct GetWeightVisitor { | ||
using result_type = UInt32; | ||
|
||
template<typename Permission> | ||
UInt32 operator()(const Permission& permission) { return permission.weight; } | ||
}; | ||
|
||
// Orders permissions descending by weight, and breaks ties with Key permissions being less than Account permissions | ||
struct MetaPermissionComparator { | ||
bool operator()(const MetaPermission& a, const MetaPermission& b) const { | ||
GetWeightVisitor scale; | ||
if (a.visit(scale) > b.visit(scale)) return true; | ||
return a.contains<types::KeyPermissionWeight>() && !b.contains<types::KeyPermissionWeight>(); | ||
} | ||
}; | ||
|
||
using MetaPermissionSet = boost::container::flat_multiset<MetaPermission, MetaPermissionComparator>; | ||
} | ||
|
||
/** | ||
* @brief This class determines whether a set of signing keys are sufficient to satisfy an authority or not | ||
* | ||
* To determine whether an authority is satisfied or not, we first determine which keys have approved of a message, and | ||
* then determine whether that list of keys is sufficient to satisfy the authority. This class takes a list of keys and | ||
* provides the @ref satisfied method to determine whether that list of keys satisfies a provided authority. | ||
* | ||
* @tparam F A callable which takes a single argument of type @ref AccountPermission and returns the corresponding | ||
* authority | ||
*/ | ||
template<typename F> | ||
class AuthorityChecker { | ||
F PermissionToAuthority; | ||
vector<public_key_type> signingKeys; | ||
vector<bool> usedKeys; | ||
|
||
struct WeightTallyVisitor { | ||
using result_type = UInt32; | ||
|
||
AuthorityChecker& checker; | ||
UInt32 totalWeight = 0; | ||
|
||
WeightTallyVisitor(AuthorityChecker& checker) | ||
: checker(checker) {} | ||
|
||
UInt32 operator()(const types::KeyPermissionWeight& permission) { | ||
auto itr = boost::find(checker.signingKeys, permission.key); | ||
if (itr != checker.signingKeys.end()) { | ||
checker.usedKeys[itr - checker.signingKeys.begin()] = true; | ||
totalWeight += permission.weight; | ||
} | ||
return totalWeight; | ||
} | ||
UInt32 operator()(const types::AccountPermissionWeight& permission) { | ||
//TODO: Recursion limit? Yes: implement as producer-configurable parameter | ||
if (checker.satisfied(permission.permission)) | ||
totalWeight += permission.weight; | ||
return totalWeight; | ||
} | ||
}; | ||
|
||
public: | ||
AuthorityChecker(F PermissionToAuthority, const flat_set<public_key_type>& signingKeys) | ||
: PermissionToAuthority(PermissionToAuthority), | ||
signingKeys(signingKeys.begin(), signingKeys.end()), | ||
usedKeys(signingKeys.size(), false) | ||
{} | ||
|
||
bool satisfied(const types::AccountPermission& permission) { | ||
return satisfied(PermissionToAuthority(permission)); | ||
} | ||
template<typename AuthorityType> | ||
bool satisfied(const AuthorityType& authority) { | ||
// Save the current used keys; if we do not satisfy this authority, the newly used keys aren't actually used | ||
auto KeyReverter = fc::make_scoped_exit([this, keys = usedKeys] () mutable { | ||
usedKeys = keys; | ||
}); | ||
|
||
// Sort key permissions and account permissions together into a single set of MetaPermissions | ||
detail::MetaPermissionSet permissions; | ||
permissions.insert(authority.keys.begin(), authority.keys.end()); | ||
permissions.insert(authority.accounts.begin(), authority.accounts.end()); | ||
|
||
// Check all permissions, from highest weight to lowest, seeing if signingKeys satisfies them or not | ||
WeightTallyVisitor visitor(*this); | ||
for (const auto& permission : permissions) | ||
// If we've got enough weight, to satisfy the authority, return! | ||
if (permission.visit(visitor) >= authority.threshold) { | ||
KeyReverter.cancel(); | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
bool all_keys_used() const { return boost::algorithm::all_of_equal(usedKeys, true); } | ||
flat_set<public_key_type> used_keys() const { | ||
auto range = utilities::FilterDataByMarker(signingKeys, usedKeys, true); | ||
return {range.begin(), range.end()}; | ||
} | ||
flat_set<public_key_type> unused_keys() const { | ||
auto range = utilities::FilterDataByMarker(signingKeys, usedKeys, false); | ||
return {range.begin(), range.end()}; | ||
} | ||
}; | ||
|
||
template<typename F> | ||
AuthorityChecker<F> MakeAuthorityChecker(F&& pta, const flat_set<public_key_type>& signingKeys) { | ||
return AuthorityChecker<F>(std::forward<F>(pta), signingKeys); | ||
} | ||
|
||
}} // namespace eos::chain |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
39 changes: 39 additions & 0 deletions
39
libraries/utilities/include/eos/utilities/parallel_markers.hpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
#pragma once | ||
|
||
#include <boost/range/combine.hpp> | ||
#include <boost/range/adaptor/filtered.hpp> | ||
#include <boost/range/adaptor/transformed.hpp> | ||
|
||
namespace eos { namespace utilities { | ||
|
||
/** | ||
* @brief Return values in DataRange corresponding to matching Markers | ||
* | ||
* Takes two parallel ranges, a Data range containing data values, and a Marker range containing markers on the | ||
* corresponding data values. Returns a new Data range containing only the values corresponding to markers which match | ||
* markerValue | ||
* | ||
* For example: | ||
* @code{.cpp} | ||
* vector<char> data = {'A', 'B', 'C'}; | ||
* vector<bool> markers = {true, false, true}; | ||
* auto markedData = FilterDataByMarker(data, markers, true); | ||
* // markedData contains {'A', 'C'} | ||
* @endcode | ||
*/ | ||
template<typename DataRange, typename MarkerRange, typename Marker> | ||
DataRange FilterDataByMarker(DataRange data, MarkerRange markers, const Marker& markerValue) { | ||
auto RemoveMismatchedMarkers = boost::adaptors::filtered([&markerValue](const auto& tuple) { | ||
return boost::get<0>(tuple) == markerValue; | ||
}); | ||
auto ToData = boost::adaptors::transformed([](const auto& tuple) { | ||
return boost::get<1>(tuple); | ||
}); | ||
|
||
// Zip the ranges together, filter out data with markers that don't match, and return the data without the markers | ||
auto range = boost::combine(markers, data) | RemoveMismatchedMarkers | ToData; | ||
return {range.begin(), range.end()}; | ||
} | ||
|
||
}} // namespace eos::utilities | ||
|
Oops, something went wrong.