tester.cpp 11.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

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

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

16
namespace eosio { namespace testing {
17 18 19 20 21 22 23

   tester::tester() {
      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 已提交
24
      cfg.genesis.initial_key = get_public_key( config::system_account_name, "active" );
25

26 27 28
      open();
   }

29
   public_key_type  tester::get_public_key( name keyname, string role ) const {
30 31 32
      return get_private_key( keyname, role ).get_public_key(); 
   }

33
   private_key_type tester::get_private_key( name keyname, string role ) const {
34 35 36 37 38
      return private_key_type::regenerate<fc::ecc::private_key_shim>(fc::sha256::hash(string(keyname)+role));
   }

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

   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);
      auto priv_key  = get_private_key( sch_pro, "producer" );

64
      return control->generate_block( next_time, sch_pro, priv_key, skip_missed_block_penalty );
65 66 67 68 69 70 71 72
   }


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

73
  void tester::set_tapos( signed_transaction& trx ) const {
74 75 76 77
     trx.set_reference_block( control->head_block_id() );
  }


78
   void tester::create_account( account_name a, asset initial_balance, account_name creator, bool multisig ) {
79 80 81
      signed_transaction trx;
      set_tapos( trx );

82 83 84 85 86 87 88 89
      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" ) );
      }

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

99 100 101 102
      set_tapos(trx);
      trx.sign( get_private_key( creator, "active" ), chain_id_type()  );
      push_transaction( trx );
   }
103

104 105
   transaction_trace tester::push_transaction( packed_transaction& trx ) {
      return control->push_transaction( trx );
106 107
   }

108
   transaction_trace tester::push_transaction( signed_transaction& trx ) {
109 110
      auto ptrx = packed_transaction(trx);
      return push_transaction( ptrx );
111 112
   }

113 114
   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 );
115 116 117
   }
   

118 119

   transaction_trace tester::transfer( account_name from, account_name to, string amount, string memo, account_name currency ) {
120
      return transfer( from, to, asset::from_string(amount), memo );
121
   }
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153

   transaction_trace tester::transfer( account_name from, account_name to, asset amount, string memo, account_name currency ) {
      auto resolver = [this]( const account_name& name ) -> optional<contracts::abi_serializer> {
         try {
            const auto& accnt  = 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))
      };

      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)
                  ("amount", amount)
                  ("memo", memo)
               )
            })
         );

154
      signed_transaction trx;
155 156
      contracts::abi_serializer::from_variant(pretty_trx, trx, resolver);
      set_tapos( trx );
157

158
      trx.sign( get_private_key( from, name(config::active_name).to_string() ), chain_id_type()  );
159
      return push_transaction( trx );
160 161
   }

162 163 164 165 166 167 168 169 170
   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,
171 172
                                   .parent     = parent,
                                   .data       = move(auth),
173 174 175 176
                                });

      set_tapos( trx );
      trx.sign( get_private_key( account, "active" ), chain_id_type()  ); 
177
      push_transaction( trx );
178 179
   } FC_CAPTURE_AND_RETHROW( (account)(perm)(auth)(parent) ) }

180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
   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()  );
235
      push_transaction( trx );
236 237
   } FC_CAPTURE_AND_RETHROW( (account)(wast) )

238 239 240 241 242 243 244 245 246 247 248
   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()  );
249
      push_transaction( trx );
250 251
   }

252 253 254 255 256 257 258 259
   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);
   }

260
   share_type tester::get_balance( const account_name& account ) const {
261
      return get_currency_balance( config::system_account_name, EOS_SYMBOL, account ).amount;
262
   }
D
Daniel Larimer 已提交
263 264 265
   /**
    *  Reads balance as stored by generic_currency contract
    */
266
   asset tester::get_currency_balance( const account_name& code,
K
Khaled Al-Hassanieh 已提交
267 268
                                       const symbol&       asset_symbol,
                                       const account_name& account ) const {
D
Daniel Larimer 已提交
269
      const auto& db  = control->get_database();
270 271 272 273 274
      const auto* tbl = db.find<contracts::table_id_object, contracts::by_scope_code_table>(boost::make_tuple(account, code, N(account)));
      share_type result = 0;

      // the balance is implied to be 0 if either the table or row does not exist
      if (tbl) {
275
         const auto *obj = db.find<contracts::key_value_object, contracts::by_scope_primary>(boost::make_tuple(tbl->id, asset_symbol.value()));
276 277 278 279 280
         if (obj) {
            fc::datastream<const char *> ds(obj->value.data(), obj->value.size());
            fc::raw::unpack(ds, result);
         }
      }
K
Khaled Al-Hassanieh 已提交
281
      return asset(result, asset_symbol);
D
Daniel Larimer 已提交
282 283
   }

284

285
} }  /// eosio::test