action.hpp 7.8 KB
Newer Older
1 2 3 4
/**
 *  @file
 *  @copyright defined in eos/LICENSE.txt
 */
5
#pragma once
6 7 8
#include <eosiolib/action.h>
#include <eosiolib/datastream.hpp>
#include <eosiolib/serialize.hpp>
9

10 11 12 13 14
#include <boost/preprocessor/variadic/size.hpp>
#include <boost/preprocessor/variadic/to_tuple.hpp>
#include <boost/preprocessor/tuple/enum.hpp>
#include <boost/preprocessor/facilities/overload.hpp>

P
Pravin 已提交
15
namespace eosio {
16 17

   /**
18 19 20
    * @defgroup actioncppapi Action C++ API
    * @ingroup actionapi
    * @brief Type-safe C++ wrapers for Action C API
21
    *
22
    * @note There are some methods from the @ref actioncapi that can be used directly from C++
23
    *
24
    * @{
25 26 27
    */

   /**
28
    *
29
    *  This method unpacks the current action at type T.
30
    *
31
    *  @brief Interpret the action body as type T
32
    *
33
    *  Example:
34
    *  @code
35
    *  struct dummy_action {
36 37 38
    *    char a; //1
    *    unsigned long long b; //8
    *    int  c; //4
39 40
    *
    *    EOSLIB_SERIALIZE( dummy_action, (a)(b)(c) )
41
    *  };
42
    *  dummy_action msg = unpack_action_data<dummy_action>();
43
    *  @endcode
44
    */
45
   template<typename T>
46 47 48
   T unpack_action_data() {
      char buffer[action_data_size()];
      read_action_data( buffer, sizeof(buffer) );
49
      return unpack<T>( buffer, sizeof(buffer) );
50 51
   }

52
   using ::require_auth;
53
   using ::require_recipient;
54 55

   /**
56
    *  All of the listed accounts will be added to the set of accounts to be notified
57
    *
58
    *  This helper method enables you to add multiple accounts to accounts to be notified list with a single
59 60
    *  call rather than having to call the similar C API multiple times.
    *
61
    *  @note action.code is also considered as part of the set of notified accounts
62 63
    *
    *  @brief Verify specified accounts exist in the set of notified accounts
64 65
    *
    *  Example:
66
    *  @code
67
    *  require_recipient(N(Account1), N(Account2), N(Account3)); // throws exception if any of them not in set.
68
    *  @endcode
69
    */
70
   template<typename... accounts>
71
   void require_recipient( account_name name, accounts... remaining_accounts ){
72 73
      require_recipient( name );
      require_recipient( remaining_accounts... );
74 75
   }

D
Daniel Larimer 已提交
76
   struct permission_level {
77 78 79
      permission_level( account_name a, permission_name p ):actor(a),permission(p){}
      permission_level(){}

D
Daniel Larimer 已提交
80 81
      account_name    actor;
      permission_name permission;
82

D
Daniel Larimer 已提交
83 84 85 86
      friend bool operator == ( const permission_level& a, const permission_level& b ) {
         return std::tie( a.actor, a.permission ) == std::tie( b.actor, b.permission );
      }

87
      EOSLIB_SERIALIZE( permission_level, (actor)(permission) )
D
Daniel Larimer 已提交
88
   };
89

90 91 92 93
   void require_auth(const permission_level& level) {
      require_auth2( level.actor, level.permission );
   }

D
Daniel Larimer 已提交
94 95 96 97
   /**
    * This is the packed representation of an action along with
    * meta-data about the authorization levels.
    */
D
Daniel Larimer 已提交
98
   struct action {
99
      account_name               account;
D
Daniel Larimer 已提交
100 101
      action_name                name;
      vector<permission_level>   authorization;
102 103 104
      bytes                      data;

      action() = default;
D
Daniel Larimer 已提交
105

D
Daniel Larimer 已提交
106 107
      /**
       *  @tparam Action - a type derived from action_meta<Scope,Name>
108
       *  @param value - will be serialized via pack into data
D
Daniel Larimer 已提交
109 110
       */
      template<typename Action>
111 112
      action( vector<permission_level>&& auth, const Action& value ) {
         account       = Action::get_account();
D
Daniel Larimer 已提交
113
         name          = Action::get_name();
D
Daniel Larimer 已提交
114
         authorization = move(auth);
115
         data          = pack(value);
D
Daniel Larimer 已提交
116
      }
117

118 119
      /**
       *  @tparam Action - a type derived from action_meta<Scope,Name>
120
       *  @param value - will be serialized via pack into data
121 122 123
       */
      template<typename Action>
      action( const permission_level& auth, const Action& value )
124
      :authorization(1,auth) {
125 126
         account       = Action::get_account();
         name          = Action::get_name();
127
         data          = pack(value);
128 129
      }

M
Matias Romeo 已提交
130 131 132 133 134 135 136 137 138 139 140
      /**
       *  @tparam Action - a type derived from action_meta<Scope,Name>
       *  @param value - will be serialized via pack into data
       */
      template<typename Action>
      action( const Action& value ) {
         account       = Action::get_account();
         name          = Action::get_name();
         data          = pack(value);
      }

D
Daniel Larimer 已提交
141
      /**
142 143 144 145
       *  @tparam T - the type of the action data
       *  @param auth - a single permission_level to be used as the authorization of the action
       *  @param a - name of the contract account
       *  @param n - name of the action
D
Daniel Larimer 已提交
146 147
       *  @param value - will be serialized via pack into data
       */
148 149 150 151 152 153 154 155 156 157 158 159 160 161
      template<typename T>
      action( const permission_level& auth, account_name a, action_name n, T&& value )
      :account(a), name(n), authorization(1,auth), data(pack(std::forward<T>(value))) {}

