Skip to content

Commit

Permalink
add slim account as a protocol feature
Browse files Browse the repository at this point in the history
  • Loading branch information
quocle108 committed Mar 26, 2024
1 parent 57e34e9 commit 888cc2a
Show file tree
Hide file tree
Showing 27 changed files with 943 additions and 107 deletions.
9 changes: 8 additions & 1 deletion libraries/chain/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ struct controller_impl {
set_activation_handler<builtin_protocol_feature_t::crypto_primitives>();
set_activation_handler<builtin_protocol_feature_t::bls_primitives>();
set_activation_handler<builtin_protocol_feature_t::disable_deferred_trxs_stage_2>();
set_activation_handler<builtin_protocol_feature_t::slim_account>();

self.irreversible_block.connect([this](const block_signal_params& t) {
const auto& [ block, id] = t;
Expand Down Expand Up @@ -876,7 +877,6 @@ struct controller_impl {
}

snapshot->write_section<value_t>([this]( auto& section ){
auto section_name = boost::core::demangle(typeid(value_t).name());
decltype(utils)::walk(db, [this, &section]( const auto &row ) {
section.add_row(row, db);
});
Expand Down Expand Up @@ -3935,6 +3935,13 @@ void controller_impl::on_activation<builtin_protocol_feature_t::disable_deferred
}
}

template<>
void controller_impl::on_activation<builtin_protocol_feature_t::slim_account>() {
db.modify( db.get<protocol_state_object>(), [&]( auto& ps ) {
add_intrinsic_to_whitelist( ps.whitelisted_intrinsics, "create_slim_account" );
} );
}

/// End of protocol feature activation handlers

} } /// eosio::chain
45 changes: 28 additions & 17 deletions libraries/chain/eosio_contract.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ void validate_authority_precondition( const apply_context& context, const author
("account", a.permission.actor)
);

if( a.permission.permission == config::owner_name || a.permission.permission == config::active_name )
continue; // account was already checked to exist, so its owner and active permissions should exist
if( a.permission.permission == config::active_name )
continue; // account was already checked to exist, so its active permissions should exist

if( a.permission.permission == config::eosio_code_name ) // virtual eosio.code permission does not really exist but is allowed
continue;
Expand Down Expand Up @@ -83,8 +83,8 @@ void apply_eosio_newaccount(apply_context& context) {
EOS_ASSERT( name_str.size() <= 12, action_validate_exception, "account names can only be 12 chars long" );

// Check if the creator is privileged
const auto &creator = db.get<account_metadata_object, by_name>(create.creator);
if( !creator.is_privileged() ) {
const auto *creator_metadata = db.find<account_metadata_object, by_name>(create.creator);
if( creator_metadata == nullptr || !creator_metadata->is_privileged() ){
EOS_ASSERT( name_str.find( "eosio." ) != 0, action_validate_exception,
"only privileged accounts can have names that start with 'eosio.'" );
}
Expand Down Expand Up @@ -145,22 +145,29 @@ void apply_eosio_setcode(apply_context& context) {
wasm_interface::validate(context.control, act.code);
}

const auto& account = db.get<account_metadata_object,by_name>(act.account);
bool existing_code = (account.code_hash != digest_type());
const auto* account_metadata = db.find<account_metadata_object,by_name>(act.account);
int64_t metadata_ram_delta = 0;
if(account_metadata == nullptr){
metadata_ram_delta -= config::billable_size_v<account_metadata_object>;
account_metadata = &db.create<account_metadata_object>([&](auto& a) {
a.name = act.account;
});
}
bool existing_code = (account_metadata->code_hash != digest_type());

EOS_ASSERT( code_size > 0 || existing_code, set_exact_code, "contract is already cleared" );

int64_t old_size = 0;
int64_t new_size = code_size * config::setcode_ram_bytes_multiplier;

if( existing_code ) {
const code_object& old_code_entry = db.get<code_object, by_code_hash>(boost::make_tuple(account.code_hash, account.vm_type, account.vm_version));
const code_object& old_code_entry = db.get<code_object, by_code_hash>(boost::make_tuple(account_metadata->code_hash, account_metadata->vm_type, account_metadata->vm_version));
EOS_ASSERT( old_code_entry.code_hash != code_hash, set_exact_code,
"contract is already running this version of code" );
old_size = (int64_t)old_code_entry.code.size() * config::setcode_ram_bytes_multiplier;
if( old_code_entry.code_ref_count == 1 ) {
db.remove(old_code_entry);
context.control.code_block_num_last_used(account.code_hash, account.vm_type, account.vm_version, context.control.head_block_num() + 1);
context.control.code_block_num_last_used(account_metadata->code_hash, account_metadata->vm_type, account_metadata->vm_version, context.control.head_block_num() + 1);
} else {
db.modify(old_code_entry, [](code_object& o) {
--o.code_ref_count;
Expand All @@ -187,7 +194,7 @@ void apply_eosio_setcode(apply_context& context) {
}
}

db.modify( account, [&]( auto& a ) {
db.modify( *account_metadata, [&]( auto& a ) {
a.code_sequence += 1;
a.code_hash = code_hash;
a.vm_type = act.vmtype;
Expand All @@ -207,7 +214,7 @@ void apply_eosio_setcode(apply_context& context) {
dm_logger->on_ram_trace(RAM_EVENT_ID("${account}", ("account", act.account)), "code", operation, "setcode");
}

context.add_ram_usage( act.account, new_size - old_size );
context.add_ram_usage( act.account, new_size - old_size + metadata_ram_delta);
}
}

Expand All @@ -218,7 +225,6 @@ void apply_eosio_setabi(apply_context& context) {

context.require_authorization(act.account);

db.get<account_object,by_name>(act.account);
const auto* account_metadata = db.find<account_metadata_object,by_name>(act.account);
int64_t metadata_ram_delta = 0;
if(account_metadata == nullptr){
Expand Down Expand Up @@ -267,15 +273,20 @@ void apply_eosio_updateauth(apply_context& context) {
EOS_ASSERT( update.permission.to_string().find( "eosio." ) != 0, action_validate_exception,
"Permission names that start with 'eosio.' are reserved" );
EOS_ASSERT(update.permission != update.parent, action_validate_exception, "Cannot set an authority as its own parent");
db.get<account_object, by_name>(update.account);
EOS_ASSERT(validate(update.auth), action_validate_exception,
"Invalid authority: ${auth}", ("auth", update.auth));
if( update.permission == config::active_name )
EOS_ASSERT(update.parent == config::owner_name, action_validate_exception, "Cannot change active authority's parent from owner", ("update.parent", update.parent) );
if (update.permission == config::owner_name)
if( update.permission == config::active_name ) {
try {
authorization.get_permission({update.account, config::owner_name});
EOS_ASSERT(update.parent == config::owner_name, action_validate_exception, "Cannot change active authority's parent from owner", ("update.parent", update.parent) );
} catch( const permission_query_exception& ) {
EOS_ASSERT(update.parent.empty(), action_validate_exception, "Cannot change active authority's parent of slim acocunt" );
}
} else if (update.permission == config::owner_name) {
EOS_ASSERT(update.parent.empty(), action_validate_exception, "Cannot change owner authority's parent");
else
} else {
EOS_ASSERT(!update.parent.empty(), action_validate_exception, "Only owner permission can have empty parent" );
}

if( update.auth.waits.size() > 0 ) {
auto max_delay = context.control.get_global_properties().configuration.max_transaction_delay;
Expand All @@ -293,7 +304,7 @@ void apply_eosio_updateauth(apply_context& context) {
// If a parent_id of 0 is going to be used to indicate the absence of a parent, then we need to make sure that the chain
// initializes permission_index with a dummy object that reserves the id of 0.
authorization_manager::permission_id_type parent_id = 0;
if( update.permission != config::owner_name ) {
if( update.permission != config::owner_name && !update.parent.empty()) {
auto& parent = authorization.get_permission({update.account, update.parent});
parent_id = parent.id;
}
Expand Down
5 changes: 3 additions & 2 deletions libraries/chain/include/eosio/chain/authority.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ namespace config {

struct shared_authority;

struct authority {
struct authority : fc::reflect_init{
authority( public_key_type k, uint32_t delay_sec = 0 )
:threshold(1),keys({{k,1}})
{
Expand Down Expand Up @@ -210,7 +210,8 @@ struct authority {
}

friend bool operator == ( const authority& lhs, const shared_authority& rhs );

friend struct fc::reflector<authority>;
void reflector_init()const {}
void sort_fields () {
std::sort(std::begin(keys), std::end(keys));
std::sort(std::begin(accounts), std::end(accounts));
Expand Down
4 changes: 3 additions & 1 deletion libraries/chain/include/eosio/chain/chain_snapshot.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ struct chain_snapshot_header {
* - chain_config update
* 6: Updated for v3.1.0 release
* 7: Updated
* - restructure account object
* - restructure account and account metadata objects
* - merge permisison and permisison usage objects, resource limits and resource usage
* - introduce the slim account
*/

static constexpr uint32_t minimum_compatible_version = 2;
Expand Down
1 change: 1 addition & 0 deletions libraries/chain/include/eosio/chain/eosio_contract.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ namespace eosio { namespace chain {
*/
///@{
void apply_eosio_newaccount(apply_context&);
void apply_eosio_newslimacc(apply_context&);
void apply_eosio_updateauth(apply_context&);
void apply_eosio_deleteauth(apply_context&);
void apply_eosio_linkauth(apply_context&);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ enum class builtin_protocol_feature_t : uint32_t {
bls_primitives = 21,
disable_deferred_trxs_stage_1 = 22,
disable_deferred_trxs_stage_2 = 23,
slim_account = 24,
reserved_private_fork_protocol_features = 500000,
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,8 @@ inline constexpr auto get_intrinsic_table() {
"env.bls_g2_map",
"env.bls_fp_mod",
"env.bls_fp_mul",
"env.bls_fp_exp"
"env.bls_fp_exp",
"env.create_slim_account"
);
}
inline constexpr std::size_t find_intrinsic_index(std::string_view hf) {
Expand Down
11 changes: 11 additions & 0 deletions libraries/chain/include/eosio/chain/webassembly/interface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,17 @@ namespace webassembly {
*/
void set_privileged(account_name account, bool is_priv);

/**
* create a slim account.
*
* @ingroup privileged
*
* @param account - account creator.
* @param name - new slim account name.
* @param pubkey - publickey.
*/
void create_slim_account(account_name creator, account_name name, legacy_span<const char> packed_authority);

// softfloat api
float _eosio_f32_add(float, float) const;
float _eosio_f32_sub(float, float) const;
Expand Down
11 changes: 11 additions & 0 deletions libraries/chain/protocol_feature_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,17 @@ retires a deferred transaction is invalid.
*/
{builtin_protocol_feature_t::disable_deferred_trxs_stage_1}
} )
( builtin_protocol_feature_t::slim_account, builtin_protocol_feature_spec{
"SLIM_ACCOUNT",
fc::variant("40aa1e384a52e19e3a98dd002430a81509f399356836b5ed0ef48f6d508464e6").as<digest_type>(),
// SHA256 hash of the raw message below within the comment delimiters (do not modify message below).
/*
Builtin protocol feature: SLIM_ACCOUNT
This feature introduces the concept of slim accounts, aiming to optimize
the resource usage associated with account.
*/
{}
} )
;


Expand Down
72 changes: 71 additions & 1 deletion libraries/chain/webassembly/privileged.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <eosio/chain/transaction_context.hpp>
#include <eosio/chain/resource_limits.hpp>
#include <eosio/chain/apply_context.hpp>
#include <eosio/chain/authorization_manager.hpp>

#include <fc/io/datastream.hpp>

Expand Down Expand Up @@ -213,7 +214,8 @@ namespace eosio { namespace chain { namespace webassembly {
}

bool interface::is_privileged( account_name n ) const {
return context.db.get<account_metadata_object, by_name>( n ).is_privileged();
auto const*account_metadata = context.db.find<account_metadata_object, by_name>( n );
return account_metadata != nullptr && account_metadata->is_privileged();
}

void interface::set_privileged( account_name n, bool is_priv ) {
Expand All @@ -223,4 +225,72 @@ namespace eosio { namespace chain { namespace webassembly {
ma.set_privileged( is_priv );
});
}
void interface::create_slim_account(account_name creator, account_name name, legacy_span<const char> packed_authority){
EOS_ASSERT( !context.trx_context.is_read_only(), wasm_execution_error, "create_slim_account not allowed in read-only transaction" );
authority active_auth;
fc::datastream<const char*> pubds ( packed_authority.data(), packed_authority.size() );

fc::raw::unpack( pubds, active_auth );

auto& authorization = context.control.get_mutable_authorization_manager();

EOS_ASSERT( validate(active_auth), action_validate_exception, "Invalid active authority");

auto& db = context.db;

auto name_str = name.to_string();

EOS_ASSERT( !name.empty(), action_validate_exception, "account name cannot be empty" );
EOS_ASSERT( name_str.size() <= 12, action_validate_exception, "account names can only be 12 chars long" );

// system account only can be created by newaccount
EOS_ASSERT( name_str.find( "eosio." ) != 0, action_validate_exception,
"only newaccount action can create account with name start with 'eosio.'" );

auto existing_account = db.find<account_object, by_name>(name);
EOS_ASSERT(existing_account == nullptr, account_name_exists_exception,
"Cannot create account named ${name}, as that name is already taken",
("name", name));

db.create<account_object>([&](auto& a) {
a.name = name;
a.creation_date = context.control.pending_block_time();
});

for (const auto& a : active_auth.accounts) {
auto* acct = context.db.find<account_object, by_name>(a.permission.actor);
EOS_ASSERT( acct != nullptr, action_validate_exception,
"account '${account}' does not exist",
("account", a.permission.actor)
);
if( a.permission.permission == config::active_name )
continue; // account was already checked to exist, so its active permissions should exist

if( a.permission.permission == config::eosio_code_name ) // virtual eosio.code permission does not really exist but is allowed
continue;

try {
context.control.get_authorization_manager().get_permission({a.permission.actor, a.permission.permission});
} catch( const permission_query_exception& ) {
EOS_THROW( action_validate_exception,
"permission '${perm}' does not exist",
("perm", a.permission)
);
}
}

const auto& active_permission = authorization.create_permission( name, config::active_name, 0,
std::move(active_auth), context.trx_context.is_transient() );
int64_t ram_delta = config::overhead_per_account_ram_bytes;
ram_delta -= config::billable_size_v<account_metadata_object>;
ram_delta += config::billable_size_v<permission_object>;
ram_delta += active_permission.auth.get_billable_size();
context.control.get_mutable_resource_limits_manager().initialize_account(name, context.trx_context.is_transient());

if (auto dm_logger = context.control.get_deep_mind_logger(context.trx_context.is_transient())) {
dm_logger->on_ram_trace(RAM_EVENT_ID("${name}", ("name", name)), "account", "add", "newslimacc");
}

context.add_ram_usage(name, ram_delta);
}
}}} // ns eosio::chain::webassembly
4 changes: 3 additions & 1 deletion libraries/chain/webassembly/runtimes/eos-vm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,8 @@ class eos_vm_profiling_module : public wasm_instantiated_module_interface {
if(auto it = _prof.find(account); it != _prof.end()) {
return it->second.get();
} else {
auto code_sequence = context.control.db().get<account_metadata_object, by_name>(account).code_sequence;
auto const *account_metadata = context.control.db().find<account_metadata_object, by_name>(account);
auto code_sequence = account_metadata != nullptr ? account_metadata->code_sequence : 0;
std::string basename = account.to_string() + "." + std::to_string(code_sequence);
auto prof = std::make_unique<profile_data>(basename + ".profile", *_instantiated_module);
auto [pos,_] = _prof.insert(std::pair{ account, std::move(prof)});
Expand Down Expand Up @@ -365,6 +366,7 @@ REGISTER_LEGACY_HOST_FUNCTION(get_blockchain_parameters_packed, privileged_check
REGISTER_LEGACY_HOST_FUNCTION(set_blockchain_parameters_packed, privileged_check);
REGISTER_HOST_FUNCTION(is_privileged, privileged_check);
REGISTER_HOST_FUNCTION(set_privileged, privileged_check);
REGISTER_LEGACY_HOST_FUNCTION(create_slim_account, privileged_check);

// softfloat api
REGISTER_INJECTED_HOST_FUNCTION(_eosio_f32_add);
Expand Down
1 change: 0 additions & 1 deletion libraries/testing/include/eosio/testing/tester.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,6 @@ namespace eosio { namespace testing {
bool multisig = false,
bool include_code = true
);

transaction_trace_ptr push_reqauth( account_name from, const vector<permission_level>& auths, const vector<private_key_type>& keys );
transaction_trace_ptr push_reqauth(account_name from, string role, bool multi_sig = false);
// use when just want any old non-context free action
Expand Down
Loading

0 comments on commit 888cc2a

Please sign in to comment.