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

Progress on #7 and #123 #137

Merged
merged 29 commits into from
Aug 9, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
eaef8b1
Partially revert 67c9b8b4615f412ec8f47cadc90ca98bd73386ef
nathanielhourt Aug 1, 2017
824bea0
Fix typo
kalloc Aug 1, 2017
8b1fbf7
Temporary add help command
kalloc Aug 1, 2017
f30ccb0
Adjust standard docker makefile
peterwillcn Aug 2, 2017
aa057cc
Add packages for ci
peterwillcn Aug 2, 2017
4592631
Add build status
peterwillcn Aug 2, 2017
9b98190
merged apply, precondition and validate contexts into apply_context
iamaprotoss Aug 3, 2017
9ad3260
Ref #7: Progress towards auth
nathanielhourt Aug 1, 2017
0393f95
Ref #7: Implement require_authorization, fix tests
nathanielhourt Aug 1, 2017
ec03f1f
Ref #7: Check all declared auths get required
nathanielhourt Aug 1, 2017
94fc433
Ref #123: Define types
nathanielhourt Aug 2, 2017
e4f3979
Ref #123: Implement lookup_minimum_permission
nathanielhourt Aug 2, 2017
05550b8
Ref #123: Rename requirepermission->linkauth
nathanielhourt Aug 2, 2017
8c5a0e7
Ref #123: Implement linkauth
nathanielhourt Aug 2, 2017
12dcd35
Ref #123: Define/Implement unlinkauth
nathanielhourt Aug 2, 2017
d78f1a6
Add satisfiability check to validate(Authority)
nathanielhourt Aug 2, 2017
fbff3c3
Ref #123: Implement updateauth, deleteauth
nathanielhourt Aug 2, 2017
d15ce5e
Add Complex_Authority test macro
nathanielhourt Aug 3, 2017
d6f89ca
Ref #123: Test macros to set/delete auths
nathanielhourt Aug 3, 2017
d4989d4
Ref #123: Write tests, fix bugs
nathanielhourt Aug 3, 2017
6218ccb
Ref #123: Testing, fixing, 1 behavior change
nathanielhourt Aug 3, 2017
a210f9c
Use C++14, not 17
nathanielhourt Aug 8, 2017
4adbf0b
Remove conflict marker that snuck past
nathanielhourt Aug 8, 2017
fab1fcb
Merge branch 'master' into permission-links-123
nathanielhourt Aug 8, 2017
8d0b97f
Merge branch 'master' into permission-links-123
nathanielhourt Aug 8, 2017
0ec8c9d
Mark submodules 'ignore = dirty'
nathanielhourt Aug 8, 2017
806508a
Ref #123: Testing, fixing
nathanielhourt Aug 8, 2017
35c4740
Ref #123: Add test macros, testing, and fixing
nathanielhourt Aug 8, 2017
f7c6021
Improve error message
nathanielhourt Aug 9, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
[submodule "libraries/chainbase"]
path = libraries/chainbase
url = https://github.com/eosio/chainbase
ignore = dirty
[submodule "libraries/appbase"]
path = libraries/appbase
url = https://github.com/eosio/appbase
ignore = dirty
[submodule "libraries/binaryen"]
path = libraries/binaryen
url = https://github.com/WebAssembly/binaryen.git
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ INCLUDE( VersionMacros )
INCLUDE( SetupTargetMacros )

set( BLOCKCHAIN_NAME "Eos" )
set( CMAKE_CXX_STANDARD 17 )
set( CMAKE_CXX_STANDARD 14 )

set( CLI_CLIENT_EXECUTABLE_NAME eos_client )
set( GUI_CLIENT_EXECUTABLE_NAME eos )
Expand Down
152 changes: 86 additions & 66 deletions libraries/chain/chain_controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include <eos/chain/action_objects.hpp>
#include <eos/chain/transaction_object.hpp>
#include <eos/chain/producer_object.hpp>
#include <eos/chain/permission_link_object.hpp>

#include <eos/chain/wasm_interface.hpp>

