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

K
Kevin Heifner 已提交
8
#include <eosio.system/eosio.system.abi.hpp>
9

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

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

18
namespace eosio { namespace testing {
19

20
   tester::tester(bool process_genesis) {
21 22 23 24 25
      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 已提交
26
      cfg.genesis.initial_key = get_public_key( config::system_account_name, "active" );
K
Kevin Heifner 已提交
27
      cfg.genesis.eosio_system_key = get_public_key( config::eosio_system_account_name, "active");
28

29
      open();
30 31
      if (process_genesis)
         create_init_accounts();
K
Kevin Heifner 已提交
32 33 34 35 36 37 38 39 40 41 42
   }

   void tester::create_init_accounts() {

      contracts::abi_def eosio_system_abi_def = fc::json::from_string(eosio_system_abi).as<contracts::abi_def>();
      chain::contracts::abi_serializer eosio_system_serializer(eosio_system_abi_def);

      signed_transaction trx;
      set_tapos(trx);

      action act;
K
Kevin Heifner 已提交
43
      act.account = config::eosio_system_account_name;
K
Kevin Heifner 已提交
44
      act.name = N(issue);
K
Kevin Heifner 已提交
45
      act.authorization = vector<permission_level>{{config::eosio_system_account_name,config::active_name}};
K
Kevin Heifner 已提交
46 47 48
      act.data = eosio_system_serializer.variant_to_binary("issue", fc::json::from_string("{\"to\":\"eosio.system\",\"quantity\":\"1000000000.0000 EOS\"}"));
      trx.actions.push_back(act);
      set_tapos(trx);
K
Kevin Heifner 已提交
49
      trx.sign( get_private_key( config::eosio_system_account_name, "active" ), chain_id_type()  );
K
Kevin Heifner 已提交
50 51
      push_transaction(trx);

K
Kevin Heifner 已提交
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
      create_account(N(inita), "1000000.0000 EOS", config::eosio_system_account_name);
      create_account(N(initb), "1000000.0000 EOS", config::eosio_system_account_name);
      create_account(N(initc), "1000000.0000 EOS", config::eosio_system_account_name);
      create_account(N(initd), "1000000.0000 EOS", config::eosio_system_account_name);
      create_account(N(inite), "1000000.0000 EOS", config::eosio_system_account_name);
      create_account(N(initf), "1000000.0000 EOS", config::eosio_system_account_name);
      create_account(N(initg), "1000000.0000 EOS", config::eosio_system_account_name);
      create_account(N(inith), "1000000.0000 EOS", config::eosio_system_account_name);
      create_account(N(initi), "1000000.0000 EOS", config::eosio_system_account_name);
      create_account(N(initj), "1000000.0000 EOS", config::eosio_system_account_name);
      create_account(N(initk), "1000000.0000 EOS", config::eosio_system_account_name);
      create_account(N(initl), "1000000.0000 EOS", config::eosio_system_account_name);
      create_account(N(initm), "1000000.0000 EOS", config::eosio_system_account_name);
      create_account(N(initn), "1000000.0000 EOS", config::eosio_system_account_name);
      create_account(N(inito), "1000000.0000 EOS", config::eosio_system_account_name);
      create_account(N(initp), "1000000.0000 EOS", config::eosio_system_account_name);
      create_account(N(initq), "1000000.0000 EOS", config::eosio_system_account_name);
      create_account(N(initr), "1000000.0000 EOS", config::eosio_system_account_name);
      create_account(N(inits), "1000000.0000 EOS", config::eosio_system_account_name);
      create_account(N(initt), "1000000.0000 EOS", config::eosio_system_account_name);
      create_account(N(initu), "1000000.0000 EOS", config::eosio_system_account_name);
73 74
   }

75
   public_key_type  tester::get_public_key( name keyname, string role ) const {
76 77 78
      return get_private_key( keyname, role ).get_public_key(); 
   }

79
   private_key_type tester::get_private_key( name keyname, string role ) const {
80 81 82 83 84
      return private_key_type::regenerate<fc::ecc::private_key_shim>(fc::sha256::hash(string(keyname)+role));
   }

   void tester::close() {
      control.reset();
85
      chain_transactions.clear();
86 87 88
   }
   void tester::open() {
      control.reset( new chain_controller(cfg) );
89
      chain_transactions.clear();
90
      control->applied_block.connect([this]( const block_trace& trace ){
91
         for( const auto& region : trace.block.regions) {
92 93
            for( const auto& cycle : region.cycles_summary ) {
               for ( const auto& shard : cycle ) {
B
Bart Wyatt 已提交
94
                  for( const auto& receipt: shard.transactions ) {
95 96
                     chain_transactions.emplace(receipt.id, receipt);
                  }
97 98 99 100
               }
            }
         }
      });
101 102 103 104 105 106 107
   }

   signed_block tester::produce_block( fc::microseconds skip_time ) {
      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 已提交
108
      auto priv_key  = get_private_key( sch_pro, "active" );
109

110
      return control->generate_block( next_time, sch_pro, priv_key, skip_missed_block_penalty );
111 112 113 114 115 116 117 118
   }


