tester.cpp 14.7 KB
Newer Older
1
#include <boost/test/unit_test.hpp>
2 3 4
#include <eosio/testing/tester.hpp>
#include <eosio/chain/asset.hpp>
#include <eosio/chain/contracts/types.hpp>
5
#include <eosio/chain/contracts/eos_contract.hpp>
6
#include <eosio/chain/contracts/contract_table_objects.hpp>
7

B
Bart Wyatt 已提交
8 9
#include <test.system/test.system.wast.hpp>
#include <test.system/test.system.abi.hpp>
10

11
#include <fc/utility.hpp>
12
#include <fc/io/json.hpp>
13 14 15 16 17

#include "WAST/WAST.h"
#include "WASM/WASM.h"
#include "IR/Module.h"
#include "IR/Validate.h"
18

19
namespace eosio { namespace testing {
20

21
   base_tester::base_tester(chain_controller::runtime_limits limits) {
22 23 24 25 26
      cfg.block_log_dir      = tempdir.path() / "blocklog";
      cfg.shared_memory_dir  = tempdir.path() / "shared";
      cfg.shared_memory_size = 1024*1024*8;

      cfg.genesis.initial_timestamp = fc::time_point::from_iso_string("2020-01-01T00:00:00.000");
D
Daniel Larimer 已提交
27
      cfg.genesis.initial_key = get_public_key( config::system_account_name, "active" );
28
      cfg.limits = limits;
29

30
      open();
K
Kevin Heifner 已提交
31 32
   }

33
   public_key_type  base_tester::get_public_key( name keyname, string role ) const {
34 35 36
      return get_private_key( keyname, role ).get_public_key(); 
   }

37
   private_key_type base_tester::get_private_key( name keyname, string role ) const {
38 39 40
      return private_key_type::regenerate<fc::ecc::private_key_shim>(fc::sha256::hash(string(keyname)+role));
   }

41
   void base_tester::close() {
42
      control.reset();
43
      chain_transactions.clear();
44
   }
45 46