Expand Down Expand Up @@ -244,6 +245,7 @@ ProcessedTransaction chain_controller::_push_transaction(const SignedTransaction
_pending_tx_session = _db.start_undo_session(true);

auto temp_session = _db.start_undo_session(true);
check_transaction_authorization(trx);
auto pt = _apply_transaction(trx);
_pending_transactions.push_back(trx);

Expand Down Expand Up @@ -328,6 +330,7 @@ signed_block chain_controller::_generate_block(
try
{
auto temp_session = _db.start_undo_session(true);
check_transaction_authorization(tx);
_apply_transaction(tx);
temp_session.squash();

Expand Down Expand Up @@ -434,6 +437,11 @@ void chain_controller::_apply_block(const signed_block& next_block)
("calc",next_block.calculate_merkle_root())("next_block",next_block)("id",next_block.id()));

const producer_object& signing_producer = validate_block_header(skip, next_block);

for (const auto& cycle : next_block.cycles)
for (const auto& thread : cycle)
for (const auto& trx : thread.user_input)
check_transaction_authorization(trx);

/* We do not need to push the undo state for each transaction
* because they either all apply and are valid or the
Expand Down Expand Up @@ -466,6 +474,39 @@ void chain_controller::_apply_block(const signed_block& next_block)

} FC_CAPTURE_AND_RETHROW( (next_block.block_num()) ) }

void chain_controller::check_transaction_authorization(const SignedTransaction& trx)const {
if ((_skip_flags & skip_transaction_signatures) && (_skip_flags & skip_authority_check)) {
ilog("Skipping auth and sigs checks");
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;
};
#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{}));

for (const auto& message : trx.messages)
for (const auto& declaredAuthority : message.authorization) {
const auto& minimumPermission = lookup_minimum_permission(declaredAuthority.account,
message.code, message.type);
if ((_skip_flags & skip_authority_check) == false) {
const auto& index = _db.get_index<permission_index>().indices();
EOS_ASSERT(getPermission(declaredAuthority).satisfies(minimumPermission, index), tx_irrelevant_auth,
"Message declares irrelevant authority '${auth}'", ("auth", declaredAuthority));
}
if ((_skip_flags & skip_transaction_signatures) == false) {
EOS_ASSERT(checker.satisfied(declaredAuthority), tx_missing_sigs,
"Transaction declares authority '${auth}', but does not have signatures for it.",
("auth", declaredAuthority));
}
}
}

ProcessedTransaction chain_controller::apply_transaction(const SignedTransaction& trx, uint32_t skip)
{
return with_skip_flags( skip, [&]() { return _apply_transaction(trx); });
Expand All @@ -480,38 +521,41 @@ try {
validate_uniqueness(trx);
validate_tapos(trx);
validate_referenced_accounts(trx);
validate_authority(trx);

} FC_CAPTURE_AND_RETHROW( (trx) ) }

void chain_controller::validate_authority( const SignedTransaction& trx )const {
if (_skip_flags | skip_transaction_signatures)
return;

auto getAuthority = [&db=_db](const types::AccountPermission& permission) {
auto key = boost::make_tuple(permission.account, permission.permission);
return db.get<permission_object, by_owner>(key).auth;
};
#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{}));

for (const auto& declaredAuthority : trx.authorizations)
EOS_ASSERT(checker.satisfied(declaredAuthority), tx_missing_sigs,
"Transaction declares authority '${auth}', but does not have signatures for it.",
("auth", declaredAuthority));
}

void chain_controller::validate_scope( const SignedTransaction& trx )const {
EOS_ASSERT(trx.scope.size() > 0, transaction_exception, "No scope specified by transaction" );
for( uint32_t i = 1; i < trx.scope.size(); ++i )
EOS_ASSERT( trx.scope[i-1] < trx.scope[i], transaction_exception, "Scopes must be sorted and unique" );
}

const permission_object& chain_controller::lookup_minimum_permission(types::AccountName authorizer_account,
types::AccountName code_account,
types::FuncName type) const {
try {
// First look up a specific link for this message type
auto key = boost::make_tuple(authorizer_account, code_account, type);
auto link = _db.find<permission_link_object, by_message_type>(key);
// If no specific link found, check for a contract-wide default
if (link == nullptr) {
get<2>(key) = "";
link = _db.find<permission_link_object, by_message_type>(key);
}

// If no specific or default link found, use active permission
auto permissionKey = boost::make_tuple<AccountName, PermissionName>(authorizer_account, "active");
if (link != nullptr)
get<1>(permissionKey) = link->required_permission;
return _db.get<permission_object, by_owner>(permissionKey);
} FC_CAPTURE_AND_RETHROW((authorizer_account)(code_account)(type))
}

void chain_controller::validate_uniqueness( const SignedTransaction& trx )const {
if( !should_check_for_duplicate_transactions() ) return;

auto transaction = _db.find<transaction_object, by_trx_id>(trx.id());
EOS_ASSERT(transaction == nullptr, transaction_exception, "Transaction is not unique");
EOS_ASSERT(transaction == nullptr, tx_duplicate, "Transaction is not unique");
}

void chain_controller::validate_tapos(const SignedTransaction& trx)const {
Expand All @@ -526,11 +570,12 @@ void chain_controller::validate_tapos(const SignedTransaction& trx)const {
}

void chain_controller::validate_referenced_accounts(const SignedTransaction& trx)const {
for(const auto& auth : trx.authorizations) {
require_account(auth.account);
}
for(const auto& msg : trx.messages) {
for (const auto& scope : trx.scope)
require_account(scope);
for (const auto& msg : trx.messages) {
require_account(msg.code);
for (const auto& auth : msg.authorization)
require_account(auth.account);
}
}

Expand Down Expand Up @@ -567,18 +612,24 @@ void chain_controller::validate_expiration(const SignedTransaction& trx) const
* The order of execution of precondition and apply can impact the validity of the
* entire message.
*/
void chain_controller::process_message( const ProcessedTransaction& trx, AccountName code, const Message& message,
TransactionAuthorizationChecker* authChecker, MessageOutput& output) {
apply_context apply_ctx(*this, _db, trx, message, code, authChecker);
void chain_controller::process_message(const ProcessedTransaction& trx, AccountName code,
const Message& message, MessageOutput& output) {
apply_context apply_ctx(*this, _db, trx, message, code);
apply_message(apply_ctx);

// process_message recurses for each notified account, but we only want to run this check at the top level
if (code == message.code && (_skip_flags & skip_authority_check) == false)
EOS_ASSERT(apply_ctx.all_authorizations_used(), tx_irrelevant_auth,
"Message declared authorities it did not need: ${unused}",
("unused", apply_ctx.unused_authorizations())("message", message));

output.notify.reserve( apply_ctx.notified.size() );

for( uint32_t i = 0; i < apply_ctx.notified.size(); ++i ) {
try {
auto notify_code = apply_ctx.notified[i];
output.notify.push_back( {notify_code} );
process_message( trx, notify_code, message, authChecker, output.notify.back().output );
process_message( trx, notify_code, message, output.notify.back().output );
} FC_CAPTURE_AND_RETHROW((apply_ctx.notified[i]))
}

Expand Down Expand Up @@ -637,44 +688,13 @@ ProcessedTransaction chain_controller::_apply_transaction(const SignedTransactio
*/
ProcessedTransaction chain_controller::process_transaction( const SignedTransaction& trx )
{ try {
// This lambda takes an AccountPermission, and returns its parent. It caches the permission_object it last returned,
// which allows it to simplify the database lookups when it is next used to fetch that object's parent.
auto getParentPermission =
[&index = _db.get_index<permission_index>().indices(), cache = static_cast<const permission_object*>(nullptr)]
(const types::AccountPermission& child) mutable -> fc::optional<types::AccountPermission> {
// Ensure cache points to permission_object corresponding to child
if (!(cache && cache->owner == child.account && cache->name == child.permission)) {
auto& ownerIndex = index.get<by_owner>();
auto itr = ownerIndex.find(boost::make_tuple(child.account, child.permission));
FC_ASSERT(itr != ownerIndex.end(), "Unable to find permission_object for AccountPermission '${perm}'",
("perm", child));
cache = &*itr;
}

// Make cache point to the parent of child and return result
if (cache) {
if (cache->parent._id == 0) {
cache = nullptr;
return {};
} else {
cache = &*index.get<by_id>().find(cache->parent);
return types::AccountPermission(cache->owner, cache->name);
}
}
return {};
};

ProcessedTransaction ptrx( trx );
TransactionAuthorizationChecker authChecker(trx.authorizations, getParentPermission);
ptrx.output.resize( trx.messages.size() );

for( uint32_t i = 0; i < ptrx.messages.size(); ++i ) {
process_message(ptrx, ptrx.messages[i].code, ptrx.messages[i], &authChecker, ptrx.output[i] );
process_message(ptrx, ptrx.messages[i].code, ptrx.messages[i], ptrx.output[i] );
}

EOS_ASSERT(authChecker.allPermissionsUsed(), tx_irrelevant_auth,
"Transaction declared an authorization it did not need");

return ptrx;
} FC_CAPTURE_AND_RETHROW( (trx) ) }

Expand Down Expand Up @@ -793,6 +813,7 @@ uint32_t chain_controller::last_irreversible_block_num() const {
void chain_controller::initialize_indexes() {
_db.add_index<account_index>();
_db.add_index<permission_index>();
_db.add_index<permission_link_index>();
_db.add_index<action_permission_index>();
_db.add_index<key_value_index>();
_db.add_index<key128x128_value_index>();
Expand Down Expand Up @@ -830,12 +851,11 @@ void chain_controller::initialize_chain(chain_initializer_interface& starter)
auto messages = starter.prepare_database(*this, _db);
std::for_each(messages.begin(), messages.end(), [&](const Message& m) {
MessageOutput output;
ProcessedTransaction trx; /// dummy transaction required for scope validation
trx.scope = { config::EosContractName, "inita" };
ProcessedTransaction trx; /// dummy tranaction required for scope validation
std::sort(trx.scope.begin(), trx.scope.end() );
with_skip_flags( skip_scope_check, [&](){
process_message(trx,m.code,m,nullptr,output);
} );
with_skip_flags(skip_scope_check | skip_transaction_signatures | skip_authority_check, [&](){
process_message(trx,m.code,m,output);
});
});
});
}
Expand Down Expand Up @@ -1103,7 +1123,6 @@ ProcessedTransaction chain_controller::transaction_from_variant( const fc::varia
GET_FIELD( vo, expiration, result );
GET_FIELD( vo, scope, result );
GET_FIELD( vo, signatures, result );
GET_FIELD( vo, authorizations, result );

if( vo.contains( "messages" ) ) {
const vector<variant>& msgs = vo["messages"].get_array();
Expand All @@ -1112,6 +1131,7 @@ ProcessedTransaction chain_controller::transaction_from_variant( const fc::varia
const auto& vo = msgs[i].get_object();
GET_FIELD( vo, code, result.messages[i] );
GET_FIELD( vo, type, result.messages[i] );
GET_FIELD( vo, authorization, result.messages[i] );

if( vo.contains( "data" ) ) {
const auto& data = vo["data"];
Expand Down Expand Up @@ -1172,7 +1192,6 @@ fc::variant chain_controller::transaction_to_variant( const ProcessedTransactio
SET_FIELD( trx_mvo, trx, expiration );
SET_FIELD( trx_mvo, trx, scope );
SET_FIELD( trx_mvo, trx, signatures );
SET_FIELD( trx_mvo, trx, authorizations );

vector<fc::mutable_variant_object> msgs( trx.messages.size() );
vector<fc::variant> msgsv(msgs.size());
Expand All @@ -1182,6 +1201,7 @@ fc::variant chain_controller::transaction_to_variant( const ProcessedTransactio
auto& msg = trx.messages[i];
SET_FIELD( msg_mvo, msg, code );
SET_FIELD( msg_mvo, msg, type );
SET_FIELD( msg_mvo, msg, authorization );

const auto& code_account = _db.get<account_object,by_name>( msg.code );
if( code_account.abi.size() > 4 ) { /// 4 == packsize of empty Abi
Expand Down
14 changes: 10 additions & 4 deletions libraries/chain/include/eos/chain/authority.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ struct shared_authority {
template<typename F>
class AuthorityChecker {
F PermissionToAuthority;
const flat_set<public_key_type>& signingKeys;
flat_set<public_key_type> signingKeys;

public:
AuthorityChecker(F PermissionToAuthority, const flat_set<public_key_type>& signingKeys)
Expand All @@ -55,12 +55,13 @@ class AuthorityChecker {
template<typename AuthorityType>
bool satisfied(const AuthorityType& authority) const {
UInt32 weight = 0;
for (const auto& kpw : authority.keys)
for (const auto& kpw : authority.keys) {
if (signingKeys.count(kpw.key)) {
weight += kpw.weight;
if (weight >= authority.threshold)
return true;
}
}
for (const auto& apw : authority.accounts)
//#warning TODO: Recursion limit? Yes: implement as producer-configurable parameter
if (satisfied(apw.permission)) {
Expand All @@ -81,20 +82,25 @@ AuthorityChecker<F> MakeAuthorityChecker(F&& pta, const flat_set<public_key_type
}

/**
* Makes sure all keys are unique and sorted and all account permissions are unique and sorted
* Makes sure all keys are unique and sorted and all account permissions are unique and sorted and that authority can
* be satisfied
*/
inline bool validate( types::Authority& auth ) {
const types::KeyPermissionWeight* prev = nullptr;
decltype(auth.threshold) totalWeight = 0;

for( const auto& k : auth.keys ) {
if( !prev ) prev = &k;
else if( prev->key < k.key ) return false;
totalWeight += k.weight;
}
const types::AccountPermissionWeight* pa = nullptr;
for( const auto& a : auth.accounts ) {
if( !pa ) pa = &a;
else if( pa->permission < a.permission ) return false;
totalWeight += a.weight;
}
return true;
return totalWeight >= auth.threshold;
}

} } // namespace eos::chain
Expand Down
Loading