   void tester::produce_blocks( uint32_t n ) {
      for( uint32_t i = 0; i < n; ++i )
         produce_block();
   }

119
  void tester::set_tapos( signed_transaction& trx ) const {
120 121 122 123
     trx.set_reference_block( control->head_block_id() );
  }


124
   void tester::create_account( account_name a, asset initial_balance, account_name creator, bool multisig ) {
125 126 127
      signed_transaction trx;
      set_tapos( trx );

128 129 130 131 132 133 134 135
      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" ) );
      }

136
      trx.actions.emplace_back( vector<permission_level>{{creator,config::active_name}}, 
137
                                contracts::newaccount{
138 139
                                   .creator  = creator,
                                   .name     = a,
140
                                   .owner    = owner_auth,
141 142 143
                                   .active   = authority( get_public_key( a, "active" ) ),
                                   .recovery = authority( get_public_key( a, "recovery" ) ),
                                });
144

145 146 147
      set_tapos(trx);
      trx.sign( get_private_key( creator, "active" ), chain_id_type()  );
      push_transaction( trx );
K
Kevin Heifner 已提交
148
      transfer(creator, a, initial_balance);
149
   }
150

151 152
   transaction_trace tester::push_transaction( packed_transaction& trx ) {
      return control->push_transaction( trx );
153 154
   }

155
   transaction_trace tester::push_transaction( signed_transaction& trx ) {
156 157
      auto ptrx = packed_transaction(trx);
      return push_transaction( ptrx );
158 159
   }

160 161
   void tester::create_account( account_name a, string initial_balance, account_name creator, bool multisig  ) {
      create_account( a, asset::from_string(initial_balance), creator, multisig );
162 163
   }

164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
   auto resolver = []( tester& t, const account_name& name ) -> optional<contracts::abi_serializer> {
      try {
         const auto& accnt = t.control->get_database().get<account_object, by_name>(name);
         contracts::abi_def abi;
         if (contracts::abi_serializer::to_abi(accnt.abi, abi)) {
            return contracts::abi_serializer(abi);
         }
         return optional<contracts::abi_serializer>();
      } FC_RETHROW_EXCEPTIONS(error, "Failed to find or parse ABI for ${name}", ("name", name))
   };

   transaction_trace tester::push_nonce(account_name from, const string& role, const string& v) {
      variant pretty_trx = fc::mutable_variant_object()
         ("actions", fc::variants({
            fc::mutable_variant_object()
K
Kevin Heifner 已提交
179
               ("account", name(config::eosio_system_account_name))
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
               ("name", "nonce")
               ("authorization", fc::variants({
                  fc::mutable_variant_object()
                     ("actor", from)
                     ("permission", name(config::owner_name))
               }))
               ("data", fc::mutable_variant_object()
                  ("value", v)
               )
            })
         );

      signed_transaction trx;
      auto resolve = [this](const account_name& name) -> optional<contracts::abi_serializer> {
         return resolver(*this, name);
      };
      contracts::abi_serializer::from_variant(pretty_trx, trx, resolve);
      set_tapos( trx );

      trx.sign( get_private_key( from, role ), chain_id_type() );
      return push_transaction( trx );
   }
202 203

   transaction_trace tester::transfer( account_name from, account_name to, string amount, string memo, account_name currency ) {
204
      return transfer( from, to, asset::from_string(amount), memo );
205
   }
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220

   transaction_trace tester::transfer( account_name from, account_name to, asset amount, string memo, account_name currency ) {
      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 已提交
221
                  ("quantity", amount)
222 223 224 225 226
                  ("memo", memo)
               )
            })
         );

227
      signed_transaction trx;
228 229 230 231
      auto resolve = [this](const account_name& name) -> optional<contracts::abi_serializer> {
         return resolver(*this, name);
      };
      contracts::abi_serializer::from_variant(pretty_trx, trx, resolve);
232
      set_tapos( trx );
233

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

238 239 240 241 242 243 244 245 246
   void tester::set_authority( account_name account,
                               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,
247 248
                                   .parent     = parent,
                                   .data       = move(auth),
249 250 251 252
                                });

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

256 257 258 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
   void tester::set_code( account_name account, const char* wast ) try {
      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()  );
311
      push_transaction( trx );
312 313
   } FC_CAPTURE_AND_RETHROW( (account)(wast) )

314 315 316 317 318 319 320 321 322 323 324
   void tester::set_abi( account_name account, const char* abi_json) {
      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()  );
325
      push_transaction( trx );
326 327
   }

328 329 330 331 332 333 334 335
   bool tester::chain_has_transaction( const transaction_id_type& txid ) const {
      return chain_transactions.count(txid) != 0;
   }

   const transaction_receipt& tester::get_transaction_receipt( const transaction_id_type& txid ) const {
      return chain_transactions.at(txid);
   }

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

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

360

361
} }  /// eosio::test