Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

Commit

Permalink
Merge pull request #156 from EOSIO/auth-recursion-limit-66
Browse files Browse the repository at this point in the history
Resolve #66, more auth tests

Failing CI check due to #154, not this commit
  • Loading branch information
nathanielhourt authored Aug 10, 2017
2 parents 73434fe + 543a687 commit 4bc6267
Show file tree
Hide file tree
Showing 8 changed files with 139 additions and 103 deletions.
4 changes: 3 additions & 1 deletion libraries/chain/chain_controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -485,15 +485,17 @@ void chain_controller::check_transaction_authorization(const SignedTransaction&
return;
}


auto getPermission = [&db=_db](const types::AccountPermission& permission) {
auto key = boost::make_tuple(permission.account, permission.permission);
return db.get<permission_object, by_owner>(key);
};
auto getAuthority = [&getPermission](const types::AccountPermission& permission) {
return getPermission(permission).auth;
};
auto depthLimit = get_global_properties().configuration.authDepthLimit;
#warning TODO: Use a real chain_id here (where is this stored? Do we still need it?)
auto checker = MakeAuthorityChecker(std::move(getAuthority), trx.get_signature_keys(chain_id_type{}));
auto checker = MakeAuthorityChecker(std::move(getAuthority), depthLimit, trx.get_signature_keys(chain_id_type{}));

for (const auto& message : trx.messages)
for (const auto& declaredAuthority : message.authorization) {
Expand Down
30 changes: 19 additions & 11 deletions libraries/chain/include/eos/chain/authority_checker.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,19 @@ using MetaPermissionSet = boost::container::flat_multiset<MetaPermission, MetaPe
template<typename F>
class AuthorityChecker {
F PermissionToAuthority;
UInt16 recursionDepthLimit;
vector<public_key_type> signingKeys;
vector<bool> usedKeys;

struct WeightTallyVisitor {
using result_type = UInt32;

AuthorityChecker& checker;
UInt16 recursionDepth;
UInt32 totalWeight = 0;

WeightTallyVisitor(AuthorityChecker& checker)
: checker(checker) {}
WeightTallyVisitor(AuthorityChecker& checker, UInt16 recursionDepth)
: checker(checker), recursionDepth(recursionDepth) {}

UInt32 operator()(const types::KeyPermissionWeight& permission) {
auto itr = boost::find(checker.signingKeys, permission.key);
Expand All @@ -68,25 +70,30 @@ class AuthorityChecker {
return totalWeight;
}
UInt32 operator()(const types::AccountPermissionWeight& permission) {
//TODO: Recursion limit? Yes: implement as producer-configurable parameter
if (checker.satisfied(permission.permission))
if (recursionDepth < checker.recursionDepthLimit
&& checker.satisfied(permission.permission, recursionDepth + 1))
totalWeight += permission.weight;
return totalWeight;
}
};

public:
AuthorityChecker(F PermissionToAuthority, const flat_set<public_key_type>& signingKeys)
AuthorityChecker(F PermissionToAuthority, UInt16 recursionDepthLimit, const flat_set<public_key_type>& signingKeys)
: PermissionToAuthority(PermissionToAuthority),
recursionDepthLimit(recursionDepthLimit),
signingKeys(signingKeys.begin(), signingKeys.end()),
usedKeys(signingKeys.size(), false)
{}

bool satisfied(const types::AccountPermission& permission) {
return satisfied(PermissionToAuthority(permission));
bool satisfied(const types::AccountPermission& permission, UInt16 depth = 0) {
return satisfied(PermissionToAuthority(permission), depth);
}
template<typename AuthorityType>
bool satisfied(const AuthorityType& authority) {
bool satisfied(const AuthorityType& authority, UInt16 depth = 0) {
// This check is redundant, since WeightTallyVisitor did it too, but I'll leave it here for future-proofing
if (depth > recursionDepthLimit)
return false;

// 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;
Expand All @@ -98,7 +105,7 @@ class AuthorityChecker {
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);
WeightTallyVisitor visitor(*this, depth);
for (const auto& permission : permissions)
// If we've got enough weight, to satisfy the authority, return!
if (permission.visit(visitor) >= authority.threshold) {
Expand All @@ -120,8 +127,9 @@ class AuthorityChecker {
};

template<typename F>
AuthorityChecker<F> MakeAuthorityChecker(F&& pta, const flat_set<public_key_type>& signingKeys) {
return AuthorityChecker<F>(std::forward<F>(pta), signingKeys);
AuthorityChecker<F> MakeAuthorityChecker(F&& pta, UInt16 recursionDepthLimit,
const flat_set<public_key_type>& signingKeys) {
return AuthorityChecker<F>(std::forward<F>(pta), recursionDepthLimit, signingKeys);
}

}} // namespace eos::chain
2 changes: 2 additions & 0 deletions libraries/chain/include/eos/chain/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <eos/types/types.hpp>

namespace eos { namespace config {
using types::UInt16;
using types::UInt32;
using types::UInt64;
using types::UInt128;
Expand Down Expand Up @@ -59,6 +60,7 @@ const static ShareType DefaultElectedPay = Asset(100).amount;
const static ShareType DefaultRunnerUpPay = Asset(75).amount;
const static ShareType DefaultMinEosBalance = Asset(100).amount;
const static UInt32 DefaultMaxTrxLifetime = 60*60;
const static UInt16 DefaultAuthDepthLimit = 6;
const static UInt32 ProducersAuthorityThreshold = 14;

const static int BlocksPerRound = 21;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ struct genesis_state_type {
config::DefaultElectedPay,
config::DefaultRunnerUpPay,
config::DefaultMinEosBalance,
config::DefaultMaxTrxLifetime
config::DefaultMaxTrxLifetime,
config::DefaultAuthDepthLimit
};
vector<initial_account_type> initial_accounts;
vector<initial_producer_type> initial_producers;
Expand Down
1 change: 1 addition & 0 deletions libraries/types/types.eos
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ struct BlockchainConfiguration
runnerUpPay ShareType
minEosBalance ShareType
maxTrxLifetime UInt32
authDepthLimit UInt16

struct TypeDef
newTypeName TypeName
Expand Down
3 changes: 2 additions & 1 deletion tests/common/database_fixture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,8 @@ void testing_blockchain::sign_transaction(SignedTransaction& trx) {
auto key = boost::make_tuple(permission.account, permission.permission);
return db.get<permission_object, by_owner>(key).auth;
};
auto checker = MakeAuthorityChecker(GetAuthority, fixture.available_keys());
auto checker = MakeAuthorityChecker(GetAuthority, get_global_properties().configuration.authDepthLimit,
fixture.available_keys());

for (const auto& message : trx.messages)
for (const auto& authorization : message.authorization)
Expand Down
107 changes: 64 additions & 43 deletions tests/tests/misc_tests.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <eos/chain/BlockchainConfiguration.hpp>
#include <eos/chain/authority_checker.hpp>
#include <eos/chain/authority.hpp>

#include <eos/utilities/key_conversion.hpp>
#include <eos/utilities/rand.hpp>
Expand All @@ -20,28 +21,28 @@ BOOST_AUTO_TEST_SUITE(misc_tests)
BOOST_AUTO_TEST_CASE(median_properties_test)
{ try {
vector<BlockchainConfiguration> votes{
{1024 , 512 , 4096 , Asset(5000 ).amount, Asset(4000 ).amount, Asset(100 ).amount, 512 },
{10000 , 100 , 4096 , Asset(3333 ).amount, Asset(27109 ).amount, Asset(10 ).amount, 100 },
{2048 , 1500 , 1000 , Asset(5432 ).amount, Asset(2000 ).amount, Asset(50 ).amount, 1500 },
{100 , 25 , 1024 , Asset(90000 ).amount, Asset(0 ).amount, Asset(433 ).amount, 25 },
{1024 , 1000 , 100 , Asset(10 ).amount, Asset(50 ).amount, Asset(200 ).amount, 1000 },
{1024 , 512 , 4096 , Asset(5000 ).amount, Asset(4000 ).amount, Asset(100 ).amount, 512 , 6},
{10000 , 100 , 4096 , Asset(3333 ).amount, Asset(27109 ).amount, Asset(10 ).amount, 100 , 6},
{2048 , 1500 , 1000 , Asset(5432 ).amount, Asset(2000 ).amount, Asset(50 ).amount, 1500 , 6},
{100 , 25 , 1024 , Asset(90000 ).amount, Asset(0 ).amount, Asset(433 ).amount, 25 , 6},
{1024 , 1000 , 100 , Asset(10 ).amount, Asset(50 ).amount, Asset(200 ).amount, 1000 , 6},
};
BlockchainConfiguration medians{
1024, 512, 1024, Asset(5000).amount, Asset(2000).amount, Asset(100).amount, 512
1024, 512, 1024, Asset(5000).amount, Asset(2000).amount, Asset(100).amount, 512, 6
};

BOOST_CHECK_EQUAL(BlockchainConfiguration::get_median_values(votes), medians);

votes.emplace_back(BlockchainConfiguration{1, 1, 1, 1, 1, 1, 1});
votes.emplace_back(BlockchainConfiguration{1, 1, 1, 1, 1, 1, 1});
medians = BlockchainConfiguration {1024, 100, 1000, Asset(3333).amount, Asset(50).amount, Asset(50).amount, 100};
votes.emplace_back(BlockchainConfiguration{1, 1, 1, 1, 1, 1, 1, 1});
votes.emplace_back(BlockchainConfiguration{1, 1, 1, 1, 1, 1, 1, 1});
medians = BlockchainConfiguration {1024, 100, 1000, Asset(3333).amount, Asset(50).amount, Asset(50).amount, 100, 6};

BOOST_CHECK_EQUAL(BlockchainConfiguration::get_median_values(votes), medians);
BOOST_CHECK_EQUAL(BlockchainConfiguration::get_median_values({medians}), medians);

votes.erase(votes.begin() + 2);
votes.erase(votes.end() - 1);
medians = BlockchainConfiguration {1024, 100, 1024, Asset(3333).amount, Asset(50).amount, Asset(100).amount, 100};
medians = BlockchainConfiguration {1024, 100, 1024, Asset(3333).amount, Asset(50).amount, Asset(100).amount, 100, 6};
BOOST_CHECK_EQUAL(BlockchainConfiguration::get_median_values(votes), medians);
} FC_LOG_AND_RETHROW() }

Expand Down Expand Up @@ -92,21 +93,21 @@ BOOST_AUTO_TEST_CASE(authority_checker)

auto A = Complex_Authority(2, ((a,1))((b,1)),);
{
auto checker = MakeAuthorityChecker(GetNullAuthority, {a, b});
auto checker = MakeAuthorityChecker(GetNullAuthority, 2, {a, b});
BOOST_CHECK(checker.satisfied(A));
BOOST_CHECK(checker.all_keys_used());
BOOST_CHECK_EQUAL(checker.used_keys().size(), 2);
BOOST_CHECK_EQUAL(checker.unused_keys().size(), 0);
}
{
auto checker = MakeAuthorityChecker(GetNullAuthority, {a, c});
auto checker = MakeAuthorityChecker(GetNullAuthority, 2, {a, c});
BOOST_CHECK(!checker.satisfied(A));
BOOST_CHECK(!checker.all_keys_used());
BOOST_CHECK_EQUAL(checker.used_keys().size(), 0);
BOOST_CHECK_EQUAL(checker.unused_keys().size(), 2);
}
{
auto checker = MakeAuthorityChecker(GetNullAuthority, {a, b, c});
auto checker = MakeAuthorityChecker(GetNullAuthority, 2, {a, b, c});
BOOST_CHECK(checker.satisfied(A));
BOOST_CHECK(!checker.all_keys_used());
BOOST_CHECK_EQUAL(checker.used_keys().size(), 2);
Expand All @@ -116,52 +117,52 @@ BOOST_AUTO_TEST_CASE(authority_checker)
BOOST_CHECK_EQUAL(checker.unused_keys().count(c), 1);
}
{
auto checker = MakeAuthorityChecker(GetNullAuthority, {b, c});
auto checker = MakeAuthorityChecker(GetNullAuthority, 2, {b, c});
BOOST_CHECK(!checker.satisfied(A));
BOOST_CHECK(!checker.all_keys_used());
BOOST_CHECK_EQUAL(checker.used_keys().size(), 0);
}

A = Complex_Authority(3, ((a,1))((b,1))((c,1)),);
BOOST_CHECK(MakeAuthorityChecker(GetNullAuthority, {c, b, a}).satisfied(A));
BOOST_CHECK(!MakeAuthorityChecker(GetNullAuthority, {a, b}).satisfied(A));
BOOST_CHECK(!MakeAuthorityChecker(GetNullAuthority, {a, c}).satisfied(A));
BOOST_CHECK(!MakeAuthorityChecker(GetNullAuthority, {b, c}).satisfied(A));
BOOST_CHECK(MakeAuthorityChecker(GetNullAuthority, 2, {c, b, a}).satisfied(A));
BOOST_CHECK(!MakeAuthorityChecker(GetNullAuthority, 2, {a, b}).satisfied(A));
BOOST_CHECK(!MakeAuthorityChecker(GetNullAuthority, 2, {a, c}).satisfied(A));
BOOST_CHECK(!MakeAuthorityChecker(GetNullAuthority, 2, {b, c}).satisfied(A));

A = Complex_Authority(1, ((a, 1))((b, 1)),);
BOOST_CHECK(MakeAuthorityChecker(GetNullAuthority, {a}).satisfied(A));
BOOST_CHECK(MakeAuthorityChecker(GetNullAuthority, {b}).satisfied(A));
BOOST_CHECK(!MakeAuthorityChecker(GetNullAuthority, {c}).satisfied(A));
BOOST_CHECK(MakeAuthorityChecker(GetNullAuthority, 2, {a}).satisfied(A));
BOOST_CHECK(MakeAuthorityChecker(GetNullAuthority, 2, {b}).satisfied(A));
BOOST_CHECK(!MakeAuthorityChecker(GetNullAuthority, 2, {c}).satisfied(A));

A = Complex_Authority(1, ((a, 2))((b, 1)),);
BOOST_CHECK(MakeAuthorityChecker(GetNullAuthority, {a}).satisfied(A));
BOOST_CHECK(MakeAuthorityChecker(GetNullAuthority, {b}).satisfied(A));
BOOST_CHECK(!MakeAuthorityChecker(GetNullAuthority, {c}).satisfied(A));
BOOST_CHECK(MakeAuthorityChecker(GetNullAuthority, 2, {a}).satisfied(A));
BOOST_CHECK(MakeAuthorityChecker(GetNullAuthority, 2, {b}).satisfied(A));
BOOST_CHECK(!MakeAuthorityChecker(GetNullAuthority, 2, {c}).satisfied(A));

auto GetCAuthority = [c_public_key](auto){return Complex_Authority(1, ((c, 1)),);};

A = Complex_Authority(2, ((a, 2))((b, 1)), (("hello", "world", 1)));
{
auto checker = MakeAuthorityChecker(GetCAuthority, {a});
auto checker = MakeAuthorityChecker(GetCAuthority, 2, {a});
BOOST_CHECK(checker.satisfied(A));
BOOST_CHECK(checker.all_keys_used());
}
{
auto checker = MakeAuthorityChecker(GetCAuthority, {b});
auto checker = MakeAuthorityChecker(GetCAuthority, 2, {b});
BOOST_CHECK(!checker.satisfied(A));
BOOST_CHECK_EQUAL(checker.used_keys().size(), 0);
BOOST_CHECK_EQUAL(checker.unused_keys().size(), 1);
BOOST_CHECK_EQUAL(checker.unused_keys().count(b), 1);
}
{
auto checker = MakeAuthorityChecker(GetCAuthority, {c});
auto checker = MakeAuthorityChecker(GetCAuthority, 2, {c});
BOOST_CHECK(!checker.satisfied(A));
BOOST_CHECK_EQUAL(checker.used_keys().size(), 0);
BOOST_CHECK_EQUAL(checker.unused_keys().size(), 1);
BOOST_CHECK_EQUAL(checker.unused_keys().count(c), 1);
}
{
auto checker = MakeAuthorityChecker(GetCAuthority, {b,c});
auto checker = MakeAuthorityChecker(GetCAuthority, 2, {b,c});
BOOST_CHECK(checker.satisfied(A));
BOOST_CHECK(checker.all_keys_used());
BOOST_CHECK_EQUAL(checker.used_keys().size(), 2);
Expand All @@ -170,7 +171,7 @@ BOOST_AUTO_TEST_CASE(authority_checker)
BOOST_CHECK_EQUAL(checker.used_keys().count(c), 1);
}
{
auto checker = MakeAuthorityChecker(GetCAuthority, {b,c,a});
auto checker = MakeAuthorityChecker(GetCAuthority, 2, {b,c,a});
BOOST_CHECK(checker.satisfied(A));
BOOST_CHECK(!checker.all_keys_used());
BOOST_CHECK_EQUAL(checker.used_keys().size(), 1);
Expand All @@ -181,14 +182,14 @@ BOOST_AUTO_TEST_CASE(authority_checker)
}

A = Complex_Authority(2, ((a, 1))((b, 1)), (("hello", "world", 1)));
BOOST_CHECK(!MakeAuthorityChecker(GetCAuthority, {a}).satisfied(A));
BOOST_CHECK(!MakeAuthorityChecker(GetCAuthority, {b}).satisfied(A));
BOOST_CHECK(!MakeAuthorityChecker(GetCAuthority, {c}).satisfied(A));
BOOST_CHECK(MakeAuthorityChecker(GetCAuthority, {a,b}).satisfied(A));
BOOST_CHECK(MakeAuthorityChecker(GetCAuthority, {b,c}).satisfied(A));
BOOST_CHECK(MakeAuthorityChecker(GetCAuthority, {a,c}).satisfied(A));
BOOST_CHECK(!MakeAuthorityChecker(GetCAuthority, 2, {a}).satisfied(A));
BOOST_CHECK(!MakeAuthorityChecker(GetCAuthority, 2, {b}).satisfied(A));
BOOST_CHECK(!MakeAuthorityChecker(GetCAuthority, 2, {c}).satisfied(A));
BOOST_CHECK(MakeAuthorityChecker(GetCAuthority, 2, {a,b}).satisfied(A));
BOOST_CHECK(MakeAuthorityChecker(GetCAuthority, 2, {b,c}).satisfied(A));
BOOST_CHECK(MakeAuthorityChecker(GetCAuthority, 2, {a,c}).satisfied(A));
{
auto checker = MakeAuthorityChecker(GetCAuthority, {a,b,c});
auto checker = MakeAuthorityChecker(GetCAuthority, 2, {a,b,c});
BOOST_CHECK(checker.satisfied(A));
BOOST_CHECK(!checker.all_keys_used());
BOOST_CHECK_EQUAL(checker.used_keys().size(), 2);
Expand All @@ -197,12 +198,12 @@ BOOST_AUTO_TEST_CASE(authority_checker)
}

A = Complex_Authority(2, ((a, 1))((b, 1)), (("hello", "world", 2)));
BOOST_CHECK(MakeAuthorityChecker(GetCAuthority, {a,b}).satisfied(A));
BOOST_CHECK(MakeAuthorityChecker(GetCAuthority, {c}).satisfied(A));
BOOST_CHECK(!MakeAuthorityChecker(GetCAuthority, {a}).satisfied(A));
BOOST_CHECK(!MakeAuthorityChecker(GetCAuthority, {b}).satisfied(A));
BOOST_CHECK(MakeAuthorityChecker(GetCAuthority, 2, {a,b}).satisfied(A));
BOOST_CHECK(MakeAuthorityChecker(GetCAuthority, 2, {c}).satisfied(A));
BOOST_CHECK(!MakeAuthorityChecker(GetCAuthority, 2, {a}).satisfied(A));
BOOST_CHECK(!MakeAuthorityChecker(GetCAuthority, 2, {b}).satisfied(A));
{
auto checker = MakeAuthorityChecker(GetCAuthority, {a,b,c});
auto checker = MakeAuthorityChecker(GetCAuthority, 2, {a,b,c});
BOOST_CHECK(checker.satisfied(A));
BOOST_CHECK(!checker.all_keys_used());
BOOST_CHECK_EQUAL(checker.used_keys().size(), 1);
Expand All @@ -223,7 +224,12 @@ BOOST_AUTO_TEST_CASE(authority_checker)

A = Complex_Authority(5, ((a, 2))((b, 2))((c, 2)), (("top", "top", 5)));
{
auto checker = MakeAuthorityChecker(GetAuthority, {a,b,c,d,e});
auto checker = MakeAuthorityChecker(GetAuthority, 2, {d, e});
BOOST_CHECK(checker.satisfied(A));
BOOST_CHECK(checker.all_keys_used());
}
{
auto checker = MakeAuthorityChecker(GetAuthority, 2, {a,b,c,d,e});
BOOST_CHECK(checker.satisfied(A));
BOOST_CHECK(!checker.all_keys_used());
BOOST_CHECK_EQUAL(checker.used_keys().size(), 2);
Expand All @@ -232,7 +238,7 @@ BOOST_AUTO_TEST_CASE(authority_checker)
BOOST_CHECK_EQUAL(checker.used_keys().count(e), 1);
}
{
auto checker = MakeAuthorityChecker(GetAuthority, {a,b,c,e});
auto checker = MakeAuthorityChecker(GetAuthority, 2, {a,b,c,e});
BOOST_CHECK(checker.satisfied(A));
BOOST_CHECK(!checker.all_keys_used());
BOOST_CHECK_EQUAL(checker.used_keys().size(), 3);
Expand All @@ -241,6 +247,21 @@ BOOST_AUTO_TEST_CASE(authority_checker)
BOOST_CHECK_EQUAL(checker.used_keys().count(b), 1);
BOOST_CHECK_EQUAL(checker.used_keys().count(c), 1);
}
BOOST_CHECK(MakeAuthorityChecker(GetAuthority, 1, {a,b,c}).satisfied(A));
// Fails due to short recursion depth limit
BOOST_CHECK(!MakeAuthorityChecker(GetAuthority, 1, {d,e}).satisfied(A));

A = Complex_Authority(2, ((a, 1))((b, 1))((c, 1)),);
auto B = Complex_Authority(1, ((b, 1))((c, 1)),);
{
auto checker = MakeAuthorityChecker(GetNullAuthority, 2, {a,b,c});
BOOST_CHECK(validate(A));
BOOST_CHECK(validate(B));
BOOST_CHECK(checker.satisfied(A));
BOOST_CHECK(checker.satisfied(B));
BOOST_CHECK(!checker.all_keys_used());
BOOST_CHECK_EQUAL(checker.unused_keys().count(c), 1);
}
} FC_LOG_AND_RETHROW() }

/// Test creating the wallet
Expand Down
Loading

0 comments on commit 4bc6267

Please sign in to comment.