Wire Sysio Wire Sysion 1.0.0
Loading...
Searching...
No Matches
txn_test_gen_plugin.cpp
Go to the documentation of this file.
5
6#include <fc/variant.hpp>
7#include <fc/io/json.hpp>
10#include <fc/io/json.hpp>
11
12#include <boost/asio/high_resolution_timer.hpp>
13#include <boost/algorithm/clamp.hpp>
14
15#include <Inline/BasicTypes.h>
16#include <IR/Module.h>
17#include <IR/Validate.h>
18#include <WAST/WAST.h>
19#include <WASM/WASM.h>
20#include <Runtime/Runtime.h>
21
22#include <contracts.hpp>
23
24using namespace sysio::testing;
25
26namespace sysio { namespace detail {
29 string status;
30 };
31}}
32
35
36namespace sysio {
37
38static appbase::abstract_plugin& _txn_test_gen_plugin = app().register_plugin<txn_test_gen_plugin>();
39
40using namespace sysio::chain;
41
42#define CALL(api_name, api_handle, call_name, INVOKE, http_response_code) \
43{std::string("/v1/" #api_name "/" #call_name), \
44 [this](string, string body, url_response_callback cb) mutable { \
45 try { \
46 if (body.empty()) body = "{}"; \
47 INVOKE \
48 cb(http_response_code, fc::variant(result)); \
49 } catch (...) { \
50 http_plugin::handle_exception(#api_name, #call_name, body, cb); \
51 } \
52 }}
53
54#define INVOKE_V_R_R_R(api_handle, call_name, in_param0, in_param1, in_param2) \
55 const auto& vs = fc::json::json::from_string(body).as<fc::variants>(); \
56 auto status = api_handle->call_name(vs.at(0).as<in_param0>(), vs.at(1).as<in_param1>(), vs.at(2).as<in_param2>()); \
57 sysio::detail::txn_test_gen_status result = { status };
58
59#define INVOKE_V_R_R(api_handle, call_name, in_param0, in_param1) \
60 const auto& vs = fc::json::json::from_string(body).as<fc::variants>(); \
61 api_handle->call_name(vs.at(0).as<in_param0>(), vs.at(1).as<in_param1>()); \
62 sysio::detail::txn_test_gen_empty result;
63
64#define INVOKE_V_V(api_handle, call_name) \
65 api_handle->call_name(); \
66 sysio::detail::txn_test_gen_empty result;
67
68#define CALL_ASYNC(api_name, api_handle, call_name, INVOKE, http_response_code) \
69{std::string("/v1/" #api_name "/" #call_name), \
70 [this](string, string body, url_response_callback cb) mutable { \
71 if (body.empty()) body = "{}"; \
72 /*plugin processes many transactions, report only first to avoid http_plugin having to deal with multiple responses*/ \
73 auto times_called = std::make_shared<std::atomic<size_t>>(0);\
74 auto result_handler = [times_called{std::move(times_called)}, cb, body](const fc::exception_ptr& e) mutable {\
75 if( ++(*times_called) > 1 ) return;\
76 if (e) {\
77 try {\
78 e->dynamic_rethrow_exception();\
79 } catch (...) {\
80 http_plugin::handle_exception(#api_name, #call_name, body, cb);\
81 }\
82 } else {\
83 cb(http_response_code, fc::variant(sysio::detail::txn_test_gen_empty())); \
84 }\
85 };\
86 INVOKE \
87 }\
88}
89
90#define INVOKE_ASYNC_R_R(api_handle, call_name, in_param0, in_param1) \
91 const auto& vs = fc::json::json::from_string(body).as<fc::variants>(); \
92 api_handle->call_name(vs.at(0).as<in_param0>(), vs.at(1).as<in_param1>(), result_handler);
93
95
98
100 std::optional<sysio::chain::named_thread_pool> thread_pool;
101 std::shared_ptr<boost::asio::high_resolution_timer> timer;
105
106 void push_next_transaction(const std::shared_ptr<std::vector<signed_transaction>>& trxs, const std::function<void(const fc::exception_ptr&)>& next ) {
108
109 for (size_t i = 0; i < trxs->size(); ++i) {
110 cp.accept_transaction( std::make_shared<packed_transaction>(trxs->at(i)), [=](const std::variant<fc::exception_ptr, transaction_trace_ptr>& result){
111
112 fc::exception_ptr except_ptr;
113 if (std::holds_alternative<fc::exception_ptr>(result)) {
114 except_ptr = std::get<fc::exception_ptr>(result);
115 } else if (std::get<transaction_trace_ptr>(result)->except) {
116 except_ptr = std::get<transaction_trace_ptr>(result)->except->dynamic_copy_exception();
117 }
118
119 if (except_ptr) {
120 next(std::get<fc::exception_ptr>(result));
121 } else {
122 if (std::holds_alternative<transaction_trace_ptr>(result) && std::get<transaction_trace_ptr>(result)->receipt) {
123 _total_us += std::get<transaction_trace_ptr>(result)->receipt->cpu_usage_us;
124 ++_txcount;
125 }
126 }
127 });
128 }
129 }
130
131 void push_transactions( std::vector<signed_transaction>&& trxs, const std::function<void(fc::exception_ptr)>& next ) {
132 auto trxs_copy = std::make_shared<std::decay_t<decltype(trxs)>>(std::move(trxs));
133 app().post(priority::low, [this, trxs_copy, next]() {
134 push_next_transaction(trxs_copy, next);
135 });
136 }
137
138 void create_test_accounts(const std::string& init_name, const std::string& init_priv_key, const std::function<void(const fc::exception_ptr&)>& next) {
139 ilog("create_test_accounts");
140 std::vector<signed_transaction> trxs;
141 trxs.reserve(2);
142
143 try {
144 name creator(init_name);
145
146 abi_def currency_abi_def = fc::json::from_string(contracts::sysio_token_abi().data()).as<abi_def>();
147
148 controller& cc = app().get_plugin<chain_plugin>().chain();
149 auto chainid = app().get_plugin<chain_plugin>().get_chain_id();
150 auto abi_serializer_max_time = app().get_plugin<chain_plugin>().get_abi_serializer_max_time();
151
152 abi_serializer sysio_token_serializer{fc::json::from_string(contracts::sysio_token_abi().data()).as<abi_def>(),
154
155 fc::crypto::private_key txn_test_receiver_A_priv_key = fc::crypto::private_key::regenerate(fc::sha256(std::string(64, 'a')));
156 fc::crypto::private_key txn_test_receiver_B_priv_key = fc::crypto::private_key::regenerate(fc::sha256(std::string(64, 'b')));
157 fc::crypto::private_key txn_test_receiver_C_priv_key = fc::crypto::private_key::regenerate(fc::sha256(std::string(64, 'c')));
158 fc::crypto::public_key txn_text_receiver_A_pub_key = txn_test_receiver_A_priv_key.get_public_key();
159 fc::crypto::public_key txn_text_receiver_B_pub_key = txn_test_receiver_B_priv_key.get_public_key();
160 fc::crypto::public_key txn_text_receiver_C_pub_key = txn_test_receiver_C_priv_key.get_public_key();
161 fc::crypto::private_key creator_priv_key = fc::crypto::private_key(init_priv_key);
162
163 //create some test accounts
164 {
166
167 //create "A" account
168 {
169 auto owner_auth = sysio::chain::authority{1, {{txn_text_receiver_A_pub_key, 1}}, {}};
170 auto active_auth = sysio::chain::authority{1, {{txn_text_receiver_A_pub_key, 1}}, {}};
171
172 trx.actions.emplace_back(vector<chain::permission_level>{{creator,name("active")}}, newaccount{creator, newaccountA, owner_auth, active_auth});
173 }
174 //create "B" account
175 {
176 auto owner_auth = sysio::chain::authority{1, {{txn_text_receiver_B_pub_key, 1}}, {}};
177 auto active_auth = sysio::chain::authority{1, {{txn_text_receiver_B_pub_key, 1}}, {}};
178
179 trx.actions.emplace_back(vector<chain::permission_level>{{creator,name("active")}}, newaccount{creator, newaccountB, owner_auth, active_auth});
180 }
181 //create "T" account
182 {
183 auto owner_auth = sysio::chain::authority{1, {{txn_text_receiver_C_pub_key, 1}}, {}};
184 auto active_auth = sysio::chain::authority{1, {{txn_text_receiver_C_pub_key, 1}}, {}};
185
186 trx.actions.emplace_back(vector<chain::permission_level>{{creator,name("active")}}, newaccount{creator, newaccountT, owner_auth, active_auth});
187 }
188
189 trx.expiration = cc.head_block_time() + fc::seconds(180);
190 trx.set_reference_block(cc.head_block_id());
191 trx.sign(creator_priv_key, chainid);
192 trxs.emplace_back(std::move(trx));
193 }
194
195 //set newaccountT contract to sysio.token & initialize it
196 {
198
199 vector<uint8_t> wasm = contracts::sysio_token_wasm();
200
201 setcode handler;
202 handler.account = newaccountT;
203 handler.code.assign(wasm.begin(), wasm.end());
204
205 trx.actions.emplace_back( vector<chain::permission_level>{{newaccountT,name("active")}}, handler);
206
207 {
208 setabi handler;
209 handler.account = newaccountT;
210 handler.abi = fc::raw::pack(json::from_string(contracts::sysio_token_abi().data()).as<abi_def>());
211 trx.actions.emplace_back( vector<chain::permission_level>{{newaccountT,name("active")}}, handler);
212 }
213
214 {
215 action act;
216 act.account = newaccountT;
217 act.name = "create"_n;
218 act.authorization = vector<permission_level>{{newaccountT,config::active_name}};
219 act.data = sysio_token_serializer.variant_to_binary("create",
220 fc::json::from_string(fc::format_string("{\"issuer\":\"${issuer}\",\"maximum_supply\":\"1000000000.0000 CUR\"}}",
221 fc::mutable_variant_object()("issuer",newaccountT.to_string()))),
223 trx.actions.push_back(act);
224 }
225 {
226 action act;
227 act.account = newaccountT;
228 act.name = "issue"_n;
229 act.authorization = vector<permission_level>{{newaccountT,config::active_name}};
230 act.data = sysio_token_serializer.variant_to_binary("issue",
231 fc::json::from_string(fc::format_string("{\"to\":\"${to}\",\"quantity\":\"60000.0000 CUR\",\"memo\":\"\"}",
232 fc::mutable_variant_object()("to",newaccountT.to_string()))),
234 trx.actions.push_back(act);
235 }
236 {
237 action act;
238 act.account = newaccountT;
239 act.name = "transfer"_n;
240 act.authorization = vector<permission_level>{{newaccountT,config::active_name}};
241 act.data = sysio_token_serializer.variant_to_binary("transfer",
242 fc::json::from_string(fc::format_string("{\"from\":\"${from}\",\"to\":\"${to}\",\"quantity\":\"20000.0000 CUR\",\"memo\":\"\"}",
243 fc::mutable_variant_object()("from",newaccountT.to_string())("to",newaccountA.to_string()))),
245 trx.actions.push_back(act);
246 }
247 {
248 action act;
249 act.account = newaccountT;
250 act.name = "transfer"_n;
251 act.authorization = vector<permission_level>{{newaccountT,config::active_name}};
252 act.data = sysio_token_serializer.variant_to_binary("transfer",
253 fc::json::from_string(fc::format_string("{\"from\":\"${from}\",\"to\":\"${to}\",\"quantity\":\"20000.0000 CUR\",\"memo\":\"\"}",
254 fc::mutable_variant_object()("from",newaccountT.to_string())("to",newaccountB.to_string()))),
256 trx.actions.push_back(act);
257 }
258
259 trx.expiration = cc.head_block_time() + fc::seconds(180);
260 trx.set_reference_block(cc.head_block_id());
261 trx.max_net_usage_words = 5000;
262 trx.sign(txn_test_receiver_C_priv_key, chainid);
263 trxs.emplace_back(std::move(trx));
264 }
265 } catch ( const std::bad_alloc& ) {
266 throw;
267 } catch ( const boost::interprocess::bad_alloc& ) {
268 throw;
269 } catch (const fc::exception& e) {
270 next(e.dynamic_copy_exception());
271 return;
272 } catch (const std::exception& e) {
273 next(fc::std_exception_wrapper::from_current_exception(e).dynamic_copy_exception());
274 return;
275 }
276
277 push_transactions(std::move(trxs), next);
278 }
279
280 string start_generation(const std::string& salt, const uint64_t& period, const uint64_t& batch_size) {
281 ilog("Starting transaction test plugin");
282 if(running)
283 return "start_generation already running";
284 if(period < 1 || period > 2500)
285 return "period must be between 1 and 2500";
286 if(batch_size < 1 || batch_size > 250)
287 return "batch_size must be between 1 and 250";
288 if(batch_size & 1)
289 return "batch_size must be even";
290 ilog("Starting transaction test plugin valid");
291
292 running = true;
293
294 auto abi_serializer_max_time = app().get_plugin<chain_plugin>().get_abi_serializer_max_time();
295 abi_serializer sysio_token_serializer{fc::json::from_string(contracts::sysio_token_abi().data()).as<abi_def>(), abi_serializer::create_yield_function( abi_serializer_max_time )};
296 //create the actions here
297 act_a_to_b.account = newaccountT;
298 act_a_to_b.name = "transfer"_n;
299 act_a_to_b.authorization = vector<permission_level>{{newaccountA,config::active_name}};
300 act_a_to_b.data = sysio_token_serializer.variant_to_binary("transfer",
301 fc::json::from_string(fc::format_string("{\"from\":\"${from}\",\"to\":\"${to}\",\"quantity\":\"1.0000 CUR\",\"memo\":\"${l}\"}",
302 fc::mutable_variant_object()("from",newaccountA.to_string())("to",newaccountB.to_string())("l", salt))),
304
305 act_b_to_a.account = newaccountT;
306 act_b_to_a.name = "transfer"_n;
307 act_b_to_a.authorization = vector<permission_level>{{newaccountB,config::active_name}};
308 act_b_to_a.data = sysio_token_serializer.variant_to_binary("transfer",
309 fc::json::from_string(fc::format_string("{\"from\":\"${from}\",\"to\":\"${to}\",\"quantity\":\"1.0000 CUR\",\"memo\":\"${l}\"}",
310 fc::mutable_variant_object()("from",newaccountB.to_string())("to",newaccountA.to_string())("l", salt))),
312
313 timer_timeout = period;
314 batch = batch_size/2;
315 nonce_prefix = 0;
316
317 thread_pool.emplace( "txntest", thread_pool_size );
318 timer = std::make_shared<boost::asio::high_resolution_timer>(thread_pool->get_executor());
319
320 ilog("Started transaction test plugin; generating ${p} transactions every ${m} ms by ${t} load generation threads",
321 ("p", batch_size) ("m", period) ("t", thread_pool_size));
322
323 boost::asio::post( thread_pool->get_executor(), [this]() {
324 arm_timer(boost::asio::high_resolution_timer::clock_type::now());
325 });
326 return "success";
327 }
328
329 void arm_timer(boost::asio::high_resolution_timer::time_point s) {
330 timer->expires_at(s + std::chrono::milliseconds(timer_timeout));
331 boost::asio::post( thread_pool->get_executor(), [this]() {
332 send_transaction([this](const fc::exception_ptr& e){
333 if (e) {
334 elog("pushing transaction failed: ${e}", ("e", e->to_detail_string()));
335 if(running)
336 stop_generation();
337 }
338 }, nonce_prefix++);
339 });
340 timer->async_wait([this](const boost::system::error_code& ec) {
341 if(!running || ec)
342 return;
343 arm_timer(timer->expires_at());
344 });
345 }
346
347 void send_transaction(std::function<void(const fc::exception_ptr&)> next, uint64_t nonce_prefix) {
348 std::vector<signed_transaction> trxs;
349 trxs.reserve(2*batch);
350
351 try {
352 controller& cc = app().get_plugin<chain_plugin>().chain();
353 auto chainid = app().get_plugin<chain_plugin>().get_chain_id();
354
355 static fc::crypto::private_key a_priv_key = fc::crypto::private_key::regenerate(fc::sha256(std::string(64, 'a')));
356 static fc::crypto::private_key b_priv_key = fc::crypto::private_key::regenerate(fc::sha256(std::string(64, 'b')));
357
358 static uint64_t nonce = static_cast<uint64_t>(fc::time_point::now().sec_since_epoch()) << 32;
359
360 uint32_t reference_block_num = cc.last_irreversible_block_num();
361 if (txn_reference_block_lag >= 0) {
362 reference_block_num = cc.head_block_num();
363 if (reference_block_num <= (uint32_t)txn_reference_block_lag) {
364 reference_block_num = 0;
365 } else {
366 reference_block_num -= (uint32_t)txn_reference_block_lag;
367 }
368 }
369
370 block_id_type reference_block_id = cc.get_block_id_for_num(reference_block_num);
371
372 for(unsigned int i = 0; i < batch; ++i) {
373 {
375 trx.actions.push_back(act_a_to_b);
376 trx.context_free_actions.emplace_back(action({}, config::null_account_name, name("nonce"), fc::raw::pack( std::to_string(nonce_prefix)+std::to_string(nonce++) )));
377 trx.set_reference_block(reference_block_id);
378 trx.expiration = cc.head_block_time() + fc::seconds(30);
379 trx.max_net_usage_words = 100;
380 trx.sign(a_priv_key, chainid);
381 trxs.emplace_back(std::move(trx));
382 }
383
384 {
386 trx.actions.push_back(act_b_to_a);
387 trx.context_free_actions.emplace_back(action({}, config::null_account_name, name("nonce"), fc::raw::pack( std::to_string(nonce_prefix)+std::to_string(nonce++) )));
388 trx.set_reference_block(reference_block_id);
389 trx.expiration = cc.head_block_time() + fc::seconds(30);
390 trx.max_net_usage_words = 100;
391 trx.sign(b_priv_key, chainid);
392 trxs.emplace_back(std::move(trx));
393 }
394 }
395 } catch ( const std::bad_alloc& ) {
396 throw;
397 } catch ( const boost::interprocess::bad_alloc& ) {
398 throw;
399 } catch ( const fc::exception& e ) {
400 next(e.dynamic_copy_exception());
401 } catch (const std::exception& e) {
402 next(fc::std_exception_wrapper::from_current_exception(e).dynamic_copy_exception());
403 }
404
405 push_transactions(std::move(trxs), next);
406 }
407
409 if(!running)
411 timer->cancel();
412 running = false;
413 if( thread_pool )
414 thread_pool->stop();
415
416 ilog("Stopping transaction generation test");
417
418 if (_txcount) {
419 ilog("${d} transactions executed, ${t}us / transaction", ("d", _txcount)("t", _total_us / (double)_txcount));
420 _txcount = _total_us = 0;
421 }
422 }
423
424 bool running{false};
425
427 unsigned batch;
429
432
434};
435
436txn_test_gen_plugin::txn_test_gen_plugin() {}
437txn_test_gen_plugin::~txn_test_gen_plugin() {}
438
439void txn_test_gen_plugin::set_program_options(options_description&, options_description& cfg) {
440 cfg.add_options()
441 ("txn-reference-block-lag", bpo::value<int32_t>()->default_value(0), "Lag in number of blocks from the head block when selecting the reference block for transactions (-1 means Last Irreversible Block)")
442 ("txn-test-gen-threads", bpo::value<uint16_t>()->default_value(2), "Number of worker threads in txn_test_gen thread pool")
443 ("txn-test-gen-account-prefix", bpo::value<string>()->default_value("txn.test."), "Prefix to use for accounts generated and used by this plugin")
444 ;
445}
446
447void txn_test_gen_plugin::plugin_initialize(const variables_map& options) {
448 try {
449 my.reset( new txn_test_gen_plugin_impl );
450 my->txn_reference_block_lag = options.at( "txn-reference-block-lag" ).as<int32_t>();
451 my->thread_pool_size = options.at( "txn-test-gen-threads" ).as<uint16_t>();
452 const std::string thread_pool_account_prefix = options.at( "txn-test-gen-account-prefix" ).as<std::string>();
453 my->newaccountA = sysio::chain::name(thread_pool_account_prefix + "a");
454 my->newaccountB = sysio::chain::name(thread_pool_account_prefix + "b");
455 my->newaccountT = sysio::chain::name(thread_pool_account_prefix + "t");
456 SYS_ASSERT( my->thread_pool_size > 0, chain::plugin_config_exception,
457 "txn-test-gen-threads ${num} must be greater than 0", ("num", my->thread_pool_size) );
459}
460
461void txn_test_gen_plugin::plugin_startup() {
462 app().get_plugin<http_plugin>().add_api({
463 CALL_ASYNC(txn_test_gen, my, create_test_accounts, INVOKE_ASYNC_R_R(my, create_test_accounts, std::string, std::string), 200),
464 CALL(txn_test_gen, my, stop_generation, INVOKE_V_V(my, stop_generation), 200),
465 CALL(txn_test_gen, my, start_generation, INVOKE_V_R_R_R(my, start_generation, std::string, uint64_t, uint64_t), 200)
466 });
467}
468
469void txn_test_gen_plugin::plugin_shutdown() {
470 try {
471 my->stop_generation();
472 }
473 catch(const std::exception& e) {
474 }
475}
476
477}
std::string name
#define SYS_ASSERT(expr, exc_type, FORMAT,...)
Definition exceptions.hpp:7
abstract_plugin & get_plugin(const string &name) const
auto post(int priority, Func &&func)
public_key get_public_key() const
static private_key regenerate(const typename KeyType::data_type &data)
Used to generate a useful error report when an exception is thrown.
Definition exception.hpp:58
virtual std::shared_ptr< exception > dynamic_copy_exception() const
static variant from_string(const string &utf8_str, const parse_type ptype=parse_type::legacy_parser, uint32_t max_depth=DEFAULT_MAX_RECURSION_DEPTH)
Definition json.cpp:442
An order-preserving dictionary of variants.
static std_exception_wrapper from_current_exception(const std::exception &e)
constexpr uint32_t sec_since_epoch() const
Definition time.hpp:53
static time_point now()
Definition time.cpp:14
T as() const
Definition variant.hpp:327
uint32_t head_block_num() const
uint32_t last_irreversible_block_num() const
block_id_type get_block_id_for_num(uint32_t block_num) const
time_point head_block_time() const
void accept_transaction(const chain::packed_transaction_ptr &trx, chain::plugin_interface::next_function< chain::transaction_trace_ptr > next)
Defines exception's used by fc.
#define FC_LOG_AND_RETHROW()
#define ilog(FORMAT,...)
Definition logger.hpp:118
application & app()
void pack(Stream &s, const std::deque< T > &value)
Definition raw.hpp:531
std::shared_ptr< exception > exception_ptr
constexpr microseconds seconds(int64_t s)
Definition time.hpp:32
@ invalid_operation_exception_code
Definition exception.hpp:33
fc::string format_string(const fc::string &, const variant_object &, bool minimize=false)
Definition variant.cpp:773
const fc::microseconds abi_serializer_max_time
Definition main.cpp:173
#define FC_REFLECT(TYPE, MEMBERS)
Specializes fc::reflector for TYPE.
Definition reflect.hpp:311
unsigned short uint16_t
Definition stdint.h:125
unsigned int uint32_t
Definition stdint.h:126
signed int int32_t
Definition stdint.h:123
unsigned __int64 uint64_t
Definition stdint.h:136
static yield_function_t create_yield_function(const fc::microseconds &max_serialization_time)
vector< permission_level > authorization
Definition action.hpp:59
Immutable except for fc::from_variant.
Definition name.hpp:43
name(std::string_view str)
Definition name.hpp:56
std::string to_string() const
Definition name.cpp:19
const signature_type & sign(const private_key_type &key, const chain_id_type &chain_id)
void set_reference_block(const block_id_type &reference_block)
time_point_sec expiration
the time at which a transaction expires
vector< action > actions
vector< action > context_free_actions
string start_generation(const std::string &salt, const uint64_t &period, const uint64_t &batch_size)
void arm_timer(boost::asio::high_resolution_timer::time_point s)
void send_transaction(std::function< void(const fc::exception_ptr &)> next, uint64_t nonce_prefix)
void push_transactions(std::vector< signed_transaction > &&trxs, const std::function< void(fc::exception_ptr)> &next)
std::shared_ptr< boost::asio::high_resolution_timer > timer
void push_next_transaction(const std::shared_ptr< std::vector< signed_transaction > > &trxs, const std::function< void(const fc::exception_ptr &)> &next)
std::optional< sysio::chain::named_thread_pool > thread_pool
void create_test_accounts(const std::string &init_name, const std::string &init_priv_key, const std::function< void(const fc::exception_ptr &)> &next)
#define INVOKE_V_R_R_R(api_handle, call_name, in_param0, in_param1, in_param2)
#define CALL_ASYNC(api_name, api_handle, call_name, INVOKE, http_response_code)
#define INVOKE_V_V(api_handle, call_name)
#define INVOKE_ASYNC_R_R(api_handle, call_name, in_param0, in_param1)
#define CALL(api_name, api_handle, call_name, INVOKE, http_response_code)
char * s
if(ppFunctionList==NULL)