      /**
       *  @tparam T - the type of the action data
       *  @param auths - vector permission_levels defining the authorizations of the action
       *  @param a - name of the contract account
       *  @param n - name of the action
       *  @param value - will be serialized via pack into data
       */
      template<typename T>
      action( vector<permission_level> auths, account_name a, action_name n, T&& value )
      :account(a), name(n), authorization(std::move(auths)), data(pack(std::forward<T>(value))) {}
D
Daniel Larimer 已提交
162

163
      EOSLIB_SERIALIZE( action, (account)(name)(authorization)(data) )
164 165

      void send() const {
166
         auto serialize = pack(*this);
167
         ::send_inline(serialize.data(), serialize.size());
M
Matias Romeo 已提交
168 169 170 171 172 173
      }

      void send_context_free() const {
         eosio_assert( authorization.size() == 0, "context free actions cannot have authorizations");
         auto serialize = pack(*this);
         ::send_context_free_inline(serialize.data(), serialize.size());
174
      }
175 176 177 178 179 180 181 182

      /**
       * Retrieve the unpacked data as T
       * @tparam T expected type of data
       * @return the action data
       */
      template<typename T>
      T data_as() {
K
Kevin Heifner 已提交
183 184 185
         eosio_assert( name == T::get_name(), "Invalid name" );
         eosio_assert( account == T::get_account(), "Invalid account" );
         return unpack<T>( &data[0], data.size() );
186 187
      }

D
Daniel Larimer 已提交
188 189
   };

D
Daniel Larimer 已提交
190
   template<uint64_t Account, uint64_t Name>
D
Daniel Larimer 已提交
191
   struct action_meta {
D
Daniel Larimer 已提交
192 193
      static uint64_t get_account() { return Account; }
      static uint64_t get_name()  { return Name; }
D
Daniel Larimer 已提交
194 195
   };

196 197 198 199 200
   template<typename... Args>
   void dispatch_inline( account_name code, action_name act,
                         vector<permission_level> perms,
                         std::tuple<Args...> args ) {
      action( perms, code, act, std::move(args) ).send();
201 202
   }

203 204 205 206 207 208
   template<typename, uint64_t>
   struct inline_dispatcher;

   template<typename T, uint64_t Name, typename... Args>
   struct inline_dispatcher<void(T::*)(Args...), Name> {
      static void call(account_name code, const permission_level& perm, std::tuple<Args...> args) {
209
         dispatch_inline(code, Name, vector<permission_level>(1, perm), std::move(args));
210 211
      }
      static void call(account_name code, vector<permission_level> perms, std::tuple<Args...> args) {
212
         dispatch_inline(code, Name, std::move(perms), std::move(args));
213 214
      }
   };
215

216
 ///@} actioncpp api
217

D
Daniel Larimer 已提交
218 219
} // namespace eosio

220 221 222 223 224 225 226 227 228 229 230 231
#define INLINE_ACTION_SENDER3( CONTRACT_CLASS, FUNCTION_NAME, ACTION_NAME  )\
::eosio::inline_dispatcher<decltype(&CONTRACT_CLASS::FUNCTION_NAME), ACTION_NAME>::call

#define INLINE_ACTION_SENDER2( CONTRACT_CLASS, NAME )\
INLINE_ACTION_SENDER3( CONTRACT_CLASS, NAME, ::eosio::string_to_name(#NAME) )

#define INLINE_ACTION_SENDER(...) BOOST_PP_OVERLOAD(INLINE_ACTION_SENDER,__VA_ARGS__)(__VA_ARGS__)

#define SEND_INLINE_ACTION( CONTRACT, NAME, ... )\
INLINE_ACTION_SENDER(std::decay_t<decltype(CONTRACT)>, NAME)( (CONTRACT).get_self(),\
BOOST_PP_TUPLE_ENUM(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__), BOOST_PP_VARIADIC_TO_TUPLE(__VA_ARGS__)) );

232

233
#define ACTION( CODE, NAME ) struct NAME : ::eosio::action_meta<CODE, ::eosio::string_to_name(#NAME) >