Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

consider: standard action parameter validation #7

Open
James-Mart opened this issue Feb 16, 2022 · 0 comments
Open

consider: standard action parameter validation #7

James-Mart opened this issue Feb 16, 2022 · 0 comments
Labels
enhancement New feature or request

Comments

@James-Mart
Copy link
Member

Standard Action Parameter Validation Proposal

Problem

A parameter of a specific type often has the same validation logic in each action in which its included. For example, if there exists a type RegisteredUser that wraps a uint64_t, then every action that takes a RegisteredUser would need to verify that the value passed in matches an actual registered user.

This forces the developer to repeat the same validation logic in many actions, increasing the RAM size of the contract executable. Furthermore, it bloats unit tests, since every test suite of a particular action would need a test case verifying that the action correctly reacts to improper values passed into this action.

Solution Description

A solution should allow a developer to define validation logic on custom types in one place, and anywhere that object is constructed as an action parameter would automatically run the validation logic. This would:

  • Be less error prone, increasing contract security
  • Result in smaller binaries, decreasing developer cost
  • Be easier to write, decreasing development time

Solution Implementation

In contract

Add an object with custom validation logic:

struct uid {
    uid();
    uid(const uint32_t account_id);
 
    void validate();
 
    uint32_t id;
};

In Unit Tests

Because the “validate” function exists, it will automatically run whenever an object of this type is created before passing it into an action handler.

This obviates the need for tests like this:

THEN("Alice may not transfer to a nonexistent recipient")
{
    CHECK(failed_with(alice.trace<eosio_nft::actions::transfer>(nft_id_a, 
          uid("alice"_n), uid{}, "Enjoy"), "Empty UID object"));
}

In Dispatcher

   template<typename T>
   concept has_validate = requires {
      { std::declval<T>().validate() } -> std::same_as<void>;
   };
 
   template<typename T>
   void validate_param(T param) { /* NOP */ }
 
   template<typename T>
   requires has_validate<T>
   void validate_param(T param)
   {
      param.validate();
   }

In execute_action:

    std::tuple<std::decay_t<Args>...> args;
    datastream<const char*> ds((char*)buffer, size);
    ds >> args;

   // Validate parameters that support validation
    std::apply([&](auto&&... param) {
        ( ( validate_param(param) ) , ...);
    }, args);
 
     T inst(self, code, ds);

Other Considerations

Thanks to @tbfleming for the input

In a specific case where the object with custom validation is just a simple wrapper around a primitive type, serializing it as a custom object forces the front-end devs to pass an object as a parameter to a smart contract rather than just the underlying value.

They shouldn’t need to know or care about the fact that there is custom validation going on. The ABI should only tell them about the underlying type and allow them to pass in the underlying type.

To achieve this, we would need:

  • Custom to_json and from_json logic written for all types with custom validation (Maybe able to automate this such that devs only need to inherit from a special type)
    • As seen in asset.hpp in abieos
  • Update to the get_type function in abi_generator.hpp in clsdk/eosiolib/contracts/
@James-Mart James-Mart added the enhancement New feature or request label Feb 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant