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

Consensus protocol upgrade to access account permission authorities #6657

Closed
arhag opened this issue Jan 24, 2019 · 3 comments
Closed

Consensus protocol upgrade to access account permission authorities #6657

arhag opened this issue Jan 24, 2019 · 3 comments
Labels
CONSENSUS Introduces a change that may modify consensus protocol rules on an existing blockchain.

Comments

@arhag
Copy link
Contributor

arhag commented Jan 24, 2019

Background

It is useful to some contracts to be able to check the existence of a permission and, assuming it exists, get the actual authority of that permission. For example, this can help a permission management contract that allows controlled modifications to a user's permissions according to some on-chain policy. Or it can be used by an asserter contract to assert the old authority of a permission so that the permission modification policy can be moved off-chain to user applications that do not necessarily trust the full nodes they need to communicate with to get blockchain state such as the original permission authority.

Consensus upgrade feature

The goal of this consensus protocol upgrade feature is to allow any contract to read the permission objects (particularly the authority) of any account.

A new consensus protocol upgrade feature will be added to trigger the changes described in this consensus upgrade proposal. The actual digest for the feature understood at the blockchain level is to be determined. For the purposes of this proposal the codename GET_PERMISSION will be used to stand-in for whatever the feature identifier will actually end up being.

New intrinsic get_permission_lower_bound

A new intrinsic int32_t get_permission_lower_bound( const account_name& account, const permission_name& permission, array_ptr<char> buffer, size_t buffer_size ) should be added to the authorization_api. The C intrinsic for the WebAssembly side would have a signature such as int32_t get_permission_lower_bound( capi_name account, capi_name permission, char* buffer, uint32_t buffer_size ).

The idea is for contracts to be able to use this intrinsic to check for the existence of a particular permission, and if it exists, get the relevant information. A contract can do so by providing the name of the permission (permission) within the specific account (account) that it is interested in searching for.

The system will instead try to find a permission of the specified account that has the smallest permission name value among the set of permissions of the account that have names with a value no smaller than the value of the provided permission (i.e. a lower bound search). If there is no such set, then the intrinsic will return -1, otherwise it will return the size (in bytes) of the serialized data representing the found (lower bound) permission.

The returned serialized data is a record of type eosio::permission_record (defined below) packed according to the regular compactly packed serialization rules done by the EOSLIB_SERIALIZE macro. The definition of eosio::permission_record and the other new types it depends on is provided below:

struct key_weight {
   eosio::public_key  key;
   uint16_t           weight;
};

struct permission_level_weight {
   permission_level  permission;
   uint16_t          weight;
};

struct wait_weight {
   uint32_t           wait_sec;
   uint16_t           weight;
};

struct authority {
   uint32_t                              threshold = 0;
   std::vector<key_weight>               keys;
   std::vector<permission_level_weight>  accounts;
   std::vector<wait_weight>              waits;
};

struct permission_record {
   name       permission;
   name       parent_permission;
   time_point last_updated;
   authority  auth;
};

If a contract wants to find an exact match, it must parse the returned serialized data enough to verify the permission name of the returned permission matches the permission name provided to the intrinsic. Because of how the permission_record is serialized, it is sufficient to simply check the first 8 bytes of the serialized data (representing the permission field of the permission_record type) and compare it to the permission argument provided to the intrinsic. The intrinsic makes it convenient for the caller to specify only the first N bytes of the serialized record to be copied into the contract's WebAssembly memory (which can improve performance compared to copying the whole thing in when only the first few bytes are relevant). The system will copy in at most buffer_size bytes of the serialized data into buffer even if buffer_size is smaller than the actual size of the full serialized data.

This enables the eosiolib to include a C++ wrapper over the intrinsic to provide an exact lookup of a particular permission as follows:

std::optional<eosio::permission_record> get_permission( eosio::name account, eosio::name permission ) 
{
   constexpr size_t max_stack_buffer_size = 512;

   eosio::name found_permission;
   
   int32_t data_size = ::get_permission_lower_bound( account.value, permission.value, static_cast<char*>( &found_permission.value ), sizeof(found_permission.value) );

   if( data_size < 0 || permission != found_permission ) return {};

   char* buffer = reinterpret_cast<char*>( max_stack_buffer_size < data_size ? malloc(data_size) : alloca(data_size) );
   
   int32_t data_size2 = ::get_permission_lower_bound( account.value, permission.value, buffer, static_cast<uint32_t>(data_size) );
   eosio::check( data_size == data_size2, "different result from second call to get_permission_lower_bound" );
   
   eosio::permission_record result;
   eosio::datastream<const char*> ds(buffer, data_size);
   ds >> result;

   if( max_stack_buffer_size < data_size ) {
      free( buffer );
   }

   return result;
}

The implementation of the intrinsic can be optimized for buffer_size values of 0 and 8 to avoid actually doing any serialization of a permission_record type. It can calculate the size of what the serialized data would be by using fc::raw::pack_size to quickly compute the size of auth.to_authority() (where auth is the authority in the permission_object), or better yet add a method shared_authority::serialized_size to do a faster calculation of the serialized size that does not require conversion to an authority type. If buffer_size == 0 it would just return this calculated size rather than doing any actual serialization. If buffer_size == 8 it would simply copy the name field of the permission_object to buffer prior to returning the calculated size as before.

By enabling the raw intrinsic to be a lower bound search rather than an exact match, it also enables contract authors to write their contracts to iterate through all the permissions of an account if they so choose. A C++ wrapper function like eosio::get_permission defined above hides from the contract developer all of the complexity associated with implementing an exact lookup given only the lower bound intrinsic. Furthermore, implementing the intrinsic as a lower bound search as described above adds negligible overhead (if any at all) compared to an intrinsic designed to only do exact lookups.

Contracts should only be able to link with this intrinsic after GET_PERMISSION activation.

@arhag arhag added the HARDFORK label Jan 24, 2019
@arhag
Copy link
Contributor Author

arhag commented Jan 24, 2019

This issue depends on #6429 and #6437. It will also be setup by default to be a protocol feature requiring pre-activation, thus it also depends on #6431.

@arhag arhag added CONSENSUS Introduces a change that may modify consensus protocol rules on an existing blockchain. and removed HARDFORK labels Mar 22, 2019
@piecesnbits
Copy link

this would be very useful!

@aclark-b1
Copy link

In order to focus our efforts on issues that are currently creating difficulty for the community we are closing tickets that were created prior to the EOSIO 2.0 release. If you believe this issue is still relevant please feel free to reopen it or create a new one. Thank you for your continued support of EOSIO!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
CONSENSUS Introduces a change that may modify consensus protocol rules on an existing blockchain.
Projects
None yet
Development

No branches or pull requests

3 participants