From cbe6b91cd39f336ca7456acc0c5c830b44d80c45 Mon Sep 17 00:00:00 2001 From: Ed Rotthoff Date: Sun, 19 Mar 2023 10:27:40 -0600 Subject: [PATCH 01/15] FIP-40 initial contract for fio.perms FIP-40 skeletal contract for fio.perms --- contracts/CMakeLists.txt | 1 + contracts/fio.perms/CMakeLists.txt | 14 +++ contracts/fio.perms/fio.perms.abi | 174 +++++++++++++++++++++++++++++ contracts/fio.perms/fio.perms.cpp | 123 ++++++++++++++++++++ contracts/fio.perms/fio.perms.hpp | 100 +++++++++++++++++ 5 files changed, 412 insertions(+) create mode 100644 contracts/fio.perms/CMakeLists.txt create mode 100644 contracts/fio.perms/fio.perms.abi create mode 100644 contracts/fio.perms/fio.perms.cpp create mode 100644 contracts/fio.perms/fio.perms.hpp diff --git a/contracts/CMakeLists.txt b/contracts/CMakeLists.txt index 2d6c3f09..d9a2efb7 100644 --- a/contracts/CMakeLists.txt +++ b/contracts/CMakeLists.txt @@ -17,4 +17,5 @@ add_subdirectory(fio.request.obt) add_subdirectory(fio.tpid) add_subdirectory(fio.treasury) add_subdirectory(fio.escrow) +add_subdirectory(fio.perms) add_subdirectory(fio.staking) diff --git a/contracts/fio.perms/CMakeLists.txt b/contracts/fio.perms/CMakeLists.txt new file mode 100644 index 00000000..e5ba9e59 --- /dev/null +++ b/contracts/fio.perms/CMakeLists.txt @@ -0,0 +1,14 @@ +add_contract(fio.perms fio.perms ${CMAKE_CURRENT_SOURCE_DIR}/fio.perms.cpp) + +target_include_directories(fio.perms + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/../fio.perms/include + ${CMAKE_CURRENT_SOURCE_DIR}/../fio.address/include + ${CMAKE_CURRENT_SOURCE_DIR}/../ + ) + + +set_target_properties(fio.perms + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") diff --git a/contracts/fio.perms/fio.perms.abi b/contracts/fio.perms/fio.perms.abi new file mode 100644 index 00000000..38bf7d7f --- /dev/null +++ b/contracts/fio.perms/fio.perms.abi @@ -0,0 +1,174 @@ +{ + "version": "eosio::abi/1.1", + "types": [], + "structs": [ + { + "name": "permission_info", + "base": "", + "fields": [ + { + "name": "id", + "type": "uint64" + }, + { + "name": "object_type", + "type": "string" + }, + { + "name": "object_type_hash", + "type": "uint128" + }, + { + "name": "object_name", + "type": "string" + }, + { + "name": "object_name_hash", + "type": "uint128" + }, + { + "name": "permission_name", + "type": "string" + }, + { + "name": "permission_name_hash", + "type": "uint128" + }, + { + "name": "permission_control_hash", + "type": "uint128" + }, + { + "name": "owner_account", + "type": "uint64" + }, + { + "name": "auxilliary_info", + "type": "string" + } + ] + }, + { + "name": "access_info", + "base": "", + "fields": [ + { + "name": "id", + "type": "uint64" + }, + { + "name": "permission_id", + "type": "uint64" + }, + { + "name": "grantee_account", + "type": "uint64" + }, + { + "name": "access_hash", + "type": "uint128" + } + ] + }, + { + "name": "addperm", + "base": "", + "fields": [ + { + "name": "grantee_account", + "type": "name" + }, + { + "name": "permission_name", + "type": "string" + }, + { + "name": "permission_info", + "type": "string" + }, + { + "name": "object_name", + "type": "string" + }, + { + "name": "max_fee", + "type": "int64" + }, + { + "name": "tpid", + "type": "string" + }, + { + "name": "actor", + "type": "name" + } + ] + }, + { + "name": "remperm", + "base": "", + "fields": [ + { + "name": "grantee_account", + "type": "name" + }, + { + "name": "permission_name", + "type": "string" + }, + { + "name": "object_name", + "type": "string" + }, + { + "name": "max_fee", + "type": "int64" + }, + { + "name": "tpid", + "type": "string" + }, + { + "name": "actor", + "type": "name" + } + ] + } + ], + "actions": [ + { + "name": "addperm", + "type": "addperm", + "ricardian_contract": "" + }, + { + "name": "remperm", + "type": "remperm", + "ricardian_contract": "" + } + ], + "tables": [ + { + "name": "permissions", + "index_type": "i64", + "key_names": [ + "id" + ], + "key_types": [ + "string" + ], + "type": "permission_info" + }, + { + "name": "access", + "index_type": "i64", + "key_names": [], + "key_types": [], + "type": "access_info" + } + ], + "ricardian_clauses": [], + "error_messages": [], + "abi_extensions": [], + "variants": [] +} diff --git a/contracts/fio.perms/fio.perms.cpp b/contracts/fio.perms/fio.perms.cpp new file mode 100644 index 00000000..0003ee57 --- /dev/null +++ b/contracts/fio.perms/fio.perms.cpp @@ -0,0 +1,123 @@ +/** FIO permissions contract + * Description: + * + * + * We will introduce a notion of a permission, a permission is a definition of information that provides some control + * and/or access to objects in state which are owned by the account that creates the permission. + * Permissions definitions will be extensible within the FIO protocol. new permissions can be added into + * the protocols contracts using the permmissions tables in this contract. + * + * + * The following process will be used for the definition and integration of new permissions in the FIO protocols: + * Step 1: define the permission desired. + * Step 2: modify the fio.contracts affected by the new access to enforce and integrate the new permission. + * Step 3: rollout the new permission into testnet and main net using the following actions + * 3.1. rollout the new version of the contracts supporting the new permission. + * 3.2 FIO user accounts begin using the permission as indicated in the spec. + * + * The following vernacular is used throughout our design: + * Permission – the name of the permission, + * Permission info -- the object type that is to be controlled, the name of the object to be controlled, + * the owning account, and also including all parameterized data used by the permission according to the + * business logic required (such as access levels, or other abstractions that can be set when an + * account grants the permission). + * Permission Auxilliary Info – the json definition of all of the parameterized data used by a given permission that is unique for a permission. + * for FIP-40 no additional data is necessary. this field provides extensibility such that we can introduce new + * or novel parameters and dials used by a new permission if this is necessary. + * Grantor – the granting/owning account of the object that relates to the permission. + * Object – the object that is being access controlled by a permission (for FIP-40 this is the domain). + * Grantee – the non grantor account that is given a permission. + * Access -- an account has access to a permission when a permission is granted to an account. + * + * + * + * + * + * + * @author Ed Rotthoff + * @file fio.perms.cpp + * @license FIO Foundation ( https://github.com/fioprotocol/fio/blob/master/LICENSE ) + */ + +#include "fio.perms.hpp" +#include +#include +#include +#include +#include +#include + +namespace fioio { + + class [[eosio::contract("FioPermissions")]] FioPermissions : public eosio::contract { + + private: + + domains_table domains; + fionames_table fionames; + fiofee_table fiofees; + eosio_names_table accountmap; + config appConfig; + + + + public: + using contract::contract; + + FioPermissions(name s, name code, datastream ds) : contract(s, code, ds), + domains(_self, _self.value), + fionames(_self, _self.value), + fiofees(FeeContract, FeeContract.value), + accountmap(_self, _self.value){ + + configs_singleton configsSingleton(FeeContract, FeeContract.value); + appConfig = configsSingleton.get_or_default(config()); + } + + + [[eosio::action]] + void + addperm(const name &grantee_account, + const string &permission_name, + const string &permission_info, + const string &object_name, + const int64_t &max_fee, + const string &tpid, + const name &actor + ) { + + + print("addperm -- called. \n"); + + + const string response_string = "status: OK, Fee collected : 0"; + + send_response(response_string.c_str()); + + } + + [[eosio::action]] + void + remperm(const name &grantee_account, + const string &permission_name, + const string &object_name, + const int64_t &max_fee, + const string &tpid, + const name &actor + ) { + + + print("remperm -- called. \n"); + + + const string response_string = "status: OK, Fee collected : 0"; + + send_response(response_string.c_str()); + + } + + + }; + + EOSIO_DISPATCH(FioPermissions, (addperm)(remperm)) +} diff --git a/contracts/fio.perms/fio.perms.hpp b/contracts/fio.perms/fio.perms.hpp new file mode 100644 index 00000000..205c0c66 --- /dev/null +++ b/contracts/fio.perms/fio.perms.hpp @@ -0,0 +1,100 @@ +/** FIO permissions + * Description: see fio.perm.cpp. + * @author Ed Rotthoff + * @file fio.perms.hpp + * @license FIO Foundation ( https://github.com/fioprotocol/fio/blob/master/LICENSE ) + */ + +#pragma once + +#include +#include +#include + +#include + +using std::string; + +namespace fioio { + + using namespace eosio; + + + struct [[eosio::action]] permission_info { + + uint64_t id = 0; + // this is a string whose acceptable values should be defined in this file, we use "domain" for FIP-40. + string object_type = ""; + uint128_t object_type_hash = 0; + //this is the name of the object being controlled in state, for FIP-40 this will be the name of the domain + //being permissed to create new addresses. + string object_name = ""; + uint128_t object_name_hash = 0; + //this is the name of the permission, these values should be constants defined in this file. + //for FIP-40 we will use register_address_on_domain as the value. + string permission_name = ""; + uint128_t permission_name_hash = 0; + //by convention we will store the hashed value of the following concatination in this field to provide a + //unique search key by object_type, object_name, and permission_name + uint128_t permission_control_hash = 0; + uint64_t owner_account = 0; + //this field can contain any string based info that is useful for the permission. + //it shouldbe json based. for FIP-40 this is unused. + string auxilliary_info = ""; + + + uint64_t primary_key() const { return id; } + uint128_t by_object_type_hash() const { return object_type_hash; } + uint128_t by_object_name_hash() const { return object_name_hash; } + uint128_t by_permission_name_hash() const { return permission_name_hash; } + uint128_t by_permission_control_hash() const { return permission_control_hash; } + uint64_t by_owner_account() const { return owner_account; } + + + EOSLIB_SERIALIZE(permission_info, (id)(object_type)(object_type_hash)(object_name)(object_name_hash) + (permission_name)(permission_control_hash)(owner_account)(auxilliary_info)) + }; + //this state table contains information relating to the permissions that are granted in the FIO protocol + //please examine fio.perms.cpp for details relating to FIO permissions. + typedef multi_index<"permissions"_n, permission_info, + indexed_by<"byobjtype"_n, const_mem_fun < permission_info, uint128_t, &permission_info::by_object_type_hash>>, + indexed_by<"byobjname"_n, const_mem_fun < permission_info, uint128_t, &permission_info::by_object_name_hash>>, + indexed_by<"bypermname"_n, const_mem_fun < permission_info, uint128_t, &permission_info::by_permission_name_hash>>, + indexed_by<"bypermctrl"_n, const_mem_fun < permission_info, uint128_t, &permission_info::by_permission_control_hash>>, + indexed_by<"byowner"_n, const_mem_fun < permission_info, uint64_t, &permission_info::by_owner_account>> + > + permissions_table; + + + + + +struct [[eosio::action]] access_info { + + uint64_t id = 0; + uint64_t permission_id = 0; + uint64_t grantee_account = 0; + //this is the hashed value of the string concatination of grantee account, permission id + uint128_t access_hash = 0; + + + uint64_t primary_key() const { return id; } + uint64_t by_permission_id() const { return permission_id; } + uint64_t by_grantee_account() const { return grantee_account; } + uint128_t by_access_hash() const { return access_hash; } + + + EOSLIB_SERIALIZE(access_info, (id)(permission_id)(grantee_account)(access_hash)) +}; +//this state table contains information relating to the acesses that are granted in the FIO protocol +//please examine fio.perms.cpp for details relating to FIO permissions. +typedef multi_index<"access"_n, access_info, + indexed_by<"bypermid"_n, const_mem_fun < access_info, uint64_t, &access_info::by_permission_id>>, + indexed_by<"bygrantee"_n, const_mem_fun < access_info, uint64_t, &access_info::by_grantee_account>>, + indexed_by<"byaccess"_n, const_mem_fun < access_info, uint128_t, &access_info::by_access_hash>> +> +access_table; + + + +} From e945ec19652c6c88088b6d629dc975dc3b23fc18 Mon Sep 17 00:00:00 2001 From: Ed Rotthoff Date: Wed, 22 Mar 2023 11:20:04 -0600 Subject: [PATCH 02/15] FIP-40 fio.perms initial integration into contracts FIP-40 fio.perms initial integration into contracts --- contracts/eosio.msig/src/eosio.msig.cpp | 2 + contracts/fio.address/fio.address.cpp | 7 +- contracts/fio.common/fio.accounts.hpp | 2 + contracts/fio.common/fio.common.hpp | 4 +- contracts/fio.common/fioerror.hpp | 5 ++ contracts/fio.perms/fio.perms.cpp | 64 ++++++++++++++++++- contracts/fio.perms/fio.perms.hpp | 2 +- contracts/fio.staking/fio.staking.cpp | 5 +- .../fio.system/include/fio.system/native.hpp | 3 +- .../fio.system/src/delegate_bandwidth.cpp | 5 +- contracts/fio.system/src/fio.system.cpp | 8 ++- contracts/fio.token/src/fio.token.cpp | 3 +- contracts/fio.tpid/fio.tpid.cpp | 7 +- contracts/fio.treasury/fio.treasury.cpp | 13 ++-- 14 files changed, 106 insertions(+), 24 deletions(-) diff --git a/contracts/eosio.msig/src/eosio.msig.cpp b/contracts/eosio.msig/src/eosio.msig.cpp index 459dfa6a..37c8b330 100755 --- a/contracts/eosio.msig/src/eosio.msig.cpp +++ b/contracts/eosio.msig/src/eosio.msig.cpp @@ -88,6 +88,8 @@ namespace eosio { _proposer == fioio::TREASURYACCOUNT || _proposer == fioio::FIOSYSTEMACCOUNT || _proposer == fioio::FIOACCOUNT || + //FIP-40 + _proposer == fioio::PERMSACCOUNT || isTopProd) ) { //collect fees. diff --git a/contracts/fio.address/fio.address.cpp b/contracts/fio.address/fio.address.cpp index 9a3eb422..b206aba5 100644 --- a/contracts/fio.address/fio.address.cpp +++ b/contracts/fio.address/fio.address.cpp @@ -2514,9 +2514,10 @@ namespace fioio { void decrcounter(const string &fio_address, const int32_t &step) { check(step > 0, "step must be greater than 0"); + //FIP-40 check((has_auth(AddressContract) || has_auth(TokenContract) || has_auth(TREASURYACCOUNT) || has_auth(STAKINGACCOUNT) || - has_auth(REQOBTACCOUNT) || has_auth(SYSTEMACCOUNT) || has_auth(FeeContract)), - "missing required authority of fio.address, fio.token, fio.fee, fio.treasury, fio.reqobt, fio.system, fio.staking "); + has_auth(REQOBTACCOUNT) || has_auth(SYSTEMACCOUNT) || has_auth(FeeContract) || has_auth(PERMSACCOUNT)), + "missing required authority of fio.address, fio.token, fio.fee, fio.treasury, fio.reqobt, fio.system, fio.staking fio.perms"); auto namesbyname = fionames.get_index<"byname"_n>(); auto fioname_iter = namesbyname.find(string_to_uint128_hash(fio_address.c_str())); @@ -2578,7 +2579,7 @@ namespace fioio { }; - EOSIO_DISPATCH(FioNameLookup, (regaddress)(addaddress)(remaddress)(remalladdr)(regdomain)(renewdomain)(renewaddress) + EOSIO_DISPATCH(FioNameLookup,(regaddress)(addaddress)(remaddress)(remalladdr)(regdomain)(renewdomain)(renewaddress) (setdomainpub)(burnexpired)(decrcounter)(bind2eosio)(burnaddress)(xferdomain)(xferaddress)(addbundles)(xferescrow) (addnft)(remnft)(remallnfts)(burnnfts)(regdomadd)(updcryptkey)) } diff --git a/contracts/fio.common/fio.accounts.hpp b/contracts/fio.common/fio.accounts.hpp index 1bdaab66..6c444874 100644 --- a/contracts/fio.common/fio.accounts.hpp +++ b/contracts/fio.common/fio.accounts.hpp @@ -41,6 +41,8 @@ namespace fioio { static const name EscrowContract = name("fio.escrow"); static const name FIOACCOUNT = name("fio"); static const name FIOORACLEContract = name("fio.oracle"); + //FIP-40 + static const name PERMSACCOUNT = name("fio.perms"); static constexpr name FIOISSUER = name("eosio"_n); static constexpr eosio::symbol FIOSYMBOL = eosio::symbol("FIO", 9); diff --git a/contracts/fio.common/fio.common.hpp b/contracts/fio.common/fio.common.hpp index 11cf96b7..38e8eb23 100644 --- a/contracts/fio.common/fio.common.hpp +++ b/contracts/fio.common/fio.common.hpp @@ -145,7 +145,9 @@ namespace fioio { actor == fioio::FIOSYSTEMACCOUNT || actor == fioio::FIOACCOUNT || actor == fioio::EscrowContract || - actor == FIOORACLEContract); + actor == FIOORACLEContract || + //FIP-40 + actor == PERMSACCOUNT); } static constexpr uint64_t string_to_uint64_hash(const char *str) { diff --git a/contracts/fio.common/fioerror.hpp b/contracts/fio.common/fioerror.hpp index 55444a38..85e90a56 100644 --- a/contracts/fio.common/fioerror.hpp +++ b/contracts/fio.common/fioerror.hpp @@ -97,6 +97,11 @@ namespace fioio { constexpr auto ErrorRetireQuantity = ident | httpDataError | 159; constexpr auto ErrorInvalidMemo = ident | httpDataError | 160; constexpr auto ErrorDomainSaleNotFound = ident | httpInvalidError | 161; // domain not found in domainsales table + //FIP-40 + constexpr auto ErrorInvalidPermissionName = ident | httpDataError | 162; + constexpr auto ErrorInvalidPermissionInfo = ident | httpDataError | 163; + constexpr auto ErrorInvalidObjectName = ident | httpDataError | 164; + constexpr auto ErrorInvalidGranteeAccount = ident | httpDataError | 165; /** * Helper funtions for detecting rich error messages and extracting bitfielded values diff --git a/contracts/fio.perms/fio.perms.cpp b/contracts/fio.perms/fio.perms.cpp index 0003ee57..493a019d 100644 --- a/contracts/fio.perms/fio.perms.cpp +++ b/contracts/fio.perms/fio.perms.cpp @@ -59,12 +59,15 @@ namespace fioio { eosio_names_table accountmap; config appConfig; + permissions_table permissions; + public: using contract::contract; FioPermissions(name s, name code, datastream ds) : contract(s, code, ds), + permissions(_self,_self.value), domains(_self, _self.value), fionames(_self, _self.value), fiofees(FeeContract, FeeContract.value), @@ -90,7 +93,51 @@ namespace fioio { print("addperm -- called. \n"); - const string response_string = "status: OK, Fee collected : 0"; + // error if permission name is not the expected name register_address_on_domain. + fio_400_assert(permission_name.compare("register_address_on_domain") == 0, "permission_name", permission_name, + "Permission name is invalid", ErrorInvalidPermissionName); + // error if permission info is not empty. + fio_400_assert(permission_info.size() == 0, "permission_info", permission_info, + "Permission info is invalid", ErrorInvalidPermissionInfo); + // error if object name is not * or is not in the domains table + fio_400_assert(object_name.size() > 0, "object_name", object_name, + "Object name is invalid", ErrorInvalidObjectName); + // TODO error if the object name is not a domain owned by the actor. + // error if the grantee account does not exist. + fio_400_assert(is_account(grantee_account), "grantee_account", grantee_account.to_string(), + "grantee account is invalid", ErrorInvalidGranteeAccount); + // TODO error if the grantee account already has this permission. + + + //look for the permission in the permissions table. if it exists, verify all info, then note the id. + //if it does not exist then insert it, and note the id. + //look for an existing entry in the access table for this id, and grantee account. + //if the entry exists then error permission exists for this account. + //if it doesnt exist then add the record to the access table. + + + + + /* + const string controlv = "stuff" + object_name + permission_name; + + const uint64_t id = permissions.available_primary_key(); + //just blindly emplace for prototyping. + permissions.emplace(get_self(), [&](struct permission_info &p) { + p.id = id; + p.object_type = "stuff"; + p.object_type_hash = string_to_uint128_hash("stuff"); + p.object_name = object_name; + p.object_name_hash = string_to_uint128_hash(object_name); + p.permission_name = permission_name; + p.permission_name_hash = string_to_uint128_hash(permission_name); + p.permission_control_hash = string_to_uint128_hash(controlv); + p.owner_account = actor.value; + p.auxilliary_info = ""; + }); + */ + + const string response_string = "{\"status\": \"OK\", \"fee_collected\" : 0}"; send_response(response_string.c_str()); @@ -110,7 +157,20 @@ namespace fioio { print("remperm -- called. \n"); - const string response_string = "status: OK, Fee collected : 0"; + uint128_t permnamehash = string_to_uint128_hash(permission_name); + auto permsbypermname = permissions.get_index<"bypermname"_n>(); + auto bynameiter = permsbypermname.find(permnamehash); + int c = 0; + while (bynameiter != permsbypermname.end()) { + bynameiter++; + if (permission_name.compare(bynameiter->permission_name)==0) { + c++; + } + } + + print("remperm -- there are this many rows with name of. "+permission_name+ " count : "+to_string(c)+" \n"); + + const string response_string = "{\"status\": \"OK\", \"fee_collected\" : 0}"; send_response(response_string.c_str()); diff --git a/contracts/fio.perms/fio.perms.hpp b/contracts/fio.perms/fio.perms.hpp index 205c0c66..c6ff1a44 100644 --- a/contracts/fio.perms/fio.perms.hpp +++ b/contracts/fio.perms/fio.perms.hpp @@ -52,7 +52,7 @@ namespace fioio { EOSLIB_SERIALIZE(permission_info, (id)(object_type)(object_type_hash)(object_name)(object_name_hash) - (permission_name)(permission_control_hash)(owner_account)(auxilliary_info)) + (permission_name)(permission_name_hash)(permission_control_hash)(owner_account)(auxilliary_info)) }; //this state table contains information relating to the permissions that are granted in the FIO protocol //please examine fio.perms.cpp for details relating to FIO permissions. diff --git a/contracts/fio.staking/fio.staking.cpp b/contracts/fio.staking/fio.staking.cpp index 0bbd5075..5cd59b2d 100644 --- a/contracts/fio.staking/fio.staking.cpp +++ b/contracts/fio.staking/fio.staking.cpp @@ -56,8 +56,9 @@ class [[eosio::contract("Staking")]] Staking: public eosio::contract { void incgrewards(const int64_t &fioamountsufs) { eosio_assert((has_auth(AddressContract) || has_auth(TokenContract) || has_auth(TREASURYACCOUNT) || has_auth(STAKINGACCOUNT) || has_auth(REQOBTACCOUNT) || has_auth(SYSTEMACCOUNT) || - has_auth(FeeContract) || has_auth(FIOORACLEContract) || has_auth(EscrowContract)), - "missing required authority of fio.address, fio.treasury, fio.fee, fio.token, fio.staking, fio.oracle, fio.escrow, eosio or fio.reqobt"); + has_auth(FeeContract) || has_auth(FIOORACLEContract) || has_auth(EscrowContract) || + has_auth(PERMSACCOUNT) ), + "missing required authority of fio.address, fio.treasury, fio.fee, fio.token, fio.staking, fio.oracle, fio.escrow, eosio, fio.perms or fio.reqobt"); const uint32_t present_time = now(); gstaking.rewards_token_pool += fioamountsufs; diff --git a/contracts/fio.system/include/fio.system/native.hpp b/contracts/fio.system/include/fio.system/native.hpp index f2da5032..6dca3d1e 100755 --- a/contracts/fio.system/include/fio.system/native.hpp +++ b/contracts/fio.system/include/fio.system/native.hpp @@ -150,7 +150,8 @@ namespace eosiosystem { account == fioio::STAKINGACCOUNT || account == fioio::FIOACCOUNT || account == fioio::FIOORACLEContract || - account == fioio::FIOACCOUNT) + account == fioio::FIOACCOUNT || + account == fioio::PERMSACCOUNT) ) { //get the sizes of the tx. diff --git a/contracts/fio.system/src/delegate_bandwidth.cpp b/contracts/fio.system/src/delegate_bandwidth.cpp index 20c12e41..d01e1346 100755 --- a/contracts/fio.system/src/delegate_bandwidth.cpp +++ b/contracts/fio.system/src/delegate_bandwidth.cpp @@ -61,9 +61,10 @@ namespace eosiosystem { has_auth(SYSTEMACCOUNT) || has_auth(FeeContract) || has_auth(StakingContract) || - has_auth(REQOBTACCOUNT) + //FIP-40 + has_auth(PERMSACCOUNT) ), - "missing required authority of fio.address, fio.treasury, eosio, fio.fee, fio.token, fio.staking, or fio.reqobt"); + "missing required authority of fio.address, fio.treasury, eosio, fio.fee, fio.token, fio.staking, fio.perms or fio.reqobt"); auto votersbyowner = _voters.get_index<"byowner"_n>(); auto voter_itr = votersbyowner.find(voter.value); diff --git a/contracts/fio.system/src/fio.system.cpp b/contracts/fio.system/src/fio.system.cpp index 6e7ea9b1..7b3835d8 100755 --- a/contracts/fio.system/src/fio.system.cpp +++ b/contracts/fio.system/src/fio.system.cpp @@ -304,7 +304,8 @@ namespace eosiosystem { acnt == FIOSYSTEMACCOUNT || acnt == EscrowContract || acnt == FIOORACLEContract || - acnt == FIOACCOUNT),"set abi not permitted." ); + acnt == FIOACCOUNT || + acnt == PERMSACCOUNT),"set abi not permitted." ); eosio::multi_index<"abihash"_n, abi_hash> table(_self, _self.value); @@ -426,8 +427,9 @@ namespace eosiosystem { eosio_assert((has_auth(AddressContract) || has_auth(TokenContract) || has_auth(TREASURYACCOUNT) || has_auth(STAKINGACCOUNT) || has_auth(REQOBTACCOUNT) || has_auth(SYSTEMACCOUNT) || - has_auth(FIOORACLEContract) || has_auth(FeeContract) || has_auth(EscrowContract)), - "missing required authority of fio.address, fio.token, fio.fee, fio.treasury, fio.oracle, fio.escrow, fio.staking, or fio.reqobt"); + has_auth(FIOORACLEContract) || has_auth(FeeContract) || has_auth(EscrowContract) || + has_auth(PERMSACCOUNT)), + "missing required authority of fio.address, fio.token, fio.fee, fio.treasury, fio.oracle, fio.escrow, fio.staking, fio.perms or fio.reqobt"); check(is_account(owner), "account must pre exist"); auto locks_by_owner = _generallockedtokens.get_index<"byowner"_n>(); auto lockiter = locks_by_owner.find(owner.value); diff --git a/contracts/fio.token/src/fio.token.cpp b/contracts/fio.token/src/fio.token.cpp index 58f9049c..55312f3b 100755 --- a/contracts/fio.token/src/fio.token.cpp +++ b/contracts/fio.token/src/fio.token.cpp @@ -422,7 +422,8 @@ namespace eosio { * we permit the use of transfer from the treasury account to any other accounts. * we permit the use of transfer from any other accounts to the treasury account for fees. */ - if (from != SYSTEMACCOUNT && from != TREASURYACCOUNT && from != EscrowContract && from != FIOORACLEContract) { + if (from != SYSTEMACCOUNT && from != TREASURYACCOUNT && from != EscrowContract + && from != FIOORACLEContract) { if(!has_auth(EscrowContract) && !has_auth(FIOORACLEContract)){ check(to == TREASURYACCOUNT, "transfer not allowed"); } diff --git a/contracts/fio.tpid/fio.tpid.cpp b/contracts/fio.tpid/fio.tpid.cpp index a422f00c..7f88affb 100644 --- a/contracts/fio.tpid/fio.tpid.cpp +++ b/contracts/fio.tpid/fio.tpid.cpp @@ -94,9 +94,10 @@ class [[eosio::contract("TPIDController")]] TPIDController: public eosio::contr [[eosio::action]] void updatetpid(const string &tpid, const name owner, const uint64_t &amount) { - eosio_assert(has_auth(AddressContract) || has_auth(TokenContract) || has_auth(TREASURYACCOUNT) || has_auth(FIOORACLEContract) || - has_auth(STAKINGACCOUNT) || has_auth("fio.reqobt"_n) || has_auth("eosio"_n) || has_auth(EscrowContract), - "missing required authority of fio.address, fio.treasury, fio.token, eosio or fio.reqobt or fio.staking"); + eosio_assert(has_auth(AddressContract) || has_auth(TokenContract) || has_auth(TREASURYACCOUNT) + || has_auth(FIOORACLEContract) || has_auth(STAKINGACCOUNT) || has_auth("fio.reqobt"_n) + || has_auth("eosio"_n) || has_auth(EscrowContract) || has_auth(PERMSACCOUNT), + "missing required authority of fio.address, fio.treasury, fio.token, eosio or fio.reqobt fio.perms or fio.staking"); if (debugout) { print("update tpid calling updatetpid with tpid ", tpid, " owner ", owner, "\n"); } diff --git a/contracts/fio.treasury/fio.treasury.cpp b/contracts/fio.treasury/fio.treasury.cpp index f549d8c0..13ca1e87 100644 --- a/contracts/fio.treasury/fio.treasury.cpp +++ b/contracts/fio.treasury/fio.treasury.cpp @@ -408,17 +408,20 @@ class [[eosio::contract("FIOTreasury")]] FIOTreasury: public eosio::contract { // @abi action [[eosio::action]] void bppoolupdate(const uint64_t &amount) { - eosio_assert((has_auth(SYSTEMACCOUNT) || has_auth(AddressContract) || has_auth(TokenContract) || has_auth(TREASURYACCOUNT) || - has_auth(REQOBTACCOUNT) || has_auth(FIOORACLEContract) || has_auth(EscrowContract)), - "missing required authority of eosio, fio.address, fio.treasury, fio.token, fio.oracle or fio.reqobt"); + eosio_assert((has_auth(SYSTEMACCOUNT) || has_auth(AddressContract) || has_auth(TokenContract) + || has_auth(TREASURYACCOUNT) || has_auth(REQOBTACCOUNT) || has_auth(FIOORACLEContract) + || has_auth(EscrowContract) || has_auth(PERMSACCOUNT)), + "missing required authority of eosio, fio.address, fio.treasury, fio.token, fio.oracle fio.perms or fio.reqobt"); bucketrewards.set(bucketrewards.exists() ? bucketpool{bucketrewards.get().rewards + amount} : bucketpool{amount}, get_self()); } // @abi action [[eosio::action]] void fdtnrwdupdat(const uint64_t &amount) { - eosio_assert((has_auth(AddressContract) || has_auth(TokenContract) || has_auth(StakingContract) || has_auth(TREASURYACCOUNT) || has_auth(REQOBTACCOUNT) || has_auth(SYSTEMACCOUNT) || has_auth(FeeContract) || has_auth(FIOORACLEContract) || has_auth(EscrowContract)), - "missing required authority of fio.address, fio.token, fio.staking, fio.fee, fio.treasury, fio.oracle or fio.reqobt"); + eosio_assert((has_auth(AddressContract) || has_auth(TokenContract) || has_auth(StakingContract) || has_auth(TREASURYACCOUNT) + || has_auth(REQOBTACCOUNT) || has_auth(SYSTEMACCOUNT) || has_auth(FeeContract) || has_auth(FIOORACLEContract) + || has_auth(EscrowContract) || has_auth(PERMSACCOUNT)), + "missing required authority of fio.address, fio.token, fio.staking, fio.fee, fio.treasury, fio.oracle fio.perms or fio.reqobt"); fdtnrewards.set(fdtnrewards.exists() ? fdtnreward{fdtnrewards.get().rewards + amount} : fdtnreward{amount}, get_self()); } From a0aa1ca4268b465258c338bfac7fde49210ddd75 Mon Sep 17 00:00:00 2001 From: Ed Rotthoff Date: Thu, 23 Mar 2023 12:48:55 -0600 Subject: [PATCH 03/15] FIP-40 flush out error handling for addperm FIP-40 flush out error handling for add perm --- contracts/fio.common/fioerror.hpp | 1 + contracts/fio.perms/fio.perms.cpp | 69 ++++++++++++++++++++++++++++--- contracts/fio.perms/fio.perms.hpp | 2 + 3 files changed, 67 insertions(+), 5 deletions(-) diff --git a/contracts/fio.common/fioerror.hpp b/contracts/fio.common/fioerror.hpp index 85e90a56..a219bf8c 100644 --- a/contracts/fio.common/fioerror.hpp +++ b/contracts/fio.common/fioerror.hpp @@ -102,6 +102,7 @@ namespace fioio { constexpr auto ErrorInvalidPermissionInfo = ident | httpDataError | 163; constexpr auto ErrorInvalidObjectName = ident | httpDataError | 164; constexpr auto ErrorInvalidGranteeAccount = ident | httpDataError | 165; + constexpr auto ErrorPermissionExists = ident | httpDataError | 166; /** * Helper funtions for detecting rich error messages and extracting bitfielded values diff --git a/contracts/fio.perms/fio.perms.cpp b/contracts/fio.perms/fio.perms.cpp index 493a019d..3bb55614 100644 --- a/contracts/fio.perms/fio.perms.cpp +++ b/contracts/fio.perms/fio.perms.cpp @@ -60,6 +60,7 @@ namespace fioio { config appConfig; permissions_table permissions; + access_table access; @@ -68,7 +69,8 @@ namespace fioio { FioPermissions(name s, name code, datastream ds) : contract(s, code, ds), permissions(_self,_self.value), - domains(_self, _self.value), + access(_self,_self.value), + domains(_self, _self.value), fionames(_self, _self.value), fiofees(FeeContract, FeeContract.value), accountmap(_self, _self.value){ @@ -92,9 +94,10 @@ namespace fioio { print("addperm -- called. \n"); + require_auth(actor); // error if permission name is not the expected name register_address_on_domain. - fio_400_assert(permission_name.compare("register_address_on_domain") == 0, "permission_name", permission_name, + fio_400_assert(permission_name.compare(REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME) == 0, "permission_name", permission_name, "Permission name is invalid", ErrorInvalidPermissionName); // error if permission info is not empty. fio_400_assert(permission_info.size() == 0, "permission_info", permission_info, @@ -102,11 +105,66 @@ namespace fioio { // error if object name is not * or is not in the domains table fio_400_assert(object_name.size() > 0, "object_name", object_name, "Object name is invalid", ErrorInvalidObjectName); - // TODO error if the object name is not a domain owned by the actor. - // error if the grantee account does not exist. + + //verify domain name, and that domain is owned by the actor account. + FioAddress fa; + getFioAddressStruct(object_name, fa); + + fio_400_assert(fa.domainOnly, "object_name", object_name, "Invalid object name", + ErrorInvalidObjectName); + + const uint128_t domainHash = string_to_uint128_hash(fa.fiodomain.c_str()); + + auto domainsbyname = domains.get_index<"byname"_n>(); + auto domains_iter = domainsbyname.find(domainHash); + + fio_400_assert(domains_iter != domainsbyname.end(), "object_name", object_name, + "Invalid object name", + ErrorInvalidObjectName); + + //add 30 days to the domain expiration, this call will work until 30 days past expire. + const uint32_t domain_expiration = get_time_plus_seconds(domains_iter->expiration, SECONDS30DAYS); + + const uint32_t present_time = now(); + fio_400_assert(present_time <= domain_expiration, "object_name", object_name, "Invalid object name", + ErrorInvalidObjectName); + + //check domain owner is actor + fio_400_assert((actor.value == domains_iter->account), "object_name", object_name, + "Invalid object name", ErrorInvalidObjectName); + + + //check grantee exists. fio_400_assert(is_account(grantee_account), "grantee_account", grantee_account.to_string(), "grantee account is invalid", ErrorInvalidGranteeAccount); - // TODO error if the grantee account already has this permission. + + + //error if the grantee account already has this permission. + string permcontrol = REGISTER_ADDRESS_ON_DOMAIN_OBJECT_TYPE + object_name + REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME; + + const uint128_t permcontrolHash = string_to_uint128_hash(permcontrol.c_str()); + + auto permissionsbycontrolhash = permissions.get_index<"bypermctrl"_n>(); + auto permctrl_iter = permissionsbycontrolhash.find(permcontrolHash); + if (permctrl_iter != permissionsbycontrolhash.end() ){ + //get the id and look in access + uint64_t permid = permctrl_iter->id; + string accessctrl = grantee_account.to_string() + to_string(permid); + + const uint128_t accessHash = string_to_uint128_hash(accessctrl.c_str()); + + auto accessbyhash = access.get_index<"byaccess"_n>(); + auto access_iter = accessbyhash.find(accessHash); + + fio_400_assert((access_iter == accessbyhash.end() ), "grantee_account", grantee_account.to_string(), + "Permission already exists", ErrorPermissionExists); + + + } + + // fio_400_assert((permctrl == permissionsbycontrolhash.end() ), "grantee_account", grantee_account.to_string(), + // "Permission already exists", ErrorPermissionExists); + //look for the permission in the permissions table. if it exists, verify all info, then note the id. @@ -156,6 +214,7 @@ namespace fioio { print("remperm -- called. \n"); + require_auth(actor); uint128_t permnamehash = string_to_uint128_hash(permission_name); auto permsbypermname = permissions.get_index<"bypermname"_n>(); diff --git a/contracts/fio.perms/fio.perms.hpp b/contracts/fio.perms/fio.perms.hpp index c6ff1a44..ce96bc81 100644 --- a/contracts/fio.perms/fio.perms.hpp +++ b/contracts/fio.perms/fio.perms.hpp @@ -19,6 +19,8 @@ namespace fioio { using namespace eosio; + const static string REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME = "register_address_on_domain"; + const static string REGISTER_ADDRESS_ON_DOMAIN_OBJECT_TYPE = "domain"; struct [[eosio::action]] permission_info { From 5ec0347217aa84ff178b059f75aa9eb0f1cfcabe Mon Sep 17 00:00:00 2001 From: Ed Rotthoff Date: Wed, 29 Mar 2023 14:30:54 -0600 Subject: [PATCH 04/15] FIP-40 implement parameter validation and tests FIP-40 implement parameter validation and tests --- contracts/fio.perms/fio.perms.cpp | 140 ++++++++++++++++++++++++++---- 1 file changed, 125 insertions(+), 15 deletions(-) diff --git a/contracts/fio.perms/fio.perms.cpp b/contracts/fio.perms/fio.perms.cpp index 3bb55614..5f4f4b2d 100644 --- a/contracts/fio.perms/fio.perms.cpp +++ b/contracts/fio.perms/fio.perms.cpp @@ -70,8 +70,8 @@ namespace fioio { FioPermissions(name s, name code, datastream ds) : contract(s, code, ds), permissions(_self,_self.value), access(_self,_self.value), - domains(_self, _self.value), - fionames(_self, _self.value), + domains(AddressContract, AddressContract.value), + fionames(AddressContract, AddressContract.value), fiofees(FeeContract, FeeContract.value), accountmap(_self, _self.value){ @@ -95,10 +95,15 @@ namespace fioio { print("addperm -- called. \n"); require_auth(actor); + string useperm = makeLowerCase(permission_name); - // error if permission name is not the expected name register_address_on_domain. - fio_400_assert(permission_name.compare(REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME) == 0, "permission_name", permission_name, + + fio_400_assert(permission_name.length() > 0, "permission_name", permission_name, + "Permission name is invalid", ErrorInvalidPermissionName); + // error if permission name is not the expected name register_address_on_domain. + fio_400_assert(useperm.compare(REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME) == 0, "permission_name", permission_name, "Permission name is invalid", ErrorInvalidPermissionName); + // error if permission info is not empty. fio_400_assert(permission_info.size() == 0, "permission_info", permission_info, "Permission info is invalid", ErrorInvalidPermissionInfo); @@ -110,15 +115,14 @@ namespace fioio { FioAddress fa; getFioAddressStruct(object_name, fa); - fio_400_assert(fa.domainOnly, "object_name", object_name, "Invalid object name", + fio_400_assert(fa.domainOnly, "domonlyobject_name", object_name, "Invalid object name", ErrorInvalidObjectName); const uint128_t domainHash = string_to_uint128_hash(fa.fiodomain.c_str()); - auto domainsbyname = domains.get_index<"byname"_n>(); auto domains_iter = domainsbyname.find(domainHash); - fio_400_assert(domains_iter != domainsbyname.end(), "object_name", object_name, + fio_400_assert(domains_iter != domainsbyname.end(), "domnotfound object_name", object_name, "Invalid object name", ErrorInvalidObjectName); @@ -126,11 +130,11 @@ namespace fioio { const uint32_t domain_expiration = get_time_plus_seconds(domains_iter->expiration, SECONDS30DAYS); const uint32_t present_time = now(); - fio_400_assert(present_time <= domain_expiration, "object_name", object_name, "Invalid object name", + fio_400_assert(present_time <= domain_expiration, "domexpired object_name", object_name, "Invalid object name", ErrorInvalidObjectName); //check domain owner is actor - fio_400_assert((actor.value == domains_iter->account), "object_name", object_name, + fio_400_assert((actor.value == domains_iter->account), "actornotowner object_name", object_name, "Invalid object name", ErrorInvalidObjectName); @@ -138,8 +142,11 @@ namespace fioio { fio_400_assert(is_account(grantee_account), "grantee_account", grantee_account.to_string(), "grantee account is invalid", ErrorInvalidGranteeAccount); + fio_400_assert((grantee_account.value != actor.value), "grantee_account", grantee_account.to_string(), + "grantee account is invalid", ErrorInvalidGranteeAccount); + - //error if the grantee account already has this permission. + //error if the grantee account already has this permission. string permcontrol = REGISTER_ADDRESS_ON_DOMAIN_OBJECT_TYPE + object_name + REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME; const uint128_t permcontrolHash = string_to_uint128_hash(permcontrol.c_str()); @@ -162,6 +169,12 @@ namespace fioio { } + fio_400_assert(max_fee >= 0, "max_fee", to_string(max_fee), "Invalid fee value", + ErrorMaxFeeInvalid); + fio_400_assert(validateTPIDFormat(tpid), "tpid", tpid, + "TPID must be empty or valid FIO address", + ErrorPubKeyValid); + // fio_400_assert((permctrl == permissionsbycontrolhash.end() ), "grantee_account", grantee_account.to_string(), // "Permission already exists", ErrorPermissionExists); @@ -177,7 +190,7 @@ namespace fioio { /* - const string controlv = "stuff" + object_name + permission_name; + const string controlv = "stuff" + object_name + useperm; const uint64_t id = permissions.available_primary_key(); //just blindly emplace for prototyping. @@ -187,8 +200,8 @@ namespace fioio { p.object_type_hash = string_to_uint128_hash("stuff"); p.object_name = object_name; p.object_name_hash = string_to_uint128_hash(object_name); - p.permission_name = permission_name; - p.permission_name_hash = string_to_uint128_hash(permission_name); + p.permission_name = userperm; + p.permission_name_hash = string_to_uint128_hash(useperm); p.permission_control_hash = string_to_uint128_hash(controlv); p.owner_account = actor.value; p.auxilliary_info = ""; @@ -216,13 +229,110 @@ namespace fioio { require_auth(actor); - uint128_t permnamehash = string_to_uint128_hash(permission_name); + + fio_400_assert(permission_name.length() > 0, "permission_name", permission_name, + "Permission name is invalid", ErrorInvalidPermissionName); + + string useperm = makeLowerCase(permission_name); + + // error if permission name is not the expected name register_address_on_domain. + fio_400_assert(useperm.compare(REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME) == 0, "permission_name", permission_name, + "Permission name is invalid", ErrorInvalidPermissionName); + + // error if object name is not * or is not in the domains table + fio_400_assert(object_name.size() > 0, "object_name", object_name, + "Object name is invalid", ErrorInvalidObjectName); + + //verify domain name, and that domain is owned by the actor account. + FioAddress fa; + getFioAddressStruct(object_name, fa); + + fio_400_assert(fa.domainOnly, "object_name", object_name, "Invalid object name", + ErrorInvalidObjectName); + + const uint128_t domainHash = string_to_uint128_hash(fa.fiodomain.c_str()); + + auto domainsbyname = domains.get_index<"byname"_n>(); + auto domains_iter = domainsbyname.find(domainHash); + + fio_400_assert(domains_iter != domainsbyname.end(), "object_name", object_name, + "Invalid object name", + ErrorInvalidObjectName); + + //add 30 days to the domain expiration, this call will work until 30 days past expire. + const uint32_t domain_expiration = get_time_plus_seconds(domains_iter->expiration, SECONDS30DAYS); + + const uint32_t present_time = now(); + fio_400_assert(present_time <= domain_expiration, "object_name", object_name, "Invalid object name", + ErrorInvalidObjectName); + + //check domain owner is actor + fio_400_assert((actor.value == domains_iter->account), "object_name", object_name, + "Invalid object name", ErrorInvalidObjectName); + + + //check grantee exists. + fio_400_assert(is_account(grantee_account), "grantee_account", grantee_account.to_string(), + "grantee account is invalid", ErrorInvalidGranteeAccount); + + fio_400_assert((grantee_account.value != actor.value), "grantee_account", grantee_account.to_string(), + "grantee account is invalid", ErrorInvalidGranteeAccount); + + + //error if the grantee account already has this permission. + string permcontrol = REGISTER_ADDRESS_ON_DOMAIN_OBJECT_TYPE + object_name + REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME; + + const uint128_t permcontrolHash = string_to_uint128_hash(permcontrol.c_str()); + + auto permissionsbycontrolhash = permissions.get_index<"bypermctrl"_n>(); + auto permctrl_iter = permissionsbycontrolhash.find(permcontrolHash); + if (permctrl_iter != permissionsbycontrolhash.end() ){ + //get the id and look in access, + // todo and remove it. + //todo do one more check for this permission by perm id, if no results then + //todo remove from permissions. + uint64_t permid = permctrl_iter->id; + string accessctrl = grantee_account.to_string() + to_string(permid); + + const uint128_t accessHash = string_to_uint128_hash(accessctrl.c_str()); + + auto accessbyhash = access.get_index<"byaccess"_n>(); + auto access_iter = accessbyhash.find(accessHash); + + fio_400_assert((access_iter != accessbyhash.end() ), "grantee_account", grantee_account.to_string(), + "Permission not found", ErrorPermissionExists); + + + } + + fio_400_assert(max_fee >= 0, "max_fee", to_string(max_fee), "Invalid fee value", + ErrorMaxFeeInvalid); + fio_400_assert(validateTPIDFormat(tpid), "tpid", tpid, + "TPID must be empty or valid FIO address", + ErrorPubKeyValid); + + + + + + + + + + + + + + + + + uint128_t permnamehash = string_to_uint128_hash(useperm); auto permsbypermname = permissions.get_index<"bypermname"_n>(); auto bynameiter = permsbypermname.find(permnamehash); int c = 0; while (bynameiter != permsbypermname.end()) { bynameiter++; - if (permission_name.compare(bynameiter->permission_name)==0) { + if (useperm.compare(bynameiter->permission_name)==0) { c++; } } From 39385d8e2de41c512f39c552f33f39268c242ba5 Mon Sep 17 00:00:00 2001 From: Ed Rotthoff Date: Thu, 30 Mar 2023 12:18:45 -0600 Subject: [PATCH 05/15] FIP-40 implement action logic add and remove FIP-40 implement action logic for add and remove permission --- contracts/fio.common/fio.common.hpp | 7 ++ contracts/fio.perms/fio.perms.cpp | 156 +++++++++++++++++----------- 2 files changed, 102 insertions(+), 61 deletions(-) diff --git a/contracts/fio.common/fio.common.hpp b/contracts/fio.common/fio.common.hpp index 38e8eb23..b76f60dc 100644 --- a/contracts/fio.common/fio.common.hpp +++ b/contracts/fio.common/fio.common.hpp @@ -80,6 +80,10 @@ #define ADD_NFT_ENDPOINT "add_nft" #define REM_NFT_ENDPOINT "remove_nft" #define REM_ALL_NFTS_ENDPOINT "remove_all_nfts" +//FIP-40 +#define ADD_PERMISSION_ENDPOINT "add_fio_permission" +#define REMOVE_PERMISSION_ENDPOINT "remove_fio_permission" +#define PERMISSION_OBJECT_TYPE_DOMAIN "domain" //FIP-38 begin #define NEW_FIO_CHAIN_ACCOUNT_ENDPOINT "new_fio_chain_account" //FIP-38 end @@ -516,6 +520,9 @@ namespace fioio { static const uint64_t BUNDLEVOTERAM = 0; //integrated. static const uint64_t ADDNFTRAMBASE = 512; static const uint64_t ADDNFTRAM = 2048; + //FIP-40 + static const uint64_t ADDPERMISSIONRAMBASE = 2560; + static const uint64_t ADDPERMISSIONRAM = 2048; static const uint64_t LISTDOMAINRAM = 1536; // FIOESCROW - List Domain 1140 bytes round to 512 x 3 //FIP-38 begin static const uint64_t NEWFIOCHAINACCOUNTRAM = 0; diff --git a/contracts/fio.perms/fio.perms.cpp b/contracts/fio.perms/fio.perms.cpp index 5f4f4b2d..0b3725c7 100644 --- a/contracts/fio.perms/fio.perms.cpp +++ b/contracts/fio.perms/fio.perms.cpp @@ -101,6 +101,8 @@ namespace fioio { fio_400_assert(permission_name.length() > 0, "permission_name", permission_name, "Permission name is invalid", ErrorInvalidPermissionName); // error if permission name is not the expected name register_address_on_domain. + //one permission name is integrated for fip 40, modify this logic for any new permission names + //being supported fio_400_assert(useperm.compare(REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME) == 0, "permission_name", permission_name, "Permission name is invalid", ErrorInvalidPermissionName); @@ -167,6 +169,26 @@ namespace fioio { "Permission already exists", ErrorPermissionExists); + } else { //insert the permission + //one permission name is integrated for fip 40, modify this logic for any new permission names + //being supported + string object_type = PERMISSION_OBJECT_TYPE_DOMAIN; + const string controlv = object_type + object_name + useperm; + + const uint64_t id = permissions.available_primary_key(); + + permissions.emplace(get_self(), [&](struct permission_info &p) { + p.id = id; + p.object_type = PERMISSION_OBJECT_TYPE_DOMAIN; + p.object_type_hash = string_to_uint128_hash(PERMISSION_OBJECT_TYPE_DOMAIN); + p.object_name = object_name; + p.object_name_hash = string_to_uint128_hash(object_name); + p.permission_name = useperm; + p.permission_name_hash = string_to_uint128_hash(useperm); + p.permission_control_hash = string_to_uint128_hash(controlv); + p.owner_account = actor.value; + p.auxilliary_info = ""; + }); } fio_400_assert(max_fee >= 0, "max_fee", to_string(max_fee), "Invalid fee value", @@ -175,40 +197,47 @@ namespace fioio { "TPID must be empty or valid FIO address", ErrorPubKeyValid); - // fio_400_assert((permctrl == permissionsbycontrolhash.end() ), "grantee_account", grantee_account.to_string(), - // "Permission already exists", ErrorPermissionExists); + //fees + const uint128_t endpoint_hash = string_to_uint128_hash(ADD_PERMISSION_ENDPOINT); + auto fees_by_endpoint = fiofees.get_index<"byendpoint"_n>(); + auto fee_iter = fees_by_endpoint.find(endpoint_hash); + fio_400_assert(fee_iter != fees_by_endpoint.end(), "endpoint_name", ADD_PERMISSION_ENDPOINT, + "FIO fee not found for endpoint", ErrorNoEndpoint); + const uint64_t fee_amount = fee_iter->suf_amount; + const uint64_t fee_type = fee_iter->type; + fio_400_assert(fee_type == 0, "fee_type", to_string(fee_type), + "unexpected fee type for endpoint transfer_fio_domain, expected 0", + ErrorNoEndpoint); - //look for the permission in the permissions table. if it exists, verify all info, then note the id. - //if it does not exist then insert it, and note the id. - //look for an existing entry in the access table for this id, and grantee account. - //if the entry exists then error permission exists for this account. - //if it doesnt exist then add the record to the access table. + fio_400_assert(max_fee >= (int64_t) fee_amount, "max_fee", to_string(max_fee), + "Fee exceeds supplied maximum.", + ErrorMaxFeeExceeded); + fio_fees(actor, asset(fee_amount, FIOSYMBOL), ADD_PERMISSION_ENDPOINT); + processbucketrewards(tpid, fee_amount, get_self(), actor); + if (fee_amount > 0) { + INLINE_ACTION_SENDER(eosiosystem::system_contract, updatepower) + (SYSTEMACCOUNT, {{_self, "active"_n}}, + {actor, true} + ); + } + //ram bump + if (ADDPERMISSIONRAM > 0) { + action( + permission_level{SYSTEMACCOUNT, "active"_n}, + "eosio"_n, + "incram"_n, + std::make_tuple(actor, ADDPERMISSIONRAMBASE + (ADDPERMISSIONRAM * permission_info.size())) + ).send(); + } - /* - const string controlv = "stuff" + object_name + useperm; - const uint64_t id = permissions.available_primary_key(); - //just blindly emplace for prototyping. - permissions.emplace(get_self(), [&](struct permission_info &p) { - p.id = id; - p.object_type = "stuff"; - p.object_type_hash = string_to_uint128_hash("stuff"); - p.object_name = object_name; - p.object_name_hash = string_to_uint128_hash(object_name); - p.permission_name = userperm; - p.permission_name_hash = string_to_uint128_hash(useperm); - p.permission_control_hash = string_to_uint128_hash(controlv); - p.owner_account = actor.value; - p.auxilliary_info = ""; - }); - */ - const string response_string = "{\"status\": \"OK\", \"fee_collected\" : 0}"; + const string response_string = "{\"status\": \"OK\", \"fee_collected\" : "+ to_string(fee_amount) +"}"; send_response(response_string.c_str()); @@ -251,9 +280,8 @@ namespace fioio { ErrorInvalidObjectName); const uint128_t domainHash = string_to_uint128_hash(fa.fiodomain.c_str()); - - auto domainsbyname = domains.get_index<"byname"_n>(); - auto domains_iter = domainsbyname.find(domainHash); + auto domainsbyname = domains.get_index<"byname"_n>(); + auto domains_iter = domainsbyname.find(domainHash); fio_400_assert(domains_iter != domainsbyname.end(), "object_name", object_name, "Invalid object name", @@ -261,8 +289,8 @@ namespace fioio { //add 30 days to the domain expiration, this call will work until 30 days past expire. const uint32_t domain_expiration = get_time_plus_seconds(domains_iter->expiration, SECONDS30DAYS); + const uint32_t present_time = now(); - const uint32_t present_time = now(); fio_400_assert(present_time <= domain_expiration, "object_name", object_name, "Invalid object name", ErrorInvalidObjectName); @@ -287,22 +315,27 @@ namespace fioio { auto permissionsbycontrolhash = permissions.get_index<"bypermctrl"_n>(); auto permctrl_iter = permissionsbycontrolhash.find(permcontrolHash); if (permctrl_iter != permissionsbycontrolhash.end() ){ - //get the id and look in access, - // todo and remove it. - //todo do one more check for this permission by perm id, if no results then - //todo remove from permissions. - uint64_t permid = permctrl_iter->id; - string accessctrl = grantee_account.to_string() + to_string(permid); - - const uint128_t accessHash = string_to_uint128_hash(accessctrl.c_str()); - - auto accessbyhash = access.get_index<"byaccess"_n>(); - auto access_iter = accessbyhash.find(accessHash); + //get the id and look in access, remove it if its there, error if not there + uint64_t permid = permctrl_iter->id; + string accessctrl = grantee_account.to_string() + to_string(permid); + const uint128_t accessHash = string_to_uint128_hash(accessctrl.c_str()); + auto accessbyhash = access.get_index<"byaccess"_n>(); + auto access_iter = accessbyhash.find(accessHash); fio_400_assert((access_iter != accessbyhash.end() ), "grantee_account", grantee_account.to_string(), "Permission not found", ErrorPermissionExists); + accessbyhash.erase(access_iter); + //do one more check for this access by permission id, if no results then + //remove from permissions. + auto accessbypermid = access.get_index<"bypermid"_n>(); + auto accessbyperm_iter = accessbypermid.find(permid); + + if(accessbyperm_iter == accessbypermid.end()){ + //no accounts with this access left, remove the permission. + permissionsbycontrolhash.erase(permctrl_iter); + } } fio_400_assert(max_fee >= 0, "max_fee", to_string(max_fee), "Invalid fee value", @@ -313,34 +346,35 @@ namespace fioio { + //fees + const uint128_t endpoint_hash = string_to_uint128_hash(REMOVE_PERMISSION_ENDPOINT); + auto fees_by_endpoint = fiofees.get_index<"byendpoint"_n>(); + auto fee_iter = fees_by_endpoint.find(endpoint_hash); + fio_400_assert(fee_iter != fees_by_endpoint.end(), "endpoint_name", REMOVE_PERMISSION_ENDPOINT, + "FIO fee not found for endpoint", ErrorNoEndpoint); + const uint64_t fee_amount = fee_iter->suf_amount; + const uint64_t fee_type = fee_iter->type; + fio_400_assert(fee_type == 0, "fee_type", to_string(fee_type), + "unexpected fee type for endpoint remove permission, expected 0", + ErrorNoEndpoint); + fio_400_assert(max_fee >= (int64_t) fee_amount, "max_fee", to_string(max_fee), + "Fee exceeds supplied maximum.", + ErrorMaxFeeExceeded); + fio_fees(actor, asset(fee_amount, FIOSYMBOL), REMOVE_PERMISSION_ENDPOINT); + processbucketrewards(tpid, fee_amount, get_self(), actor); - - - - - - - - - - uint128_t permnamehash = string_to_uint128_hash(useperm); - auto permsbypermname = permissions.get_index<"bypermname"_n>(); - auto bynameiter = permsbypermname.find(permnamehash); - int c = 0; - while (bynameiter != permsbypermname.end()) { - bynameiter++; - if (useperm.compare(bynameiter->permission_name)==0) { - c++; - } + if (fee_amount > 0) { + INLINE_ACTION_SENDER(eosiosystem::system_contract, updatepower) + (SYSTEMACCOUNT, {{_self, "active"_n}}, + {actor, true} + ); } - print("remperm -- there are this many rows with name of. "+permission_name+ " count : "+to_string(c)+" \n"); - - const string response_string = "{\"status\": \"OK\", \"fee_collected\" : 0}"; + const string response_string = "{\"status\": \"OK\", \"fee_collected\" : "+ to_string(fee_amount) +"}"; send_response(response_string.c_str()); } From a7952e167f9448195c14a86022e14ef206ea2410 Mon Sep 17 00:00:00 2001 From: Ed Rotthoff Date: Fri, 31 Mar 2023 12:52:55 -0600 Subject: [PATCH 06/15] FIP-40 change name of table access to accesses FIP-40 table name change to plural --- contracts/fio.perms/fio.perms.abi | 10 +++-- contracts/fio.perms/fio.perms.cpp | 61 +++++++++++++++++++------------ contracts/fio.perms/fio.perms.hpp | 2 +- 3 files changed, 46 insertions(+), 27 deletions(-) diff --git a/contracts/fio.perms/fio.perms.abi b/contracts/fio.perms/fio.perms.abi index 38bf7d7f..c7772042 100644 --- a/contracts/fio.perms/fio.perms.abi +++ b/contracts/fio.perms/fio.perms.abi @@ -160,10 +160,14 @@ "type": "permission_info" }, { - "name": "access", + "name": "accesses", "index_type": "i64", - "key_names": [], - "key_types": [], + "key_names": [ + "id" + ], + "key_types": [ + "string" + ], "type": "access_info" } ], diff --git a/contracts/fio.perms/fio.perms.cpp b/contracts/fio.perms/fio.perms.cpp index 0b3725c7..c8f9a6f5 100644 --- a/contracts/fio.perms/fio.perms.cpp +++ b/contracts/fio.perms/fio.perms.cpp @@ -60,7 +60,7 @@ namespace fioio { config appConfig; permissions_table permissions; - access_table access; + access_table accesses; @@ -69,7 +69,7 @@ namespace fioio { FioPermissions(name s, name code, datastream ds) : contract(s, code, ds), permissions(_self,_self.value), - access(_self,_self.value), + accesses(_self,_self.value), domains(AddressContract, AddressContract.value), fionames(AddressContract, AddressContract.value), fiofees(FeeContract, FeeContract.value), @@ -152,33 +152,22 @@ namespace fioio { string permcontrol = REGISTER_ADDRESS_ON_DOMAIN_OBJECT_TYPE + object_name + REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME; const uint128_t permcontrolHash = string_to_uint128_hash(permcontrol.c_str()); + auto accessbyhash = accesses.get_index<"byaccess"_n>(); + auto permissionsbycontrolhash = permissions.get_index<"bypermctrl"_n>(); + auto permctrl_iter = permissionsbycontrolhash.find(permcontrolHash); + uint64_t permid = 0; - auto permissionsbycontrolhash = permissions.get_index<"bypermctrl"_n>(); - auto permctrl_iter = permissionsbycontrolhash.find(permcontrolHash); - if (permctrl_iter != permissionsbycontrolhash.end() ){ - //get the id and look in access - uint64_t permid = permctrl_iter->id; - string accessctrl = grantee_account.to_string() + to_string(permid); - - const uint128_t accessHash = string_to_uint128_hash(accessctrl.c_str()); - - auto accessbyhash = access.get_index<"byaccess"_n>(); - auto access_iter = accessbyhash.find(accessHash); - - fio_400_assert((access_iter == accessbyhash.end() ), "grantee_account", grantee_account.to_string(), - "Permission already exists", ErrorPermissionExists); - - - } else { //insert the permission + if(permctrl_iter == permissionsbycontrolhash.end()) + { //insert the permission //one permission name is integrated for fip 40, modify this logic for any new permission names //being supported string object_type = PERMISSION_OBJECT_TYPE_DOMAIN; const string controlv = object_type + object_name + useperm; - const uint64_t id = permissions.available_primary_key(); + permid = permissions.available_primary_key(); permissions.emplace(get_self(), [&](struct permission_info &p) { - p.id = id; + p.id = permid; p.object_type = PERMISSION_OBJECT_TYPE_DOMAIN; p.object_type_hash = string_to_uint128_hash(PERMISSION_OBJECT_TYPE_DOMAIN); p.object_name = object_name; @@ -190,6 +179,28 @@ namespace fioio { p.auxilliary_info = ""; }); } + else { + //get the id for the perm + permid = permctrl_iter->id; + } + + + string accessctrl = grantee_account.to_string() + to_string(permid); + const uint128_t accessHash = string_to_uint128_hash(accessctrl.c_str()); + auto access_iter = accessbyhash.find(accessHash); + + fio_400_assert((access_iter == accessbyhash.end() ), "grantee_account", grantee_account.to_string(), + "Permission already exists", ErrorPermissionExists); + + //add the record to accesses. + const uint64_t accessid = accesses.available_primary_key(); + accesses.emplace(get_self(), [&](struct access_info &a) { + a.id = accessid; + a.permission_id = permid; + a.grantee_account = grantee_account.value; + a.access_hash = accessHash; + }); + fio_400_assert(max_fee >= 0, "max_fee", to_string(max_fee), "Invalid fee value", ErrorMaxFeeInvalid); @@ -319,7 +330,7 @@ namespace fioio { uint64_t permid = permctrl_iter->id; string accessctrl = grantee_account.to_string() + to_string(permid); const uint128_t accessHash = string_to_uint128_hash(accessctrl.c_str()); - auto accessbyhash = access.get_index<"byaccess"_n>(); + auto accessbyhash = accesses.get_index<"byaccess"_n>(); auto access_iter = accessbyhash.find(accessHash); fio_400_assert((access_iter != accessbyhash.end() ), "grantee_account", grantee_account.to_string(), @@ -329,13 +340,17 @@ namespace fioio { //do one more check for this access by permission id, if no results then //remove from permissions. - auto accessbypermid = access.get_index<"bypermid"_n>(); + auto accessbypermid = accesses.get_index<"bypermid"_n>(); auto accessbyperm_iter = accessbypermid.find(permid); if(accessbyperm_iter == accessbypermid.end()){ //no accounts with this access left, remove the permission. permissionsbycontrolhash.erase(permctrl_iter); } + }else{ + //cant find access by control hash. permission not found + fio_400_assert((permctrl_iter != permissionsbycontrolhash.end()), "grantee_account", grantee_account.to_string(), + "Permission not found", ErrorPermissionExists); } fio_400_assert(max_fee >= 0, "max_fee", to_string(max_fee), "Invalid fee value", diff --git a/contracts/fio.perms/fio.perms.hpp b/contracts/fio.perms/fio.perms.hpp index ce96bc81..8398d192 100644 --- a/contracts/fio.perms/fio.perms.hpp +++ b/contracts/fio.perms/fio.perms.hpp @@ -90,7 +90,7 @@ struct [[eosio::action]] access_info { }; //this state table contains information relating to the acesses that are granted in the FIO protocol //please examine fio.perms.cpp for details relating to FIO permissions. -typedef multi_index<"access"_n, access_info, +typedef multi_index<"accesses"_n, access_info, indexed_by<"bypermid"_n, const_mem_fun < access_info, uint64_t, &access_info::by_permission_id>>, indexed_by<"bygrantee"_n, const_mem_fun < access_info, uint64_t, &access_info::by_grantee_account>>, indexed_by<"byaccess"_n, const_mem_fun < access_info, uint128_t, &access_info::by_access_hash>> From 8ff1f3cad9d7d8c864d4cf2944e86030b719d7f8 Mon Sep 17 00:00:00 2001 From: Ed Rotthoff Date: Fri, 31 Mar 2023 13:19:58 -0600 Subject: [PATCH 07/15] FIP-40 alittle code reformatting FIP-40 alittle code reformatting. --- contracts/fio.perms/fio.perms.cpp | 61 +++++++++++++------------------ 1 file changed, 25 insertions(+), 36 deletions(-) diff --git a/contracts/fio.perms/fio.perms.cpp b/contracts/fio.perms/fio.perms.cpp index c8f9a6f5..fbc781a1 100644 --- a/contracts/fio.perms/fio.perms.cpp +++ b/contracts/fio.perms/fio.perms.cpp @@ -53,14 +53,13 @@ namespace fioio { private: - domains_table domains; - fionames_table fionames; - fiofee_table fiofees; + domains_table domains; + fionames_table fionames; + fiofee_table fiofees; eosio_names_table accountmap; - config appConfig; - permissions_table permissions; - access_table accesses; + access_table accesses; + config appConfig; @@ -74,18 +73,23 @@ namespace fioio { fionames(AddressContract, AddressContract.value), fiofees(FeeContract, FeeContract.value), accountmap(_self, _self.value){ - configs_singleton configsSingleton(FeeContract, FeeContract.value); appConfig = configsSingleton.get_or_default(config()); } + /* + * This action will check if a permission exists for the specified arguments, if it does not + * yet exist in the permissions table a new record will be added, the accesses table will also + * be updated to indicate the grantee account access that has been granted. please see the code + * for error logic and parameter descriptions. + */ [[eosio::action]] void addperm(const name &grantee_account, - const string &permission_name, - const string &permission_info, - const string &object_name, + const string &permission_name, //one permission is permitted register_address_on_domain + const string &permission_info, //this is empty for FIP-40, an extensibility field for the future. + const string &object_name, //the name of the fio domain const int64_t &max_fee, const string &tpid, const name &actor @@ -150,12 +154,11 @@ namespace fioio { //error if the grantee account already has this permission. string permcontrol = REGISTER_ADDRESS_ON_DOMAIN_OBJECT_TYPE + object_name + REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME; - - const uint128_t permcontrolHash = string_to_uint128_hash(permcontrol.c_str()); + const uint128_t permcontrolHash = string_to_uint128_hash(permcontrol.c_str()); auto accessbyhash = accesses.get_index<"byaccess"_n>(); - auto permissionsbycontrolhash = permissions.get_index<"bypermctrl"_n>(); - auto permctrl_iter = permissionsbycontrolhash.find(permcontrolHash); - uint64_t permid = 0; + auto permissionsbycontrolhash = permissions.get_index<"bypermctrl"_n>(); + auto permctrl_iter = permissionsbycontrolhash.find(permcontrolHash); + uint64_t permid = 0; if(permctrl_iter == permissionsbycontrolhash.end()) { //insert the permission @@ -163,9 +166,7 @@ namespace fioio { //being supported string object_type = PERMISSION_OBJECT_TYPE_DOMAIN; const string controlv = object_type + object_name + useperm; - permid = permissions.available_primary_key(); - permissions.emplace(get_self(), [&](struct permission_info &p) { p.id = permid; p.object_type = PERMISSION_OBJECT_TYPE_DOMAIN; @@ -245,11 +246,7 @@ namespace fioio { std::make_tuple(actor, ADDPERMISSIONRAMBASE + (ADDPERMISSIONRAM * permission_info.size())) ).send(); } - - - const string response_string = "{\"status\": \"OK\", \"fee_collected\" : "+ to_string(fee_amount) +"}"; - send_response(response_string.c_str()); } @@ -335,14 +332,11 @@ namespace fioio { fio_400_assert((access_iter != accessbyhash.end() ), "grantee_account", grantee_account.to_string(), "Permission not found", ErrorPermissionExists); - accessbyhash.erase(access_iter); - //do one more check for this access by permission id, if no results then //remove from permissions. - auto accessbypermid = accesses.get_index<"bypermid"_n>(); + auto accessbypermid = accesses.get_index<"bypermid"_n>(); auto accessbyperm_iter = accessbypermid.find(permid); - if(accessbyperm_iter == accessbypermid.end()){ //no accounts with this access left, remove the permission. permissionsbycontrolhash.erase(permctrl_iter); @@ -359,16 +353,16 @@ namespace fioio { "TPID must be empty or valid FIO address", ErrorPubKeyValid); - - //fees - const uint128_t endpoint_hash = string_to_uint128_hash(REMOVE_PERMISSION_ENDPOINT); - auto fees_by_endpoint = fiofees.get_index<"byendpoint"_n>(); - auto fee_iter = fees_by_endpoint.find(endpoint_hash); + const uint128_t endpoint_hash = string_to_uint128_hash(REMOVE_PERMISSION_ENDPOINT); + auto fees_by_endpoint = fiofees.get_index<"byendpoint"_n>(); + auto fee_iter = fees_by_endpoint.find(endpoint_hash); + fio_400_assert(fee_iter != fees_by_endpoint.end(), "endpoint_name", REMOVE_PERMISSION_ENDPOINT, "FIO fee not found for endpoint", ErrorNoEndpoint); + const uint64_t fee_amount = fee_iter->suf_amount; - const uint64_t fee_type = fee_iter->type; + const uint64_t fee_type = fee_iter->type; fio_400_assert(fee_type == 0, "fee_type", to_string(fee_type), "unexpected fee type for endpoint remove permission, expected 0", @@ -387,14 +381,9 @@ namespace fioio { {actor, true} ); } - - const string response_string = "{\"status\": \"OK\", \"fee_collected\" : "+ to_string(fee_amount) +"}"; send_response(response_string.c_str()); - } - - }; EOSIO_DISPATCH(FioPermissions, (addperm)(remperm)) From df6a4e3bae3ad2dcebb795c3524677d9185522c6 Mon Sep 17 00:00:00 2001 From: Ed Rotthoff Date: Tue, 4 Apr 2023 12:58:25 -0600 Subject: [PATCH 08/15] FIP-40 regaddress integration. FIP-40 integrate register address with permissions. --- contracts/fio.address/fio.address.cpp | 34 ++++++++++++++++++++++++--- contracts/fio.perms/fio.perms.cpp | 5 ++-- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/contracts/fio.address/fio.address.cpp b/contracts/fio.address/fio.address.cpp index b206aba5..f26b370f 100644 --- a/contracts/fio.address/fio.address.cpp +++ b/contracts/fio.address/fio.address.cpp @@ -5,6 +5,12 @@ * @license FIO Foundation ( https://github.com/fioprotocol/fio/blob/master/LICENSE ) Dapix */ +/* + * FIP-40 +todo fio.address burnexpired /burn_expired Burn all the associated domain permissions when FIO Domain is burned. +todo fio.address xferdomain /transfer_fio_domain Burn all the associated domain permissions when FIO Domain is transferred. + */ + #include "fio.address.hpp" #include #include @@ -13,6 +19,7 @@ #include #include //TEMP FOR XFERADDRESS #include +#include namespace fioio { @@ -33,6 +40,8 @@ namespace fioio { eosiosystem::producers_table producers; eosiosystem::locked_tokens_table lockedTokensTable; nfts_table nftstable; + permissions_table permissions_table; + access_table accesses_table; config appConfig; //FIP-39 begin @@ -56,8 +65,9 @@ namespace fioio { voters(SYSTEMACCOUNT, SYSTEMACCOUNT.value), topprods(SYSTEMACCOUNT, SYSTEMACCOUNT.value), producers(SYSTEMACCOUNT, SYSTEMACCOUNT.value), - lockedTokensTable(SYSTEMACCOUNT, - SYSTEMACCOUNT.value), + lockedTokensTable(SYSTEMACCOUNT,SYSTEMACCOUNT.value), + permissions_table(PERMSACCOUNT,PERMSACCOUNT.value), + accesses_table(PERMSACCOUNT,PERMSACCOUNT.value), //FIP-39 begin fionameinfo(_self, _self.value){ //FIP-39 end @@ -282,7 +292,25 @@ namespace fioio { const bool isPublic = domains_iter->is_public; uint64_t domain_owner = domains_iter->account; - if (!isPublic) { + //object_type, object_name, and permission_name get hashed for permission control hash + bool hasDomainAccess = false; + string permctrl = REGISTER_ADDRESS_ON_DOMAIN_OBJECT_TYPE+fa.fiodomain+REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME; + uint128_t permctrlhash = string_to_uint128_hash(permctrl.c_str()); + auto permissionbyctrl = permissions_table.get_index<"bypermctrl"_n>(); + auto permsbypermctrl_iter = permissionbyctrl.find(permctrlhash); + + + if(permsbypermctrl_iter != permissionbyctrl.end()){ + string accessctrl = actor.to_string()+to_string(permsbypermctrl_iter->id); + uint128_t accessctrlhash = string_to_uint128_hash(accessctrl.c_str()); + auto accessesbyctrl = accesses_table.get_index<"byaccess"_n>(); + auto accessesbyctrl_iter = accessesbyctrl.find(accessctrlhash); + if(accessesbyctrl_iter != accessesbyctrl.end()){ + hasDomainAccess = true; + } + } + + if (!(isPublic || hasDomainAccess)) { fio_400_assert(domain_owner == actor.value, "fio_address", fa.fioaddress, "FIO Domain is not public. Only owner can create FIO Addresses.", ErrorInvalidFioNameFormat); diff --git a/contracts/fio.perms/fio.perms.cpp b/contracts/fio.perms/fio.perms.cpp index fbc781a1..2b9780b9 100644 --- a/contracts/fio.perms/fio.perms.cpp +++ b/contracts/fio.perms/fio.perms.cpp @@ -153,7 +153,7 @@ namespace fioio { //error if the grantee account already has this permission. - string permcontrol = REGISTER_ADDRESS_ON_DOMAIN_OBJECT_TYPE + object_name + REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME; + string permcontrol = REGISTER_ADDRESS_ON_DOMAIN_OBJECT_TYPE + object_name + useperm; const uint128_t permcontrolHash = string_to_uint128_hash(permcontrol.c_str()); auto accessbyhash = accesses.get_index<"byaccess"_n>(); auto permissionsbycontrolhash = permissions.get_index<"bypermctrl"_n>(); @@ -165,7 +165,6 @@ namespace fioio { //one permission name is integrated for fip 40, modify this logic for any new permission names //being supported string object_type = PERMISSION_OBJECT_TYPE_DOMAIN; - const string controlv = object_type + object_name + useperm; permid = permissions.available_primary_key(); permissions.emplace(get_self(), [&](struct permission_info &p) { p.id = permid; @@ -175,7 +174,7 @@ namespace fioio { p.object_name_hash = string_to_uint128_hash(object_name); p.permission_name = useperm; p.permission_name_hash = string_to_uint128_hash(useperm); - p.permission_control_hash = string_to_uint128_hash(controlv); + p.permission_control_hash = permcontrolHash; p.owner_account = actor.value; p.auxilliary_info = ""; }); From 2832571a4e77fe787ba50bae87b5a82ad3db85a1 Mon Sep 17 00:00:00 2001 From: Ed Rotthoff Date: Wed, 12 Apr 2023 12:29:44 -0600 Subject: [PATCH 09/15] FIP-40 integrate transfer domain and burn expired. FIP-40 integrate transfer domain and burn expired. --- contracts/fio.address/fio.address.cpp | 47 +++++++++++++- contracts/fio.perms/fio.perms.abi | 23 ++++++- contracts/fio.perms/fio.perms.cpp | 92 ++++++++++++++++++++++++++- contracts/fio.perms/fio.perms.hpp | 12 ++++ 4 files changed, 167 insertions(+), 7 deletions(-) diff --git a/contracts/fio.address/fio.address.cpp b/contracts/fio.address/fio.address.cpp index f26b370f..3c6fa7b6 100644 --- a/contracts/fio.address/fio.address.cpp +++ b/contracts/fio.address/fio.address.cpp @@ -1380,10 +1380,37 @@ namespace fioio { while (domainiter != domains.end()) { const uint64_t expire = domainiter->expiration; if ((expire + DOMAINWAITFORBURNDAYS) < nowtime) { + + //clear the domain permissions. + //if there are domain permissions. + //call to clear them and return. + //if no permissions continue the process. + string permcontrol = REGISTER_ADDRESS_ON_DOMAIN_OBJECT_TYPE + domainiter->name + REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME; + + const uint128_t permcontrolHash = string_to_uint128_hash(permcontrol.c_str()); + + auto permissionsbycontrolhash = permissions_table.get_index<"bypermctrl"_n>(); + auto permctrl_iter = permissionsbycontrolhash.find(permcontrolHash); + if (permctrl_iter != permissionsbycontrolhash.end() ) { + //delete the permission, and the granted accounts, + // clear all the permissions for this domain. + //FIP-40 + action( + permission_level{get_self(), "active"_n}, + "fio.perms"_n, + "clearperm"_n, + std::make_tuple(REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME, domainiter->name) + ).send(); + + // increment the number record processed by one + //this increment helps manage the time used by the burn + recordProcessed++; + if (recordProcessed == numbertoburn) { break; } + } + const auto domainhash = domainiter->domainhash; auto nameexpidx = fionames.get_index<"bydomain"_n>(); auto nameiter = nameexpidx.find(domainhash); - while (nameiter != nameexpidx.end()) { auto nextname = nameiter; nextname++; @@ -2043,11 +2070,14 @@ namespace fioio { auto burnqbyname = nftburnqueue.get_index<"byaddress"_n>(); auto nftburnq_iter = burnqbyname.begin(); - auto contractsbyname = nftstable.get_index<"byaddress"_n>(); + auto contractsbyname = nftstable.get_index<"byaddress"_n>(); //more than 3k records ed@edge uint16_t counter = 0; auto nft_iter = contractsbyname.begin(); while (nftburnq_iter != burnqbyname.end()) { - nft_iter = contractsbyname.find(nftburnq_iter->fio_address_hash); + //as soon as this has toooo many records it will not execute in a tx, we limit the number + // of accounts granted a permission see fio.perms.hpp MAX_GRANTEES in + // the code for a more detailed explanation + nft_iter = contractsbyname.find(nftburnq_iter->fio_address_hash); //search delay. counter++; if (nft_iter != contractsbyname.end()) { // if row, delete an nft nft_iter = contractsbyname.erase(nft_iter); @@ -2432,6 +2462,17 @@ namespace fioio { domainsbyname.modify(domains_iter, actor, [&](struct domain &a) { a.account = nm.value; }); + + //clear all the permissions for this domainas paert of the transfer + //note we limit grantees to 100 in the protocol to permit this kind of operation. + //see fio.perms.hpp MAX_GRANTEES documentaiton for further details. + //FIP-40 + action( + permission_level{get_self(), "active"_n}, + "fio.perms"_n, + "clearperm"_n, + std::make_tuple(REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME, fio_domain) + ).send(); //fees const uint64_t fee_amount = fee_iter->suf_amount; diff --git a/contracts/fio.perms/fio.perms.abi b/contracts/fio.perms/fio.perms.abi index c7772042..437b9488 100644 --- a/contracts/fio.perms/fio.perms.abi +++ b/contracts/fio.perms/fio.perms.abi @@ -133,7 +133,21 @@ "type": "name" } ] - } + }, + { + "name": "clearperm", + "base": "", + "fields": [ + { + "name": "permission_name", + "type": "string" + }, + { + "name": "object_name", + "type": "string" + } + ] + } ], "actions": [ { @@ -145,7 +159,12 @@ "name": "remperm", "type": "remperm", "ricardian_contract": "" - } + }, + { + "name": "clearperm", + "type": "remperm", + "ricardian_contract": "" + } ], "tables": [ { diff --git a/contracts/fio.perms/fio.perms.cpp b/contracts/fio.perms/fio.perms.cpp index 2b9780b9..83e02eb8 100644 --- a/contracts/fio.perms/fio.perms.cpp +++ b/contracts/fio.perms/fio.perms.cpp @@ -156,6 +156,7 @@ namespace fioio { string permcontrol = REGISTER_ADDRESS_ON_DOMAIN_OBJECT_TYPE + object_name + useperm; const uint128_t permcontrolHash = string_to_uint128_hash(permcontrol.c_str()); auto accessbyhash = accesses.get_index<"byaccess"_n>(); + auto accessbypermid = accesses.get_index<"bypermid"_n>(); auto permissionsbycontrolhash = permissions.get_index<"bypermctrl"_n>(); auto permctrl_iter = permissionsbycontrolhash.find(permcontrolHash); uint64_t permid = 0; @@ -192,6 +193,21 @@ namespace fioio { fio_400_assert((access_iter == accessbyhash.end() ), "grantee_account", grantee_account.to_string(), "Permission already exists", ErrorPermissionExists); + //count the number of grantees. + auto grantees_iter = accessbypermid.find(permid); + + int countgrantees = 0; + while(grantees_iter != accessbypermid.end()){ + countgrantees ++; + grantees_iter ++; + } + + string msg = "Number of grantees exceeded, Max number grantees permitted is "+ to_string(MAX_GRANTEES); + + fio_400_assert((countgrantees <= MAX_GRANTEES ), "grantee_account", grantee_account.to_string(), + msg , ErrorPermissionExists); + + //add the record to accesses. const uint64_t accessid = accesses.available_primary_key(); accesses.emplace(get_self(), [&](struct access_info &a) { @@ -383,7 +399,79 @@ namespace fioio { const string response_string = "{\"status\": \"OK\", \"fee_collected\" : "+ to_string(fee_amount) +"}"; send_response(response_string.c_str()); } - }; - EOSIO_DISPATCH(FioPermissions, (addperm)(remperm)) + + [[eosio::action]] + void + clearperm( + const string &permission_name, + const string &object_name + ) { + + + print("clearperm -- called. \n"); + + eosio_assert((has_auth(AddressContract) ), + "missing required authority of fio.addresss"); + + + fio_400_assert(permission_name.length() > 0, "permission_name", permission_name, + "Permission name is invalid", ErrorInvalidPermissionName); + + string useperm = makeLowerCase(permission_name); + + // error if permission name is not the expected name register_address_on_domain. + fio_400_assert(useperm.compare(REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME) == 0, "permission_name", permission_name, + "Permission name is invalid", ErrorInvalidPermissionName); + + // error if object name is not * or is not in the domains table + fio_400_assert(object_name.size() > 0, "object_name", object_name, + "Object name is invalid", ErrorInvalidObjectName); + + //verify domain name, and that domain is owned by the actor account. + FioAddress fa; + getFioAddressStruct(object_name, fa); + + fio_400_assert(fa.domainOnly, "object_name", object_name, "Invalid object name", + ErrorInvalidObjectName); + + string permcontrol = REGISTER_ADDRESS_ON_DOMAIN_OBJECT_TYPE + object_name + REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME; + + const uint128_t permcontrolHash = string_to_uint128_hash(permcontrol.c_str()); + + auto permissionsbycontrolhash = permissions.get_index<"bypermctrl"_n>(); + auto permctrl_iter = permissionsbycontrolhash.find(permcontrolHash); + if (permctrl_iter != permissionsbycontrolhash.end() ) { + int numcleared =0; + //get the id and look in access, remove it if its there, error if not there + uint64_t permid = permctrl_iter->id; + auto accessbypermid1 = accesses.get_index<"bypermid"_n>(); + auto accessbypermid_iter = accessbypermid1.find(permid); + //remove all accesses, this code assumes that there are less than 5k accesses per permission. + //if there are more than this, then the method of removal must be changed to be more + //like the NFT clearing in FIO. make a queue and remove some number at a time. + //for register address on domain we limit the number of grantees to 100. + while (accessbypermid_iter != accessbypermid1.end()) { + numcleared++; + auto nextaccess = accessbypermid_iter; + nextaccess++; + accessbypermid1.erase(accessbypermid_iter); + accessbypermid_iter = nextaccess; + } + permissionsbycontrolhash.erase(permctrl_iter); + }else{ + //cant find access by control hash. permission not found + fio_400_assert((permctrl_iter != permissionsbycontrolhash.end()), "permission_name", permission_name, + "Permission not found", ErrorPermissionExists); + } + + + const string response_string = "{\"status\": \"OK\"}"; + send_response(response_string.c_str()); + } +}; + + + + EOSIO_DISPATCH(FioPermissions, (addperm)(remperm)(clearperm)) } diff --git a/contracts/fio.perms/fio.perms.hpp b/contracts/fio.perms/fio.perms.hpp index 8398d192..07aef591 100644 --- a/contracts/fio.perms/fio.perms.hpp +++ b/contracts/fio.perms/fio.perms.hpp @@ -21,6 +21,18 @@ namespace fioio { const static string REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME = "register_address_on_domain"; const static string REGISTER_ADDRESS_ON_DOMAIN_OBJECT_TYPE = "domain"; + /** + * The fio protocol could permit users to grant a host of accounts a given permission, just like any + * well known permissions schemes users could then grant so many accounts permissioins that it becomes an + * extended offline effort to perform housekeeping and other analysis when the permission changes or are removed. + * we decide to enforce that 100 accounts can be granted a permission, this number provides that + * house keeping of permissions in state can have a chance to be performed by the contracts when users + * perform operations that necessitate that pre-existing permissions be removed as part of an operation + * (for example for FIP-40 when transferring a domain, we must remove pre-exisitng permissions + * for the user or we could give an error and make users clean permissions offline before allowing the + * transfer). + */ + const static int MAX_GRANTEES = 100; struct [[eosio::action]] permission_info { From fe1a86a533a8b16040f0e0c6683be96a8afff59e Mon Sep 17 00:00:00 2001 From: Ed Rotthoff Date: Thu, 13 Apr 2023 16:10:38 -0600 Subject: [PATCH 10/15] FIP-40 code review action items FIP-40 code review action items --- contracts/fio.address/fio.address.cpp | 8 ++++---- contracts/fio.perms/fio.perms.abi | 4 ++-- contracts/fio.perms/fio.perms.cpp | 4 ++-- contracts/fio.perms/fio.perms.hpp | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/contracts/fio.address/fio.address.cpp b/contracts/fio.address/fio.address.cpp index 3c6fa7b6..64daa02b 100644 --- a/contracts/fio.address/fio.address.cpp +++ b/contracts/fio.address/fio.address.cpp @@ -2070,7 +2070,7 @@ namespace fioio { auto burnqbyname = nftburnqueue.get_index<"byaddress"_n>(); auto nftburnq_iter = burnqbyname.begin(); - auto contractsbyname = nftstable.get_index<"byaddress"_n>(); //more than 3k records ed@edge + auto contractsbyname = nftstable.get_index<"byaddress"_n>(); uint16_t counter = 0; auto nft_iter = contractsbyname.begin(); while (nftburnq_iter != burnqbyname.end()) { @@ -2462,10 +2462,10 @@ namespace fioio { domainsbyname.modify(domains_iter, actor, [&](struct domain &a) { a.account = nm.value; }); - - //clear all the permissions for this domainas paert of the transfer + + //clear all the permissions for this domain as part of the transfer //note we limit grantees to 100 in the protocol to permit this kind of operation. - //see fio.perms.hpp MAX_GRANTEES documentaiton for further details. + //see fio.perms.hpp MAX_GRANTEES documentation for further details. //FIP-40 action( permission_level{get_self(), "active"_n}, diff --git a/contracts/fio.perms/fio.perms.abi b/contracts/fio.perms/fio.perms.abi index 437b9488..368e58df 100644 --- a/contracts/fio.perms/fio.perms.abi +++ b/contracts/fio.perms/fio.perms.abi @@ -43,7 +43,7 @@ "type": "uint64" }, { - "name": "auxilliary_info", + "name": "auxiliary_info", "type": "string" } ] @@ -162,7 +162,7 @@ }, { "name": "clearperm", - "type": "remperm", + "type": "clearperm", "ricardian_contract": "" } ], diff --git a/contracts/fio.perms/fio.perms.cpp b/contracts/fio.perms/fio.perms.cpp index 83e02eb8..cdb3198c 100644 --- a/contracts/fio.perms/fio.perms.cpp +++ b/contracts/fio.perms/fio.perms.cpp @@ -21,7 +21,7 @@ * the owning account, and also including all parameterized data used by the permission according to the * business logic required (such as access levels, or other abstractions that can be set when an * account grants the permission). - * Permission Auxilliary Info – the json definition of all of the parameterized data used by a given permission that is unique for a permission. + * Permission Auxiliary Info – the json definition of all of the parameterized data used by a given permission that is unique for a permission. * for FIP-40 no additional data is necessary. this field provides extensibility such that we can introduce new * or novel parameters and dials used by a new permission if this is necessary. * Grantor – the granting/owning account of the object that relates to the permission. @@ -177,7 +177,7 @@ namespace fioio { p.permission_name_hash = string_to_uint128_hash(useperm); p.permission_control_hash = permcontrolHash; p.owner_account = actor.value; - p.auxilliary_info = ""; + p.auxiliary_info = ""; }); } else { diff --git a/contracts/fio.perms/fio.perms.hpp b/contracts/fio.perms/fio.perms.hpp index 07aef591..06eef0bb 100644 --- a/contracts/fio.perms/fio.perms.hpp +++ b/contracts/fio.perms/fio.perms.hpp @@ -54,7 +54,7 @@ namespace fioio { uint64_t owner_account = 0; //this field can contain any string based info that is useful for the permission. //it shouldbe json based. for FIP-40 this is unused. - string auxilliary_info = ""; + string auxiliary_info = ""; uint64_t primary_key() const { return id; } @@ -66,7 +66,7 @@ namespace fioio { EOSLIB_SERIALIZE(permission_info, (id)(object_type)(object_type_hash)(object_name)(object_name_hash) - (permission_name)(permission_name_hash)(permission_control_hash)(owner_account)(auxilliary_info)) + (permission_name)(permission_name_hash)(permission_control_hash)(owner_account)(auxiliary_info)) }; //this state table contains information relating to the permissions that are granted in the FIO protocol //please examine fio.perms.cpp for details relating to FIO permissions. From 6420954876808b96bb309a71c5f614de554db177 Mon Sep 17 00:00:00 2001 From: Ed Rotthoff Date: Tue, 18 Apr 2023 19:56:21 -0600 Subject: [PATCH 11/15] FIP-40 add getters FIP-40 add getters --- contracts/fio.address/fio.address.cpp | 15 ---------- contracts/fio.perms/fio.perms.abi | 16 ++++++++--- contracts/fio.perms/fio.perms.cpp | 41 ++++++++++++++------------- contracts/fio.perms/fio.perms.hpp | 20 ++++++++----- 4 files changed, 47 insertions(+), 45 deletions(-) diff --git a/contracts/fio.address/fio.address.cpp b/contracts/fio.address/fio.address.cpp index 64daa02b..cd792b8b 100644 --- a/contracts/fio.address/fio.address.cpp +++ b/contracts/fio.address/fio.address.cpp @@ -5,11 +5,6 @@ * @license FIO Foundation ( https://github.com/fioprotocol/fio/blob/master/LICENSE ) Dapix */ -/* - * FIP-40 -todo fio.address burnexpired /burn_expired Burn all the associated domain permissions when FIO Domain is burned. -todo fio.address xferdomain /transfer_fio_domain Burn all the associated domain permissions when FIO Domain is transferred. - */ #include "fio.address.hpp" #include @@ -1381,18 +1376,11 @@ namespace fioio { const uint64_t expire = domainiter->expiration; if ((expire + DOMAINWAITFORBURNDAYS) < nowtime) { - //clear the domain permissions. - //if there are domain permissions. - //call to clear them and return. - //if no permissions continue the process. string permcontrol = REGISTER_ADDRESS_ON_DOMAIN_OBJECT_TYPE + domainiter->name + REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME; - const uint128_t permcontrolHash = string_to_uint128_hash(permcontrol.c_str()); - auto permissionsbycontrolhash = permissions_table.get_index<"bypermctrl"_n>(); auto permctrl_iter = permissionsbycontrolhash.find(permcontrolHash); if (permctrl_iter != permissionsbycontrolhash.end() ) { - //delete the permission, and the granted accounts, // clear all the permissions for this domain. //FIP-40 action( @@ -2074,9 +2062,6 @@ namespace fioio { uint16_t counter = 0; auto nft_iter = contractsbyname.begin(); while (nftburnq_iter != burnqbyname.end()) { - //as soon as this has toooo many records it will not execute in a tx, we limit the number - // of accounts granted a permission see fio.perms.hpp MAX_GRANTEES in - // the code for a more detailed explanation nft_iter = contractsbyname.find(nftburnq_iter->fio_address_hash); //search delay. counter++; if (nft_iter != contractsbyname.end()) { // if row, delete an nft diff --git a/contracts/fio.perms/fio.perms.abi b/contracts/fio.perms/fio.perms.abi index 368e58df..475048da 100644 --- a/contracts/fio.perms/fio.perms.abi +++ b/contracts/fio.perms/fio.perms.abi @@ -39,8 +39,8 @@ "type": "uint128" }, { - "name": "owner_account", - "type": "uint64" + "name": "grantor_account", + "type": "name" }, { "name": "auxiliary_info", @@ -62,12 +62,20 @@ }, { "name": "grantee_account", - "type": "uint64" + "type": "name" }, { "name": "access_hash", "type": "uint128" - } + }, + { + "name": "grantor_account", + "type": "name" + }, + { + "name": "names_hash", + "type": "uint128" + } ] }, { diff --git a/contracts/fio.perms/fio.perms.cpp b/contracts/fio.perms/fio.perms.cpp index cdb3198c..2a34bea8 100644 --- a/contracts/fio.perms/fio.perms.cpp +++ b/contracts/fio.perms/fio.perms.cpp @@ -2,18 +2,16 @@ * Description: * * - * We will introduce a notion of a permission, a permission is a definition of information that provides some control - * and/or access to objects in state which are owned by the account that creates the permission. + * We will introduce a notion of a permission, a permission is a definition of information that provides some + * access control to objects in state which are owned by the account that creates the permission. * Permissions definitions will be extensible within the FIO protocol. new permissions can be added into - * the protocols contracts using the permmissions tables in this contract. + * the FIO protocol by performing the following process: * - * - * The following process will be used for the definition and integration of new permissions in the FIO protocols: - * Step 1: define the permission desired. - * Step 2: modify the fio.contracts affected by the new access to enforce and integrate the new permission. + * Step 1: define the functionality of the permission desired. + * Step 2: modify the fio.contracts to enforce and integrate the new permission. * Step 3: rollout the new permission into testnet and main net using the following actions * 3.1. rollout the new version of the contracts supporting the new permission. - * 3.2 FIO user accounts begin using the permission as indicated in the spec. + * 3.2 FIO user accounts then begin using the permission as indicated in the spec. * * The following vernacular is used throughout our design: * Permission – the name of the permission, @@ -96,7 +94,7 @@ namespace fioio { ) { - print("addperm -- called. \n"); + //print("addperm -- called. \n"); require_auth(actor); string useperm = makeLowerCase(permission_name); @@ -154,11 +152,12 @@ namespace fioio { //error if the grantee account already has this permission. string permcontrol = REGISTER_ADDRESS_ON_DOMAIN_OBJECT_TYPE + object_name + useperm; - const uint128_t permcontrolHash = string_to_uint128_hash(permcontrol.c_str()); + const uint128_t permcontrolhash = string_to_uint128_hash(permcontrol.c_str()); + const uint128_t objectnamehash = string_to_uint128_hash(object_name); auto accessbyhash = accesses.get_index<"byaccess"_n>(); auto accessbypermid = accesses.get_index<"bypermid"_n>(); auto permissionsbycontrolhash = permissions.get_index<"bypermctrl"_n>(); - auto permctrl_iter = permissionsbycontrolhash.find(permcontrolHash); + auto permctrl_iter = permissionsbycontrolhash.find(permcontrolhash); uint64_t permid = 0; if(permctrl_iter == permissionsbycontrolhash.end()) @@ -172,11 +171,11 @@ namespace fioio { p.object_type = PERMISSION_OBJECT_TYPE_DOMAIN; p.object_type_hash = string_to_uint128_hash(PERMISSION_OBJECT_TYPE_DOMAIN); p.object_name = object_name; - p.object_name_hash = string_to_uint128_hash(object_name); + p.object_name_hash = objectnamehash; p.permission_name = useperm; p.permission_name_hash = string_to_uint128_hash(useperm); - p.permission_control_hash = permcontrolHash; - p.owner_account = actor.value; + p.permission_control_hash = permcontrolhash; + p.grantor_account = actor.value; p.auxiliary_info = ""; }); } @@ -187,8 +186,8 @@ namespace fioio { string accessctrl = grantee_account.to_string() + to_string(permid); - const uint128_t accessHash = string_to_uint128_hash(accessctrl.c_str()); - auto access_iter = accessbyhash.find(accessHash); + const uint128_t accesshash = string_to_uint128_hash(accessctrl.c_str()); + auto access_iter = accessbyhash.find(accesshash); fio_400_assert((access_iter == accessbyhash.end() ), "grantee_account", grantee_account.to_string(), "Permission already exists", ErrorPermissionExists); @@ -196,6 +195,8 @@ namespace fioio { //count the number of grantees. auto grantees_iter = accessbypermid.find(permid); + //enforce the limit on the number of grantee accounts see the comments in fio.perms.hpp for MAX_GRANTEES + //for details int countgrantees = 0; while(grantees_iter != accessbypermid.end()){ countgrantees ++; @@ -214,7 +215,9 @@ namespace fioio { a.id = accessid; a.permission_id = permid; a.grantee_account = grantee_account.value; - a.access_hash = accessHash; + a.access_hash = accesshash; + a.grantor_account = actor.value; + a.names_hash = string_to_uint128_hash(object_name + useperm); }); @@ -277,7 +280,7 @@ namespace fioio { ) { - print("remperm -- called. \n"); + // print("remperm -- called. \n"); require_auth(actor); @@ -409,7 +412,7 @@ namespace fioio { ) { - print("clearperm -- called. \n"); + // print("clearperm -- called. \n"); eosio_assert((has_auth(AddressContract) ), "missing required authority of fio.addresss"); diff --git a/contracts/fio.perms/fio.perms.hpp b/contracts/fio.perms/fio.perms.hpp index 06eef0bb..507b963c 100644 --- a/contracts/fio.perms/fio.perms.hpp +++ b/contracts/fio.perms/fio.perms.hpp @@ -51,7 +51,7 @@ namespace fioio { //by convention we will store the hashed value of the following concatination in this field to provide a //unique search key by object_type, object_name, and permission_name uint128_t permission_control_hash = 0; - uint64_t owner_account = 0; + uint64_t grantor_account = 0; //this field can contain any string based info that is useful for the permission. //it shouldbe json based. for FIP-40 this is unused. string auxiliary_info = ""; @@ -62,11 +62,11 @@ namespace fioio { uint128_t by_object_name_hash() const { return object_name_hash; } uint128_t by_permission_name_hash() const { return permission_name_hash; } uint128_t by_permission_control_hash() const { return permission_control_hash; } - uint64_t by_owner_account() const { return owner_account; } + uint64_t by_grantor_account() const { return grantor_account; } EOSLIB_SERIALIZE(permission_info, (id)(object_type)(object_type_hash)(object_name)(object_name_hash) - (permission_name)(permission_name_hash)(permission_control_hash)(owner_account)(auxiliary_info)) + (permission_name)(permission_name_hash)(permission_control_hash)(grantor_account)(auxiliary_info)) }; //this state table contains information relating to the permissions that are granted in the FIO protocol //please examine fio.perms.cpp for details relating to FIO permissions. @@ -75,7 +75,7 @@ namespace fioio { indexed_by<"byobjname"_n, const_mem_fun < permission_info, uint128_t, &permission_info::by_object_name_hash>>, indexed_by<"bypermname"_n, const_mem_fun < permission_info, uint128_t, &permission_info::by_permission_name_hash>>, indexed_by<"bypermctrl"_n, const_mem_fun < permission_info, uint128_t, &permission_info::by_permission_control_hash>>, - indexed_by<"byowner"_n, const_mem_fun < permission_info, uint64_t, &permission_info::by_owner_account>> + indexed_by<"bygrantor"_n, const_mem_fun < permission_info, uint64_t, &permission_info::by_grantor_account>> > permissions_table; @@ -90,22 +90,28 @@ struct [[eosio::action]] access_info { uint64_t grantee_account = 0; //this is the hashed value of the string concatination of grantee account, permission id uint128_t access_hash = 0; + uint64_t grantor_account = 0; + uint128_t names_hash = 0; //the hashed value of the string concatination of the object name and permission name. uint64_t primary_key() const { return id; } uint64_t by_permission_id() const { return permission_id; } uint64_t by_grantee_account() const { return grantee_account; } uint128_t by_access_hash() const { return access_hash; } + uint64_t by_grantor_account() const {return grantor_account; } + uint128_t by_names_hash() const { return names_hash; } - EOSLIB_SERIALIZE(access_info, (id)(permission_id)(grantee_account)(access_hash)) + EOSLIB_SERIALIZE(access_info, (id)(permission_id)(grantee_account)(access_hash)(grantor_account)(names_hash)) }; -//this state table contains information relating to the acesses that are granted in the FIO protocol +//this state table contains information relating to the accesses that are granted in the FIO protocol //please examine fio.perms.cpp for details relating to FIO permissions. typedef multi_index<"accesses"_n, access_info, indexed_by<"bypermid"_n, const_mem_fun < access_info, uint64_t, &access_info::by_permission_id>>, indexed_by<"bygrantee"_n, const_mem_fun < access_info, uint64_t, &access_info::by_grantee_account>>, - indexed_by<"byaccess"_n, const_mem_fun < access_info, uint128_t, &access_info::by_access_hash>> + indexed_by<"byaccess"_n, const_mem_fun < access_info, uint128_t, &access_info::by_access_hash>>, + indexed_by<"bygrantor"_n, const_mem_fun < access_info, uint64_t, &access_info::by_grantor_account>>, + indexed_by<"bynames"_n, const_mem_fun < access_info, uint128_t, &access_info::by_names_hash>> > access_table; From 59c46627b70c41e250671fcd61484236b72012c3 Mon Sep 17 00:00:00 2001 From: Ed Rotthoff Date: Fri, 28 Apr 2023 12:05:15 -0400 Subject: [PATCH 12/15] FIP-40 implement wildcard for domain name option FIP-40 implement wildcard for domain name option --- contracts/fio.address/fio.address.cpp | 71 +++++++---- contracts/fio.common/fio_common_validator.hpp | 18 ++- contracts/fio.perms/fio.perms.abi | 4 + contracts/fio.perms/fio.perms.cpp | 120 ++++++++++-------- contracts/fio.perms/fio.perms.hpp | 2 +- 5 files changed, 135 insertions(+), 80 deletions(-) diff --git a/contracts/fio.address/fio.address.cpp b/contracts/fio.address/fio.address.cpp index c0c1a5b3..2e526dd3 100644 --- a/contracts/fio.address/fio.address.cpp +++ b/contracts/fio.address/fio.address.cpp @@ -287,21 +287,38 @@ namespace fioio { const bool isPublic = domains_iter->is_public; uint64_t domain_owner = domains_iter->account; + //object_type, object_name, and permission_name get hashed for permission control hash bool hasDomainAccess = false; - string permctrl = REGISTER_ADDRESS_ON_DOMAIN_OBJECT_TYPE+fa.fiodomain+REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME; - uint128_t permctrlhash = string_to_uint128_hash(permctrl.c_str()); - auto permissionbyctrl = permissions_table.get_index<"bypermctrl"_n>(); - auto permsbypermctrl_iter = permissionbyctrl.find(permctrlhash); - - - if(permsbypermctrl_iter != permissionbyctrl.end()){ - string accessctrl = actor.to_string()+to_string(permsbypermctrl_iter->id); - uint128_t accessctrlhash = string_to_uint128_hash(accessctrl.c_str()); - auto accessesbyctrl = accesses_table.get_index<"byaccess"_n>(); - auto accessesbyctrl_iter = accessesbyctrl.find(accessctrlhash); - if(accessesbyctrl_iter != accessesbyctrl.end()){ - hasDomainAccess = true; + + //if actor is NOT owner, check for permissions + if(actor.value != domain_owner) { + + name grantor_account = name(domain_owner); + name grantee_account = actor; + string permctrl = + grantor_account.to_string() + REGISTER_ADDRESS_ON_DOMAIN_OBJECT_TYPE + fa.fiodomain + REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME; + uint128_t permctrlhash = string_to_uint128_hash(permctrl.c_str()); + auto permissionbyctrl = permissions_table.get_index<"bypermctrl"_n>(); + auto permsbypermctrl_iter = permissionbyctrl.find(permctrlhash); + + if (permsbypermctrl_iter == permissionbyctrl.end()) { + permissionbyctrl = permissions_table.get_index<"bypermctrl"_n>(); + permctrl = + grantor_account.to_string() + REGISTER_ADDRESS_ON_DOMAIN_OBJECT_TYPE + "*" + REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME; + permctrlhash = string_to_uint128_hash(permctrl.c_str()); + permsbypermctrl_iter = permissionbyctrl.find(permctrlhash); + + } + + if(permsbypermctrl_iter != permissionbyctrl.end()){ + string accessctrl = actor.to_string() + to_string(permsbypermctrl_iter->id); + uint128_t accessctrlhash = string_to_uint128_hash(accessctrl.c_str()); + auto accessesbyctrl = accesses_table.get_index<"byaccess"_n>(); + auto accessesbyctrl_iter = accessesbyctrl.find(accessctrlhash); + if (accessesbyctrl_iter != accessesbyctrl.end()) { + hasDomainAccess = true; + } } } @@ -1377,8 +1394,8 @@ namespace fioio { while (domainiter != domains.end()) { const uint64_t expire = domainiter->expiration; if ((expire + DOMAINWAITFORBURNDAYS) < nowtime) { - - string permcontrol = REGISTER_ADDRESS_ON_DOMAIN_OBJECT_TYPE + domainiter->name + REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME; + name grantor_account = name(domainiter->account); + string permcontrol = grantor_account.to_string() + REGISTER_ADDRESS_ON_DOMAIN_OBJECT_TYPE + domainiter->name + REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME; const uint128_t permcontrolHash = string_to_uint128_hash(permcontrol.c_str()); auto permissionsbycontrolhash = permissions_table.get_index<"bypermctrl"_n>(); auto permctrl_iter = permissionsbycontrolhash.find(permcontrolHash); @@ -1389,7 +1406,7 @@ namespace fioio { permission_level{get_self(), "active"_n}, "fio.perms"_n, "clearperm"_n, - std::make_tuple(REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME, domainiter->name) + std::make_tuple(grantor_account, REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME, domainiter->name) ).send(); // increment the number record processed by one @@ -2454,13 +2471,23 @@ namespace fioio { //note we limit grantees to 100 in the protocol to permit this kind of operation. //see fio.perms.hpp MAX_GRANTEES documentation for further details. //FIP-40 - action( - permission_level{get_self(), "active"_n}, - "fio.perms"_n, - "clearperm"_n, - std::make_tuple(REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME, fio_domain) - ).send(); + string permcontrol = actor.to_string() + REGISTER_ADDRESS_ON_DOMAIN_OBJECT_TYPE + fio_domain + REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME; + const uint128_t permcontrolHash = string_to_uint128_hash(permcontrol.c_str()); + auto permissionsbycontrolhash = permissions_table.get_index<"bypermctrl"_n>(); + auto permctrl_iter = permissionsbycontrolhash.find(permcontrolHash); + if (permctrl_iter != permissionsbycontrolhash.end() ) { + // clear all the permissions for this domain. + //FIP-40 + action( + permission_level{get_self(), "active"_n}, + "fio.perms"_n, + "clearperm"_n, + std::make_tuple(actor, REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME, fio_domain) + ).send(); + } + + //fees const uint64_t fee_amount = fee_iter->suf_amount; const uint64_t fee_type = fee_iter->type; diff --git a/contracts/fio.common/fio_common_validator.hpp b/contracts/fio.common/fio_common_validator.hpp index 7485ae76..88a1145b 100644 --- a/contracts/fio.common/fio_common_validator.hpp +++ b/contracts/fio.common/fio_common_validator.hpp @@ -66,6 +66,22 @@ namespace fioio { return true; } + inline bool validateFioNameFormatTPID(const FioAddress &fa) { + if (fa.domainOnly) { + return false; + } else { + if (fa.fioaddress.size() < 3 || fa.fioaddress.size() > maxFioLen) { + return false; + } + if (!validateCharName(fa.fioname) || !validateCharName(fa.fiodomain)) { + return false; + }; + } + + return true; + } + + inline bool validateFioNameFormat(const FioAddress &fa) { if (fa.domainOnly) { if (fa.fiodomain.size() < 1 || fa.fiodomain.size() > maxFioDomainLen) { @@ -107,7 +123,7 @@ namespace fioio { if (tpid.size() > 0) { FioAddress fa; getFioAddressStruct(tpid, fa); - return validateFioNameFormat(fa); + return validateFioNameFormatTPID(fa); } return true; } diff --git a/contracts/fio.perms/fio.perms.abi b/contracts/fio.perms/fio.perms.abi index 475048da..f7de9c0a 100644 --- a/contracts/fio.perms/fio.perms.abi +++ b/contracts/fio.perms/fio.perms.abi @@ -146,6 +146,10 @@ "name": "clearperm", "base": "", "fields": [ + { + "name": "grantor_account", + "type": "name" + }, { "name": "permission_name", "type": "string" diff --git a/contracts/fio.perms/fio.perms.cpp b/contracts/fio.perms/fio.perms.cpp index 2a34bea8..8b869505 100644 --- a/contracts/fio.perms/fio.perms.cpp +++ b/contracts/fio.perms/fio.perms.cpp @@ -101,57 +101,59 @@ namespace fioio { fio_400_assert(permission_name.length() > 0, "permission_name", permission_name, - "Permission name is invalid", ErrorInvalidPermissionName); + "Permission name is invalid.", ErrorInvalidPermissionName); // error if permission name is not the expected name register_address_on_domain. //one permission name is integrated for fip 40, modify this logic for any new permission names //being supported fio_400_assert(useperm.compare(REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME) == 0, "permission_name", permission_name, - "Permission name is invalid", ErrorInvalidPermissionName); + "Permission name is invalid.", ErrorInvalidPermissionName); // error if permission info is not empty. fio_400_assert(permission_info.size() == 0, "permission_info", permission_info, - "Permission info is invalid", ErrorInvalidPermissionInfo); + "Permission Info is invalid.", ErrorInvalidPermissionInfo); // error if object name is not * or is not in the domains table fio_400_assert(object_name.size() > 0, "object_name", object_name, - "Object name is invalid", ErrorInvalidObjectName); + "Object Name is invalid.", ErrorInvalidObjectName); - //verify domain name, and that domain is owned by the actor account. - FioAddress fa; - getFioAddressStruct(object_name, fa); + if(object_name.compare("*")!=0) { + //verify domain name, and that domain is owned by the actor account. + FioAddress fa; + getFioAddressStruct(object_name, fa); - fio_400_assert(fa.domainOnly, "domonlyobject_name", object_name, "Invalid object name", - ErrorInvalidObjectName); + fio_400_assert(fa.domainOnly, "object_name", object_name, "Object Name is invalid.", + ErrorInvalidObjectName); - const uint128_t domainHash = string_to_uint128_hash(fa.fiodomain.c_str()); - auto domainsbyname = domains.get_index<"byname"_n>(); - auto domains_iter = domainsbyname.find(domainHash); + const uint128_t domainHash = string_to_uint128_hash(fa.fiodomain.c_str()); + auto domainsbyname = domains.get_index<"byname"_n>(); + auto domains_iter = domainsbyname.find(domainHash); - fio_400_assert(domains_iter != domainsbyname.end(), "domnotfound object_name", object_name, - "Invalid object name", - ErrorInvalidObjectName); + fio_400_assert(domains_iter != domainsbyname.end(), "object_name", object_name, + "Object Name is invalid.", + ErrorInvalidObjectName); - //add 30 days to the domain expiration, this call will work until 30 days past expire. - const uint32_t domain_expiration = get_time_plus_seconds(domains_iter->expiration, SECONDS30DAYS); + //add 30 days to the domain expiration, this call will work until 30 days past expire. + const uint32_t domain_expiration = get_time_plus_seconds(domains_iter->expiration, SECONDS30DAYS); - const uint32_t present_time = now(); - fio_400_assert(present_time <= domain_expiration, "domexpired object_name", object_name, "Invalid object name", - ErrorInvalidObjectName); + const uint32_t present_time = now(); + fio_400_assert(present_time <= domain_expiration, "object_name", object_name, "Object Name is invalid.", + ErrorInvalidObjectName); - //check domain owner is actor - fio_400_assert((actor.value == domains_iter->account), "actornotowner object_name", object_name, - "Invalid object name", ErrorInvalidObjectName); + //check domain owner is actor + fio_400_assert((actor.value == domains_iter->account), "object_name", object_name, + "Object Name is invalid.", ErrorInvalidObjectName); + } //check grantee exists. fio_400_assert(is_account(grantee_account), "grantee_account", grantee_account.to_string(), - "grantee account is invalid", ErrorInvalidGranteeAccount); + "Account is invalid or does not exist.", ErrorInvalidGranteeAccount); fio_400_assert((grantee_account.value != actor.value), "grantee_account", grantee_account.to_string(), - "grantee account is invalid", ErrorInvalidGranteeAccount); + "Account is invalid or does not exist.", ErrorInvalidGranteeAccount); //error if the grantee account already has this permission. - string permcontrol = REGISTER_ADDRESS_ON_DOMAIN_OBJECT_TYPE + object_name + useperm; + string permcontrol = actor.to_string() + REGISTER_ADDRESS_ON_DOMAIN_OBJECT_TYPE + object_name + useperm; const uint128_t permcontrolhash = string_to_uint128_hash(permcontrol.c_str()); const uint128_t objectnamehash = string_to_uint128_hash(object_name); auto accessbyhash = accesses.get_index<"byaccess"_n>(); @@ -286,55 +288,60 @@ namespace fioio { fio_400_assert(permission_name.length() > 0, "permission_name", permission_name, - "Permission name is invalid", ErrorInvalidPermissionName); + "Permission name is invalid.", ErrorInvalidPermissionName); string useperm = makeLowerCase(permission_name); // error if permission name is not the expected name register_address_on_domain. fio_400_assert(useperm.compare(REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME) == 0, "permission_name", permission_name, - "Permission name is invalid", ErrorInvalidPermissionName); + "Permission name is invalid.", ErrorInvalidPermissionName); // error if object name is not * or is not in the domains table fio_400_assert(object_name.size() > 0, "object_name", object_name, - "Object name is invalid", ErrorInvalidObjectName); + "Object Name is invalid.", ErrorInvalidObjectName); - //verify domain name, and that domain is owned by the actor account. - FioAddress fa; - getFioAddressStruct(object_name, fa); + //todo handle the * - fio_400_assert(fa.domainOnly, "object_name", object_name, "Invalid object name", - ErrorInvalidObjectName); + if(object_name.compare("*")!= 0) { - const uint128_t domainHash = string_to_uint128_hash(fa.fiodomain.c_str()); - auto domainsbyname = domains.get_index<"byname"_n>(); - auto domains_iter = domainsbyname.find(domainHash); + //verify domain name, and that domain is owned by the actor account. + FioAddress fa; + getFioAddressStruct(object_name, fa); - fio_400_assert(domains_iter != domainsbyname.end(), "object_name", object_name, - "Invalid object name", - ErrorInvalidObjectName); + fio_400_assert(fa.domainOnly, "object_name", object_name, "Object Name is invalid.", + ErrorInvalidObjectName); - //add 30 days to the domain expiration, this call will work until 30 days past expire. - const uint32_t domain_expiration = get_time_plus_seconds(domains_iter->expiration, SECONDS30DAYS); - const uint32_t present_time = now(); + const uint128_t domainHash = string_to_uint128_hash(fa.fiodomain.c_str()); + auto domainsbyname = domains.get_index<"byname"_n>(); + auto domains_iter = domainsbyname.find(domainHash); - fio_400_assert(present_time <= domain_expiration, "object_name", object_name, "Invalid object name", - ErrorInvalidObjectName); + fio_400_assert(domains_iter != domainsbyname.end(), "object_name", object_name, + "Object Name is invalid.", + ErrorInvalidObjectName); + + //add 30 days to the domain expiration, this call will work until 30 days past expire. + const uint32_t domain_expiration = get_time_plus_seconds(domains_iter->expiration, SECONDS30DAYS); + const uint32_t present_time = now(); - //check domain owner is actor - fio_400_assert((actor.value == domains_iter->account), "object_name", object_name, - "Invalid object name", ErrorInvalidObjectName); + fio_400_assert(present_time <= domain_expiration, "object_name", object_name, "Object Name is invalid.", + ErrorInvalidObjectName); + + //check domain owner is actor + fio_400_assert((actor.value == domains_iter->account), "object_name", object_name, + "Object Name is invalid.", ErrorInvalidObjectName); + } //check grantee exists. fio_400_assert(is_account(grantee_account), "grantee_account", grantee_account.to_string(), - "grantee account is invalid", ErrorInvalidGranteeAccount); + "Account is invalid or does not exist.", ErrorInvalidGranteeAccount); fio_400_assert((grantee_account.value != actor.value), "grantee_account", grantee_account.to_string(), - "grantee account is invalid", ErrorInvalidGranteeAccount); + "Account is invalid or does not exist.", ErrorInvalidGranteeAccount); //error if the grantee account already has this permission. - string permcontrol = REGISTER_ADDRESS_ON_DOMAIN_OBJECT_TYPE + object_name + REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME; + string permcontrol = actor.to_string() + REGISTER_ADDRESS_ON_DOMAIN_OBJECT_TYPE + object_name + REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME; const uint128_t permcontrolHash = string_to_uint128_hash(permcontrol.c_str()); @@ -407,6 +414,7 @@ namespace fioio { [[eosio::action]] void clearperm( + const name &grantor_account, const string &permission_name, const string &object_name ) { @@ -419,26 +427,26 @@ namespace fioio { fio_400_assert(permission_name.length() > 0, "permission_name", permission_name, - "Permission name is invalid", ErrorInvalidPermissionName); + "Permission name is invalid.", ErrorInvalidPermissionName); string useperm = makeLowerCase(permission_name); // error if permission name is not the expected name register_address_on_domain. fio_400_assert(useperm.compare(REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME) == 0, "permission_name", permission_name, - "Permission name is invalid", ErrorInvalidPermissionName); + "Permission name is invalid.", ErrorInvalidPermissionName); // error if object name is not * or is not in the domains table fio_400_assert(object_name.size() > 0, "object_name", object_name, - "Object name is invalid", ErrorInvalidObjectName); + "Object Name is invalid.", ErrorInvalidObjectName); //verify domain name, and that domain is owned by the actor account. FioAddress fa; getFioAddressStruct(object_name, fa); - fio_400_assert(fa.domainOnly, "object_name", object_name, "Invalid object name", + fio_400_assert(fa.domainOnly, "object_name", object_name, "Object Name is invalid.", ErrorInvalidObjectName); - string permcontrol = REGISTER_ADDRESS_ON_DOMAIN_OBJECT_TYPE + object_name + REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME; + string permcontrol = grantor_account.to_string() + REGISTER_ADDRESS_ON_DOMAIN_OBJECT_TYPE + object_name + REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME; const uint128_t permcontrolHash = string_to_uint128_hash(permcontrol.c_str()); diff --git a/contracts/fio.perms/fio.perms.hpp b/contracts/fio.perms/fio.perms.hpp index 507b963c..2913c8e7 100644 --- a/contracts/fio.perms/fio.perms.hpp +++ b/contracts/fio.perms/fio.perms.hpp @@ -49,7 +49,7 @@ namespace fioio { string permission_name = ""; uint128_t permission_name_hash = 0; //by convention we will store the hashed value of the following concatination in this field to provide a - //unique search key by object_type, object_name, and permission_name + //unique search key by grantor object_type, object_name, and permission_name uint128_t permission_control_hash = 0; uint64_t grantor_account = 0; //this field can contain any string based info that is useful for the permission. From 3f476b3f83d11ac7c778ae447aac1add75d89e46 Mon Sep 17 00:00:00 2001 From: Ed Rotthoff Date: Mon, 1 May 2023 12:17:27 -0600 Subject: [PATCH 13/15] FIP-40 final prep for merge FIP-40 final prep for merge --- contracts/fio.address/fio.address.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/contracts/fio.address/fio.address.cpp b/contracts/fio.address/fio.address.cpp index 2e526dd3..a540ce5e 100644 --- a/contracts/fio.address/fio.address.cpp +++ b/contracts/fio.address/fio.address.cpp @@ -286,9 +286,7 @@ namespace fioio { const bool isPublic = domains_iter->is_public; uint64_t domain_owner = domains_iter->account; - - - //object_type, object_name, and permission_name get hashed for permission control hash + bool hasDomainAccess = false; //if actor is NOT owner, check for permissions From 48b75ab79b3c6b88f8b3c0868efd37d5acb01477 Mon Sep 17 00:00:00 2001 From: Ed Rotthoff Date: Mon, 1 May 2023 12:21:22 -0600 Subject: [PATCH 14/15] FIP-40 final merge prep FIP-40 final merge prep --- contracts/fio.address/fio.address.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/fio.address/fio.address.cpp b/contracts/fio.address/fio.address.cpp index a540ce5e..be05168a 100644 --- a/contracts/fio.address/fio.address.cpp +++ b/contracts/fio.address/fio.address.cpp @@ -286,7 +286,7 @@ namespace fioio { const bool isPublic = domains_iter->is_public; uint64_t domain_owner = domains_iter->account; - + bool hasDomainAccess = false; //if actor is NOT owner, check for permissions @@ -2079,7 +2079,7 @@ namespace fioio { uint16_t counter = 0; auto nft_iter = contractsbyname.begin(); while (nftburnq_iter != burnqbyname.end()) { - nft_iter = contractsbyname.find(nftburnq_iter->fio_address_hash); //search delay. + nft_iter = contractsbyname.find(nftburnq_iter->fio_address_hash); counter++; if (nft_iter != contractsbyname.end()) { // if row, delete an nft nft_iter = contractsbyname.erase(nft_iter); From 4d5fa02feb8114ab39281a543d07dbf5cbffc371 Mon Sep 17 00:00:00 2001 From: Ed Rotthoff Date: Mon, 1 May 2023 12:31:08 -0600 Subject: [PATCH 15/15] FIP-40 prep for merge into develop FIP-40 perp for merge into develop --- contracts/fio.perms/fio.perms.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/contracts/fio.perms/fio.perms.cpp b/contracts/fio.perms/fio.perms.cpp index 8b869505..9a66fb6d 100644 --- a/contracts/fio.perms/fio.perms.cpp +++ b/contracts/fio.perms/fio.perms.cpp @@ -271,6 +271,10 @@ namespace fioio { } + /* + * This action will remove the specified account from the accesses table, if the resulting permission + * has no remaining grantees then the permission will be removed from the permissions table. + */ [[eosio::action]] void remperm(const name &grantee_account, @@ -300,8 +304,6 @@ namespace fioio { fio_400_assert(object_name.size() > 0, "object_name", object_name, "Object Name is invalid.", ErrorInvalidObjectName); - //todo handle the * - if(object_name.compare("*")!= 0) { //verify domain name, and that domain is owned by the actor account. @@ -339,8 +341,6 @@ namespace fioio { fio_400_assert((grantee_account.value != actor.value), "grantee_account", grantee_account.to_string(), "Account is invalid or does not exist.", ErrorInvalidGranteeAccount); - - //error if the grantee account already has this permission. string permcontrol = actor.to_string() + REGISTER_ADDRESS_ON_DOMAIN_OBJECT_TYPE + object_name + REGISTER_ADDRESS_ON_DOMAIN_PERMISSION_NAME; const uint128_t permcontrolHash = string_to_uint128_hash(permcontrol.c_str()); @@ -411,6 +411,8 @@ namespace fioio { } + //This action will clear the specified permission for the specified object and specified grantor account. + //all granted accesses will be removed. [[eosio::action]] void clearperm(