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

Commit

Permalink
Partially revert 67c9b8b
Browse files Browse the repository at this point in the history
Remove the changes around moving authorizations from Message to
Transaction, as we decided this is not the best way to go. See details
at #2 (comment)
  • Loading branch information
nathanielhourt committed Aug 1, 2017
1 parent e1f0aa0 commit eaef8b1
Show file tree
Hide file tree
Showing 16 changed files with 97 additions and 189 deletions.
48 changes: 8 additions & 40 deletions libraries/chain/chain_controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -567,9 +567,8 @@ 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);

output.notify.reserve( apply_ctx.notified.size() );
Expand All @@ -578,7 +577,7 @@ void chain_controller::process_message( const ProcessedTransaction& trx, Account
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 +636,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 @@ -830,11 +798,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
ProcessedTransaction trx; /// dummy tranaction required for scope validation
trx.scope = { config::EosContractName, "inita" };
std::sort(trx.scope.begin(), trx.scope.end() );
with_skip_flags( skip_scope_check, [&](){
process_message(trx,m.code,m,nullptr,output);
process_message(trx,m.code,m,output);
} );
});
});
Expand Down Expand Up @@ -1103,7 +1071,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 +1079,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 +1140,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 +1149,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
3 changes: 1 addition & 2 deletions libraries/chain/include/eos/chain/chain_controller.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -275,8 +275,7 @@ namespace eos { namespace chain {
void validate_authority(const SignedTransaction& trx )const;
/// @}

void process_message(const ProcessedTransaction& trx, AccountName code, const Message& message,
TransactionAuthorizationChecker* authChecker, MessageOutput& output);
void process_message(const ProcessedTransaction& trx, AccountName code, const Message& message, MessageOutput& output);
void apply_message(apply_context& c);

bool should_check_for_duplicate_transactions()const { return !(_skip_flags&skip_transaction_dupe_check); }
Expand Down
8 changes: 4 additions & 4 deletions libraries/chain/include/eos/chain/message.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ namespace eos { namespace chain {
struct Message : public types::Message {
Message() = default;
template<typename T>
Message(const AccountName& code, const types::FuncName& type, T&& value)
:types::Message(code, type, Bytes()) {
Message(const AccountName& code, const vector<types::AccountPermission>& authorization, const types::FuncName& type, T&& value)
:types::Message(code, type, authorization, Bytes()) {
set<T>(type, std::forward<T>(value));
}

Message(const AccountName& code, const types::FuncName& type)
:types::Message(code, type, Bytes()) {}
Message(const AccountName& code, const vector<types::AccountPermission>& authorization, const types::FuncName& type)
:types::Message(code, type, authorization, Bytes()) {}

Message(const types::Message& m) : types::Message(m) {}

Expand Down
20 changes: 9 additions & 11 deletions libraries/chain/include/eos/chain/message_handling_contexts.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@ class message_validate_context {
const chainbase::database& d,
const chain::Transaction& t,
const chain::Message& m,
types::AccountName c,
TransactionAuthorizationChecker* authChecker)
:controller(control),db(d),trx(t),msg(m),code(c),authChecker(authChecker){}
types::AccountName c )
:controller(control),db(d),trx(t),msg(m),code(c),used_authorizations(msg.authorization.size(), false){}

/**
* @brief Require @ref account to have approved of this message
Expand All @@ -37,8 +36,6 @@ class message_validate_context {
const chain::Message& msg; ///< message being applied
types::AccountName code; ///< the code that is currently running

TransactionAuthorizationChecker* authChecker;


int32_t load_i64( Name scope, Name code, Name table, Name Key, char* data, uint32_t maxlen );

Expand All @@ -59,6 +56,9 @@ class message_validate_context {
uint128_t* primary, uint128_t* secondary, char* data, uint32_t maxlen );
int32_t lowerbound_secondary_i128i128( Name scope, Name code, Name table,
uint128_t* primary, uint128_t* secondary, char* data, uint32_t maxlen );

///< Parallel to msg.authorization; tracks which permissions have been used while processing the message
vector<bool> used_authorizations;
};

class precondition_validate_context : public message_validate_context {
Expand All @@ -67,9 +67,8 @@ class precondition_validate_context : public message_validate_context {
const chainbase::database& db,
const chain::Transaction& t,
const chain::Message& m,
const types::AccountName& code,
TransactionAuthorizationChecker* authChecker)
:message_validate_context(con, db, t, m, code, authChecker){}
const types::AccountName& code)
:message_validate_context(con, db, t, m, code){}
};

class apply_context : public precondition_validate_context {
Expand All @@ -78,9 +77,8 @@ class apply_context : public precondition_validate_context {
chainbase::database& db,
const chain::Transaction& t,
const chain::Message& m,
const types::AccountName& code,
TransactionAuthorizationChecker* authChecker)
:precondition_validate_context(con,db,t,m,code,authChecker),mutable_controller(con),mutable_db(db){}
const types::AccountName& code)
:precondition_validate_context(con,db,t,m,code),mutable_controller(con),mutable_db(db){}

int32_t store_i64( Name scope, Name table, Name key, const char* data, uint32_t len);
int32_t remove_i64( Name scope, Name table, Name key );
Expand Down
74 changes: 0 additions & 74 deletions libraries/chain/include/eos/chain/transaction.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,80 +146,6 @@ namespace eos { namespace chain {
vector<MessageOutput> output;
};

/**
* @brief This class aids in checking that a transaction is properly authorized, and that no unnecessary
* authorizations are present in it.
*
* In order for a transaction to be valid, it must declare the permissions it requires to execute all of its
* contained messages (@see SignedTransaction::authorizations). The blockchain can verify that the transaction bears
* signatures necessary and sufficient to satisfy its declared permissions before the transaction is executed;
* however, the blockchain cannot know whether the declared permissions are necessary and sufficient to authorize
* the transaction until the transaction is fully executed. This is because the permissions required is a
* contract-layer concern, so the way we discover what permissions are required is by executing the messages, and as
* the message handlers execute, they assert that certain permissions are required.
*
* This class takes the list of declared permissions provided by a transaction at construction. As the transaction
* is subsequently executed, and the message handlers assert that permissions are required, and these required
* permissions can be passed to @ref requirePermission which will verify that the declared permissions satisfy the
* required permissions. This class also tracks which of the declared permissions have been used to satisfy a
* permission passed to @ref requirePermission and which ones have not.
*
* When the transaction is finished executing, call @ref allPermissionsUsed to determine whether any declared
* permissions were unnecessary to fully authorize the transaction.
*/
class TransactionAuthorizationChecker {
public:
using ParentGetter = std::function<fc::optional<types::AccountPermission>(const types::AccountPermission&)>;

/**
* @param declaredPermissions The permissions declared by the transaction as necessary and sufficient to
* authorize it
* @param getParentPermission A callable which takes a @ref types::AccountPermission and returns its parent, or
* an empty optional if no parent exists
*/
TransactionAuthorizationChecker(const vector<types::AccountPermission>& declaredPermissions,
ParentGetter getParentPermission)
: declaredPermissions(declaredPermissions), getParentPermission(getParentPermission) {}

bool requirePermission(const types::AccountPermission& permission) {
auto Matches = [](const types::AccountPermission& permission) {
return [&permission](const types::AccountPermission& other) {
return permission.account == other.account && permission.permission == other.permission;
};
};

auto itr = std::find_if(declaredPermissions.begin(), declaredPermissions.end(), Matches(permission));
if (itr != declaredPermissions.end()) {
usedPermissions[itr - declaredPermissions.begin()] = true;
return true;
}

auto parent = getParentPermission(permission);
while (parent) {
itr = std::find_if(declaredPermissions.begin(), declaredPermissions.end(), Matches(*parent));
if (itr != declaredPermissions.end()) {
usedPermissions[itr - declaredPermissions.begin()] = true;
return true;
}
parent = getParentPermission(*parent);
}

return false;
}

bool allPermissionsUsed() const {
return std::all_of(usedPermissions.begin(), usedPermissions.end(), [](bool b){return b;});
}

private:
/// The list of permissions declared by the transaction
const vector<types::AccountPermission> declaredPermissions;
/// Parallel to @ref declaredPermissions; usedPermissions[N] is true iff declaredPermissions[N] has been required
vector<bool> usedPermissions = vector<bool>(declaredPermissions.size(), false);

ParentGetter getParentPermission;
};

/// @} transactions group

} } // eos::chain
Expand Down
5 changes: 1 addition & 4 deletions libraries/chain/message_handling_contexts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,7 @@
namespace eos { namespace chain {

void message_validate_context::require_authorization(const types::AccountName& account) {
#warning TODO: Look up the permission_object that account has specified to use for this message type
if (authChecker)
EOS_ASSERT(authChecker->requirePermission({account, "active"}), tx_missing_auth,
"Transaction does not declare required authority '${auth}'", ("auth", account));
#warning TODO
}

void message_validate_context::require_scope(const types::AccountName& account)const {
Expand Down
3 changes: 1 addition & 2 deletions libraries/native_contract/eos_contract.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,7 @@ void apply_eos_setcode(apply_context& context) {
a.set_abi( msg.abi );
});

apply_context init_context( context.mutable_controller, context.mutable_db, context.trx, context.msg, msg.account,
context.authChecker);
apply_context init_context( context.mutable_controller, context.mutable_db, context.trx, context.msg, msg.account );
wasm_interface::get().init( init_context );
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,16 @@ std::vector<chain::Message> native_contract_chain_initializer::prepare_database(
};
for (const auto& acct : genesis.initial_accounts) {
chain::Message message(config::EosContractName,
vector<types::AccountPermission>{{config::EosContractName, "active"}},
"newaccount", types::newaccount(config::EosContractName, acct.name,
KeyAuthority(acct.owner_key),
KeyAuthority(acct.active_key),
KeyAuthority(acct.owner_key),
acct.staking_balance));
messages_to_process.emplace_back(std::move(message));
if (acct.liquid_balance > 0) {
message = chain::Message(config::EosContractName,
message = chain::Message(config::EosContractName,
vector<types::AccountPermission>{{config::EosContractName, "active"}},
"transfer", types::transfer(config::EosContractName, acct.name,
acct.liquid_balance.amount/*, "Genesis Allocation"*/));
messages_to_process.emplace_back(std::move(message));
Expand All @@ -101,7 +103,7 @@ std::vector<chain::Message> native_contract_chain_initializer::prepare_database(

// Create initial producers
auto CreateProducer = boost::adaptors::transformed([config = genesis.initial_configuration](const auto& p) {
return chain::Message(config::EosContractName,
return chain::Message(config::EosContractName, vector<types::AccountPermission>{{p.owner_name, "active"}},
"setproducer", types::setproducer(p.owner_name, p.block_signing_key, config));
});
boost::copy(genesis.initial_producers | CreateProducer, std::back_inserter(messages_to_process));
Expand Down
3 changes: 2 additions & 1 deletion libraries/types/types.eos
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ struct AccountPermission

struct Message
code AccountName # the contract defining the primary code to execute for code/type
type FuncName # the action to be taken
type FuncName # the action to be taken
authorization AccountPermission[] # the accounts and permission levels provided
data Bytes # opaque data processed by code

struct AccountPermissionWeight
Expand Down
Loading

0 comments on commit eaef8b1

Please sign in to comment.