   void base_tester::open() {
47
      control.reset( new chain_controller(cfg) );
48
      chain_transactions.clear();
49
      control->applied_block.connect([this]( const block_trace& trace ){
50
         for( const auto& region : trace.block.regions) {
51 52
            for( const auto& cycle : region.cycles_summary ) {
               for ( const auto& shard : cycle ) {
B
Bart Wyatt 已提交
53
                  for( const auto& receipt: shard.transactions ) {
54 55
                     chain_transactions.emplace(receipt.id, receipt);
                  }
56 57 58 59
               }
            }
         }
      });
60 61
   }

62
   signed_block base_tester::produce_block( fc::microseconds skip_time ) {
63 64 65 66
      auto head_time = control->head_block_time();
      auto next_time = head_time + skip_time;
      uint32_t slot  = control->get_slot_at_time( next_time );
      auto sch_pro   = control->get_scheduled_producer(slot);
K
Kevin Heifner 已提交
67
      auto priv_key  = get_private_key( sch_pro, "active" );
68

69
      return control->generate_block( next_time, sch_pro, priv_key, skip_missed_block_penalty );
70 71
   }

72
   void base_tester::produce_blocks( uint32_t n ) {
73 74 75 76
      for( uint32_t i = 0; i < n; ++i )
         produce_block();
   }

77
  void base_tester::set_tapos( signed_transaction& trx ) const {
78 79 80
     trx.set_reference_block( control->head_block_id() );
  }

81
   void base_tester::create_account( account_name a, account_name creator, bool multisig ) {
82 83 84
      signed_transaction trx;
      set_tapos( trx );

85 86 87 88 89 90 91 92
      authority owner_auth;
      if (multisig) {
         // multisig between account's owner key and creators active permission
         owner_auth = authority(2, {key_weight{get_public_key( a, "owner" ), 1}}, {permission_level_weight{{creator, config::active_name}, 1}});
      } else {
         owner_auth =  authority( get_public_key( a, "owner" ) );
      }

93
      trx.actions.emplace_back( vector<permission_level>{{creator,config::active_name}}, 
94
                                contracts::newaccount{
95 96
                                   .creator  = creator,
                                   .name     = a,
97
                                   .owner    = owner_auth,
98 99 100
                                   .active   = authority( get_public_key( a, "active" ) ),
                                   .recovery = authority( get_public_key( a, "recovery" ) ),
                                });
101

102 103 104 105
      set_tapos(trx);
      trx.sign( get_private_key( creator, "active" ), chain_id_type()  );
      push_transaction( trx );
   }
106

107
   transaction_trace base_tester::push_transaction( packed_transaction& trx ) {
108
      return control->push_transaction( trx );
109 110
   }

111
   transaction_trace base_tester::push_transaction( signed_transaction& trx ) {
112 113
      auto ptrx = packed_transaction(trx);
      return push_transaction( ptrx );
114 115
   }

116
   base_tester::action_result base_tester::push_action(action&& cert_act, uint64_t authorizer) {
117 118 119 120 121 122 123 124 125 126
      signed_transaction trx;
      if (authorizer) {
         cert_act.authorization = vector<permission_level>{{authorizer, config::active_name}};
      }
      trx.actions.emplace_back(std::move(cert_act));
      set_tapos(trx);
      if (authorizer) {
         trx.sign(get_private_key(authorizer, "active"), chain_id_type());
      }
      try {
A
Anton Perkov 已提交
127
         push_transaction(trx);
128 129 130 131 132 133 134 135
      } catch (const fc::exception& ex) {
         return error(ex.top_message());
      }
      produce_block();
      BOOST_REQUIRE_EQUAL(true, chain_has_transaction(trx.id()));
      return success();
   }

136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
   transaction_trace base_tester::push_action( const account_name& code, 
                             const action_name& acttype, 
                             const account_name& actor, 
                             const variant_object& data
                             ) 
   { try {
      chain::contracts::abi_serializer abis( control->get_database().get<account_object,by_name>(code).get_abi() );    

      string action_type_name = abis.get_action_type(acttype);
   
      action act;
      act.account = code;
      act.name = acttype;
      act.authorization = vector<permission_level>{{actor, config::active_name}};
      act.data = abis.variant_to_binary(action_type_name, data);
      wdump((act));
   
      signed_transaction trx;
      trx.actions.emplace_back(std::move(act));
      set_tapos(trx);
      trx.sign(get_private_key(actor, "active"), chain_id_type());
      wdump((get_public_key( actor, "active" )));;

      return push_transaction(trx);
   } FC_CAPTURE_AND_RETHROW( (code)(acttype)(actor) ) }

162
   transaction_trace base_tester::push_reqauth( account_name from, const vector<permission_level>& auths, const vector<private_key_type>& keys ) {
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
      variant pretty_trx = fc::mutable_variant_object()
         ("actions", fc::variants({
            fc::mutable_variant_object()
               ("account", name(config::system_account_name))
               ("name", "reqauth")
               ("authorization", auths)
               ("data", fc::mutable_variant_object()
                  ("from", from)
               )
            })
        );

      signed_transaction trx;
      contracts::abi_serializer::from_variant(pretty_trx, trx, get_resolver());
      set_tapos( trx );
      for(auto iter = keys.begin(); iter != keys.end(); iter++)
         trx.sign( *iter, chain_id_type() );
      return push_transaction( trx );
   }

183
   transaction_trace base_tester::push_nonce(account_name from, const string& v) {
184 185 186
      variant pretty_trx = fc::mutable_variant_object()
         ("actions", fc::variants({
            fc::mutable_variant_object()
187
               ("account", name(config::system_account_name))
188 189 190 191
               ("name", "nonce")
               ("authorization", fc::variants({
                  fc::mutable_variant_object()
                     ("actor", from)
192
                     ("permission", name(config::active_name))
193 194 195 196 197 198 199 200
               }))
               ("data", fc::mutable_variant_object()
                  ("value", v)
               )
            })
         );

      signed_transaction trx;
201
      contracts::abi_serializer::from_variant(pretty_trx, trx, get_resolver());
202 203
      set_tapos( trx );

204
      trx.sign( get_private_key( from, "active" ), chain_id_type() );
205 206
      return push_transaction( trx );
   }
207

208
   transaction_trace base_tester::transfer( account_name from, account_name to, string amount, string memo, account_name currency ) {
209
      return transfer( from, to, asset::from_string(amount), memo, currency );
210
   }
211

212
   transaction_trace base_tester::transfer( account_name from, account_name to, asset amount, string memo, account_name currency ) {
213 214 215 216 217 218 219 220 221 222 223 224 225
      variant pretty_trx = fc::mutable_variant_object()
         ("actions", fc::variants({
            fc::mutable_variant_object()
               ("account", currency)
               ("name", "transfer")
               ("authorization", fc::variants({
                  fc::mutable_variant_object()
                     ("actor", from)
                     ("permission", name(config::active_name))
               }))
               ("data", fc::mutable_variant_object()
                  ("from", from)
                  ("to", to)
K
Kevin Heifner 已提交
226
                  ("quantity", amount)
227 228 229 230 231
                  ("memo", memo)
               )
            })
         );

232
      signed_transaction trx;
233
      contracts::abi_serializer::from_variant(pretty_trx, trx, get_resolver());
234
      set_tapos( trx );
235

236
      trx.sign( get_private_key( from, name(config::active_name).to_string() ), chain_id_type()  );
237
      return push_transaction( trx );
238 239
   }

240
   void base_tester::set_authority( account_name account,
241 242 243 244 245 246 247 248
                               permission_name perm,
                               authority auth,
                               permission_name parent ) { try {
      signed_transaction trx;
      trx.actions.emplace_back( vector<permission_level>{{account,perm}},
                                contracts::updateauth{
                                   .account    = account,
                                   .permission = perm,
249 250
                                   .parent     = parent,
                                   .data       = move(auth),
251 252 253 254
                                });

      set_tapos( trx );
      trx.sign( get_private_key( account, "active" ), chain_id_type()  ); 
255
      push_transaction( trx );
256 257
   } FC_CAPTURE_AND_RETHROW( (account)(perm)(auth)(parent) ) }

258
   void base_tester::set_code( account_name account, const char* wast ) try {
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
      const auto assemble = [](const char* wast) -> vector<unsigned char> {
         using namespace IR;
         using namespace WAST;
         using namespace WASM;
         using namespace Serialization;

         Module module;
         vector<Error> parse_errors;
         parseModule(wast, fc::const_strlen(wast), module, parse_errors);
         if (!parse_errors.empty()) {
            fc::exception parse_exception(
               FC_LOG_MESSAGE(warn, "Failed to parse WAST"),
               fc::std_exception_code,
               "wast_parse_error",
               "Failed to parse WAST"
            );

            for (const auto& err: parse_errors) {
               parse_exception.append_log( FC_LOG_MESSAGE(error, ":${desc}: ${message}", ("desc", err.locus.describe())("message", err.message.c_str()) ) );
               parse_exception.append_log( FC_LOG_MESSAGE(error, string(err.locus.column(8), ' ') + "^" ));
            }

            throw parse_exception;
         }

         try {
            // Serialize the WebAssembly module.
            ArrayOutputStream stream;
            serialize(stream,module);
            return stream.getBytes();
         } catch(const FatalSerializationException& ex) {
            fc::exception serialize_exception (
               FC_LOG_MESSAGE(warn, "Failed to serialize wasm: ${message}", ("message", ex.message)),
               fc::std_exception_code,
               "wasm_serialization_error",
               "Failed to serialize WASM"
            );
            throw serialize_exception;
         }
      };

      auto wasm = assemble(wast);

      signed_transaction trx;
      trx.actions.emplace_back( vector<permission_level>{{account,config::active_name}},
                                contracts::setcode{
                                   .account    = account,
                                   .vmtype     = 0,
                                   .vmversion  = 0,
                                   .code       = bytes(wasm.begin(), wasm.end())
                                });

      set_tapos( trx );
      trx.sign( get_private_key( account, "active" ), chain_id_type()  );
313
      push_transaction( trx );
314 315
   } FC_CAPTURE_AND_RETHROW( (account)(wast) )

316
   void base_tester::set_abi( account_name account, const char* abi_json) {
317 318 319 320 321 322 323 324 325 326
      auto abi = fc::json::from_string(abi_json).template as<contracts::abi_def>();
      signed_transaction trx;
      trx.actions.emplace_back( vector<permission_level>{{account,config::active_name}},
                                contracts::setabi{
                                   .account    = account,
                                   .abi        = abi
                                });

      set_tapos( trx );
      trx.sign( get_private_key( account, "active" ), chain_id_type()  );
327
      push_transaction( trx );
328 329
   }

330
   bool base_tester::chain_has_transaction( const transaction_id_type& txid ) const {
331 332 333
      return chain_transactions.count(txid) != 0;
   }

334
   const transaction_receipt& base_tester::get_transaction_receipt( const transaction_id_type& txid ) const {
335 336 337
      return chain_transactions.at(txid);
   }

D
Daniel Larimer 已提交
338 339 340
   /**
    *  Reads balance as stored by generic_currency contract
    */
341
   asset base_tester::get_currency_balance( const account_name& code,
K
Khaled Al-Hassanieh 已提交
342 343
                                       const symbol&       asset_symbol,
                                       const account_name& account ) const {
D
Daniel Larimer 已提交
344
      const auto& db  = control->get_database();
345
      const auto* tbl = db.find<contracts::table_id_object, contracts::by_code_scope_table>(boost::make_tuple(code, account, N(account)));
346 347 348 349
      share_type result = 0;

      // the balance is implied to be 0 if either the table or row does not exist
      if (tbl) {
350
         const auto *obj = db.find<contracts::key_value_object, contracts::by_scope_primary>(boost::make_tuple(tbl->id, asset_symbol.value()));
351 352 353 354 355
         if (obj) {
            fc::datastream<const char *> ds(obj->value.data(), obj->value.size());
            fc::raw::unpack(ds, result);
         }
      }
K
Khaled Al-Hassanieh 已提交
356
      return asset(result, asset_symbol);
D
Daniel Larimer 已提交
357 358
   }

359
   vector<uint8_t> base_tester::to_uint8_vector(const string& s) {
360 361 362 363 364
      vector<uint8_t> v(s.size());
      copy(s.begin(), s.end(), v.begin());
      return v;
   };

365
   vector<uint8_t> base_tester::to_uint8_vector(uint64_t x) {
366 367 368 369 370
      vector<uint8_t> v(sizeof(x));
      *reinterpret_cast<uint64_t*>(v.data()) = x;
      return v;
   };

371
   uint64_t base_tester::to_uint64(fc::variant x) {
372 373 374 375 376 377
      vector<uint8_t> blob;
      fc::from_variant<uint8_t>(x, blob);
      FC_ASSERT(8 == blob.size());
      return *reinterpret_cast<uint64_t*>(blob.data());
   }

378
   string base_tester::to_string(fc::variant x) {
379 380 381 382 383 384
      vector<uint8_t> v;
      fc::from_variant<uint8_t>(x, v);
      string s(v.size(), 0);
      copy(v.begin(), v.end(), s.begin());
      return s;
   }
385

386 387 388 389 390
   tester::tester(chain_controller::runtime_limits limits)
      : base_tester(limits) {
      push_genesis_block();
   }

391 392 393 394 395
   void tester::push_genesis_block() {
      set_code(config::system_account_name, test_system_wast);
      set_abi(config::system_account_name, test_system_abi);
   }

396
} }  /// eosio::test