eosio_contract.cpp 20.4 KB
Newer Older
1 2 3 4
/**
 *  @file
 *  @copyright defined in eos/LICENSE.txt
 */
B
Bart Wyatt 已提交
5
#include <eosio/chain/contracts/eos_contract.hpp>
6
#include <eosio/chain/contracts/contract_table_objects.hpp>
7
#include <eosio/chain/contracts/chain_initializer.hpp>
N
Nathan Hourt 已提交
8

B
Bart Wyatt 已提交
9 10 11 12
#include <eosio/chain/chain_controller.hpp>
#include <eosio/chain/apply_context.hpp>
#include <eosio/chain/transaction.hpp>
#include <eosio/chain/exceptions.hpp>
N
Nathan Hourt 已提交
13

B
Bart Wyatt 已提交
14 15 16
#include <eosio/chain/account_object.hpp>
#include <eosio/chain/permission_object.hpp>
#include <eosio/chain/permission_link_object.hpp>
17
#include <eosio/chain/generated_transaction_object.hpp>
B
Bart Wyatt 已提交
18 19 20
#include <eosio/chain/global_property_object.hpp>
#include <eosio/chain/contracts/types.hpp>
#include <eosio/chain/producer_object.hpp>
21

B
Bart Wyatt 已提交
22
#include <eosio/chain/wasm_interface.hpp>
23
#include <eosio/chain/contracts/abi_serializer.hpp>
24

25 26
#include <eosio/chain/rate_limiting.hpp>

27
namespace eosio { namespace chain { namespace contracts {
N
Nathan Hourt 已提交
28

29
void validate_authority_precondition( const apply_context& context, const authority& auth ) {
M
Matias Romeo 已提交
30
   for(const auto& a : auth.accounts) {
D
Daniel Larimer 已提交
31 32
      context.db.get<account_object, by_name>(a.permission.actor);
      context.db.find<permission_object, by_owner>(boost::make_tuple(a.permission.actor, a.permission.permission));
M
Matias Romeo 已提交
33
   }
N
Nathan Hourt 已提交
34 35
}

36
/**
D
Daniel Larimer 已提交
37
 *  This method is called assuming precondition_system_newaccount succeeds a
38
 */
39
void apply_eosio_newaccount(apply_context& context) { 
40
   auto create = context.act.data_as<newaccount>();
41
   try {
42
   context.require_authorization(create.creator);
43
   context.require_write_lock( config::eosio_auth_scope );
44

D
Daniel Larimer 已提交
45 46 47
   EOS_ASSERT( validate(create.owner), action_validate_exception, "Invalid owner authority");
   EOS_ASSERT( validate(create.active), action_validate_exception, "Invalid active authority");
   EOS_ASSERT( validate(create.recovery), action_validate_exception, "Invalid recovery authority");
48 49 50

   auto& db = context.mutable_db;

51 52 53 54 55
   EOS_ASSERT( create.name.to_string().size() <= 12, action_validate_exception, "account names can only be 12 chars long" );
   if( !context.privileged ) {
      EOS_ASSERT( name(create.name).to_string().find( "eosio." ) == std::string::npos, action_validate_exception, "only privileged accounts can have names that contain 'eosio.'" );
   }

56
   auto existing_account = db.find<account_object, by_name>(create.name);
D
Daniel Larimer 已提交
57
   EOS_ASSERT(existing_account == nullptr, action_validate_exception,
58 59 60 61 62 63 64
              "Cannot create account named ${name}, as that name is already taken",
              ("name", create.name));

   for( const auto& auth : { create.owner, create.active, create.recovery } ){
      validate_authority_precondition( context, auth );
   }

65
   const auto& new_account = db.create<account_object>([&create, &context](account_object& a) {
66
      a.name = create.name;
67
      a.creation_date = context.controller.head_block_time();
68
   });
69

70 71 72 73 74 75 76 77 78 79 80
   auto create_permission = [owner=create.name, &db, &context](const permission_name& name, permission_object::id_type parent, authority &&auth) {
      return db.create<permission_object>([&](permission_object& p) {
         p.name = name;
         p.parent = parent;
         p.owner = owner;
         p.auth = std::move(auth);
      });
   };

   const auto& owner_permission = create_permission("owner", 0, std::move(create.owner));
   create_permission("active", owner_permission.id, std::move(create.active));
81

82 83 84
   db.create<bandwidth_usage_object>([&]( auto& bu ) { bu.owner = create.name; });

} FC_CAPTURE_AND_RETHROW( (create) ) }
85

86

D
Daniel Larimer 已提交
87
void apply_eosio_setcode(apply_context& context) {
88
   auto& db = context.mutable_db;
89
   auto  act = context.act.data_as<setcode>();
90

91
   context.require_authorization(act.account);
92
   context.require_write_lock( config::eosio_auth_scope );
N
Nathan Hourt 已提交
93

94 95
   FC_ASSERT( act.vmtype == 0 );
   FC_ASSERT( act.vmversion == 0 );
96

97 98
   auto code_id = fc::sha256::hash( act.code.data(), act.code.size() );

99
   wasm_interface::validate(act.code);
100

101 102
   const auto& account = db.get<account_object,by_name>(act.account);
//   wlog( "set code: ${size}", ("size",act.code.size()));
103
   db.modify( account, [&]( auto& a ) {
104
      /** TODO: consider whether a microsecond level local timestamp is sufficient to detect code version changes*/
105
      #warning TODO: update setcode message to include the hash, then validate it in validate 
106
      a.code_version = code_id;
107 108
      // Added resize(0) here to avoid bug in boost vector container
      a.code.resize( 0 );
109
      a.code.resize( act.code.size() );
110
      a.last_code_update = context.controller.head_block_time();
111
      memcpy( a.code.data(), act.code.data(), act.code.size() );
112 113 114 115 116

   });

}

117
void apply_eosio_setabi(apply_context& context) {
118
   auto& db = context.mutable_db;
119
   auto  act = context.act.data_as<setabi>();
120 121 122

   context.require_authorization(act.account);

123 124 125 126 127
   // if system account append native abi
   if ( act.account == eosio::chain::config::system_account_name ) {
      act.abi = chain_initializer::eos_contract_abi(act.abi);
   }

128 129 130
   /// if an ABI is specified make sure it is well formed and doesn't
   /// reference any undefined types
   abi_serializer(act.abi).validate();
131
   // todo: figure out abi serialization location
132 133 134 135 136 137 138

   const auto& account = db.get<account_object,by_name>(act.account);
   db.modify( account, [&]( auto& a ) {
      a.set_abi( act.abi );
   });
}

D
Daniel Larimer 已提交
139
void apply_eosio_updateauth(apply_context& context) {
140
   context.require_write_lock( config::eosio_auth_scope );
141

142
   auto update = context.act.data_as<updateauth>();
D
Daniel Larimer 已提交
143 144
   EOS_ASSERT(!update.permission.empty(), action_validate_exception, "Cannot create authority with empty name");
   EOS_ASSERT(update.permission != update.parent, action_validate_exception,
N
Nathan Hourt 已提交
145
              "Cannot set an authority as its own parent");
146 147
   EOS_ASSERT(validate(update.data), action_validate_exception,
              "Invalid authority: ${auth}", ("auth", update.data));
148
   if (update.permission == "active")
149
      EOS_ASSERT(update.parent == "owner", action_validate_exception, "Cannot change active authority's parent from owner", ("update.parent", update.parent) );
150
   if (update.permission == "owner")
D
Daniel Larimer 已提交
151
      EOS_ASSERT(update.parent.empty(), action_validate_exception, "Cannot change owner authority's parent");
152 153

   auto& db = context.mutable_db;
154

155 156 157 158 159 160 161
   FC_ASSERT(context.act.authorization.size(), "updateauth can only have one action authorization");
   const auto& act_auth = context.act.authorization.front();
   // lazy evaluating loop
   auto permission_is_valid_for_update = [&](){
      if (act_auth.permission == config::owner_name || act_auth.permission == update.permission) {
         return true;
      }
162
      const permission_object *current = db.find<permission_object, by_owner>(boost::make_tuple(update.account, update.permission));
163 164 165 166 167 168
      // Permission doesn't exist yet, check parent permission
      if (current == nullptr) current = db.find<permission_object, by_owner>(boost::make_tuple(update.account, update.parent));
      // Ensure either the permission or parent's permission exists
      EOS_ASSERT(current != nullptr, permission_query_exception,
                 "Fail to retrieve permission for: {\"actor\": \"${actor}\", \"permission\": \"${permission}\" }",
                 ("actor", update.account)("permission", update.parent));
169

170 171
      while(current->name != config::owner_name) {
         if (current->name == act_auth.permission) {
172 173
            return true;
         }
174
         current = &db.get<permission_object>(current->parent);
175 176 177 178 179 180
      }

      return false;
   };

   FC_ASSERT(act_auth.actor == update.account && permission_is_valid_for_update(), "updateauth must carry a permission equal to or in the ancestery of permission it updates");
181 182

   db.get<account_object, by_name>(update.account);
183
   validate_authority_precondition(context, update.data);
184

185
   auto permission = db.find<permission_object, by_owner>(boost::make_tuple(update.account, update.permission));
M
Matias Romeo 已提交
186 187 188 189 190 191 192
   
   permission_object::id_type parent_id = 0;
   if(update.permission != "owner") {
      auto& parent = db.get<permission_object, by_owner>(boost::make_tuple(update.account, update.parent));
      parent_id = parent.id;
   }

193
   if (permission) {
D
Daniel Larimer 已提交
194
      EOS_ASSERT(parent_id == permission->parent, action_validate_exception,
195
                 "Changing parent authority is not currently supported");
196
   
197 198
      // TODO/QUESTION: If we are updating an existing permission, should we check if the message declared
      // permission satisfies the permission we want to modify?
199
      db.modify(*permission, [&update, &parent_id, &context](permission_object& po) {
200
         po.auth = update.data;
201
         po.parent = parent_id;
202
         po.last_updated = context.controller.head_block_time();
203
         po.delay = time_point_sec(update.delay.convert_to<uint64_t>());
204 205
      });
   }  else {
206 207
      // TODO/QUESTION: If we are creating a new permission, should we check if the message declared
      // permission satisfies the parent permission?
208
      db.create<permission_object>([&update, &parent_id, &context](permission_object& po) {
209 210
         po.name = update.permission;
         po.owner = update.account;
211
         po.auth = update.data;
M
Matias Romeo 已提交
212
         po.parent = parent_id;
213
         po.last_updated = context.controller.head_block_time();
214
         po.delay = time_point_sec(update.delay.convert_to<uint64_t>());
215 216 217 218
      });
   }
}

D
Daniel Larimer 已提交
219
void apply_eosio_deleteauth(apply_context& context) {
220
   auto remove = context.act.data_as<deleteauth>();
D
Daniel Larimer 已提交
221 222
   EOS_ASSERT(remove.permission != "active", action_validate_exception, "Cannot delete active authority");
   EOS_ASSERT(remove.permission != "owner", action_validate_exception, "Cannot delete owner authority");
223 224 225 226 227 228 229 230

   auto& db = context.mutable_db;
   context.require_authorization(remove.account);
   const auto& permission = db.get<permission_object, by_owner>(boost::make_tuple(remove.account, remove.permission));

   { // Check for children
      const auto& index = db.get_index<permission_index, by_parent>();
      auto range = index.equal_range(permission.id);
D
Daniel Larimer 已提交
231
      EOS_ASSERT(range.first == range.second, action_validate_exception,
232 233 234 235 236 237
                 "Cannot delete an authority which has children. Delete the children first");
   }

   { // Check for links to this permission
      const auto& index = db.get_index<permission_link_index, by_permission_name>();
      auto range = index.equal_range(boost::make_tuple(remove.account, remove.permission));
D
Daniel Larimer 已提交
238
      EOS_ASSERT(range.first == range.second, action_validate_exception,
239 240 241
                 "Cannot delete a linked authority. Unlink the authority first");
   }

242
   db.remove(permission);
243 244
}

D
Daniel Larimer 已提交
245
void apply_eosio_linkauth(apply_context& context) {
246
   auto requirement = context.act.data_as<linkauth>();
D
Daniel Larimer 已提交
247
   EOS_ASSERT(!requirement.requirement.empty(), action_validate_exception, "Required permission cannot be empty");
N
Nathan Hourt 已提交
248 249 250 251 252 253 254 255

   context.require_authorization(requirement.account);
   
   auto& db = context.mutable_db;
   db.get<account_object, by_name>(requirement.account);
   db.get<account_object, by_name>(requirement.code);
   db.get<permission_object, by_name>(requirement.requirement);
   
B
Bart Wyatt 已提交
256 257
   auto link_key = boost::make_tuple(requirement.account, requirement.code, requirement.type);
   auto link = db.find<permission_link_object, by_action_name>(link_key);
N
Nathan Hourt 已提交
258 259
   
   if (link) {
D
Daniel Larimer 已提交
260
      EOS_ASSERT(link->required_permission != requirement.requirement, action_validate_exception,
261
                 "Attempting to update required authority, but new requirement is same as old");
262 263 264 265
      db.modify(*link, [requirement = requirement.requirement](permission_link_object& link) {
          link.required_permission = requirement;
      });
   } else {
N
Nathan Hourt 已提交
266 267 268 269 270 271 272 273
      db.create<permission_link_object>([&requirement](permission_link_object& link) {
         link.account = requirement.account;
         link.code = requirement.code;
         link.message_type = requirement.type;
         link.required_permission = requirement.requirement;
      });
   }
}
N
Nathan Hourt 已提交
274

D
Daniel Larimer 已提交
275
void apply_eosio_unlinkauth(apply_context& context) {
276
   auto& db = context.mutable_db;
277
   auto unlink = context.act.data_as<unlinkauth>();
278 279 280

   context.require_authorization(unlink.account);

B
Bart Wyatt 已提交
281 282
   auto link_key = boost::make_tuple(unlink.account, unlink.code, unlink.type);
   auto link = db.find<permission_link_object, by_action_name>(link_key);
D
Daniel Larimer 已提交
283
   EOS_ASSERT(link != nullptr, action_validate_exception, "Attempting to unlink authority, but no link found");
284
   db.remove(*link);
285 286
}

287

288
void apply_eosio_onerror(apply_context& context) {
B
Bart Wyatt 已提交
289 290
   assert(context.trx_meta.sender);
   context.require_recipient(*context.trx_meta.sender);
291 292
}

293 294 295
static const abi_serializer& get_abi_serializer() {
   static optional<abi_serializer> _abi_serializer;
   if (!_abi_serializer) {
296
      _abi_serializer.emplace(chain_initializer::eos_contract_abi(abi_def()));
297 298 299 300 301 302
   }

   return *_abi_serializer;
}

static optional<variant> get_pending_recovery(apply_context& context, account_name account ) {
303 304 305 306 307 308 309
   const uint64_t id = account;
   const auto table = N(recovery);
   const auto iter = context.db_find_i64(config::system_account_name, account, table, id);
   if (iter != -1) {
      const auto buffer_size = context.db_get_i64(iter, nullptr, 0);
      bytes value(buffer_size);

B
Brian Johnson 已提交
310 311
      const auto written_size = context.db_get_i64(iter, value.data(), buffer_size);
      assert(written_size == buffer_size);
312 313

      return get_abi_serializer().binary_to_variant("pending_recovery", value);
314 315 316 317 318
   }

   return optional<variant_object>();
}

319 320 321 322 323 324 325 326 327 328 329 330 331 332
static auto get_account_creation(const apply_context& context, const account_name& account) {
   auto const& accnt = context.db.get<account_object, by_name>(account);
   return (time_point)accnt.creation_date;
};

static auto get_permission_last_used(const apply_context& context, const account_name& account, const permission_name& permission) {
   auto const* perm = context.db.find<permission_usage_object, by_account_permission>(boost::make_tuple(account, permission));
   if (perm) {
      return optional<time_point>(perm->last_used);
   }

   return optional<time_point>();
};

333
void apply_eosio_postrecovery(apply_context& context) {
334
   context.require_write_lock( config::eosio_auth_scope );
335 336 337

   FC_ASSERT(context.act.authorization.size() == 1, "Recovery Message must have exactly one authorization");

338
   auto recover_act = context.act.data_as<postrecovery>();
339 340
   const auto &auth = context.act.authorization.front();
   const auto& account = recover_act.account;
341
   context.require_write_lock(account);
342 343 344 345 346 347 348 349 350 351 352

   FC_ASSERT(!get_pending_recovery(context, account), "Account ${account} already has a pending recovery request!", ("account",account));

   fc::microseconds delay_lock;
   auto now = context.controller.head_block_time();
   if (auth.actor == account && auth.permission == N(active)) {
      // process owner recovery from active
      delay_lock = fc::days(30);
   } else {
      // process lost password

353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
      auto owner_last_used = get_permission_last_used(context, account, N(owner));
      auto active_last_used = get_permission_last_used(context, account, N(active));

      if (!owner_last_used || !active_last_used) {
         auto account_creation = get_account_creation(context, account);
         if (!owner_last_used) {
            owner_last_used.emplace(account_creation);
         }

         if (!active_last_used) {
            active_last_used.emplace(account_creation);
         }
      }

      FC_ASSERT(*owner_last_used <= now - fc::days(30), "Account ${account} has had owner key activity recently and cannot be recovered yet!", ("account",account));
      FC_ASSERT(*active_last_used <= now - fc::days(30), "Account ${account} has had active key activity recently and cannot be recovered yet!", ("account",account));
369 370 371 372 373 374 375 376 377 378 379 380

      delay_lock = fc::days(7);
   }

   variant update;
   fc::to_variant( updateauth {
      .account = account,
      .permission = N(owner),
      .parent = 0,
      .data = recover_act.data
   }, update);

381
   uint32_t request_id = context.get_next_sender_id();
382

383 384
   auto record_data = mutable_variant_object()
      ("account", account)
385 386 387 388 389 390 391 392 393 394 395
      ("request_id", request_id)
      ("update", update)
      ("memo", recover_act.memo);

   deferred_transaction dtrx;
   dtrx.sender = config::system_account_name;
   dtrx.sender_id = request_id;
   dtrx.region = 0;
   dtrx.execute_after = context.controller.head_block_time() + delay_lock;
   dtrx.set_reference_block(context.controller.head_block_id());
   dtrx.expiration = dtrx.execute_after + fc::seconds(60);
396
   dtrx.actions.emplace_back(vector<permission_level>{{account,config::active_name}},
397 398 399 400
                             passrecovery { account });

   context.execute_deferred(std::move(dtrx));

401 402

   auto data = get_abi_serializer().variant_to_binary("pending_recovery", record_data);
403 404
   const uint64_t id = account;
   const uint64_t table = N(recovery);
405
   const auto payer = account;
406 407 408 409 410 411
   const auto iter = context.db_find_i64(config::system_account_name, account, table, id);
   if (iter == -1) {
      context.db_store_i64(account, table, payer, id, (const char*)data.data(), data.size());
   } else {
      context.db_update_i64(iter, payer, (const char*)data.data(), data.size());
   }
412 413 414 415 416

   context.console_append_formatted("Recovery Started for account ${account} : ${memo}\n", mutable_variant_object()("account", account)("memo", recover_act.memo));
}

static void remove_pending_recovery(apply_context& context, const account_name& account) {
417 418 419 420
   const auto iter = context.db_find_i64(config::system_account_name, account, N(recovery), account);
   if (iter != -1) {
      context.db_remove_i64(iter);
   }
421 422 423
}

void apply_eosio_passrecovery(apply_context& context) {
424
   auto pass_act = context.act.data_as<passrecovery>();
425 426
   const auto& account = pass_act.account;

427
   // ensure this is only processed if it is a deferred transaction from the system account
B
Bart Wyatt 已提交
428
   FC_ASSERT(context.trx_meta.sender && *context.trx_meta.sender == config::system_account_name);
429 430
   context.require_authorization(account);

431 432 433 434 435 436 437
   auto maybe_recovery = get_pending_recovery(context, account);
   FC_ASSERT(maybe_recovery, "No pending recovery found for account ${account}", ("account", account));
   auto recovery = *maybe_recovery;

   updateauth update;
   fc::from_variant(recovery["update"], update);

438
   action act(vector<permission_level>{{account,config::owner_name}}, update);
439 440 441 442 443 444 445
   context.execute_inline(move(act));

   remove_pending_recovery(context, account);
   context.console_append_formatted("Account ${account} successfully recoverd!\n", mutable_variant_object()("account", account));
}

void apply_eosio_vetorecovery(apply_context& context) {
446
   context.require_write_lock( config::eosio_auth_scope );
447
   auto pass_act = context.act.data_as<vetorecovery>();
448 449 450 451 452 453 454 455 456 457 458 459 460
   const auto& account = pass_act.account;
   context.require_authorization(account);

   auto maybe_recovery = get_pending_recovery(context, account);
   FC_ASSERT(maybe_recovery, "No pending recovery found for account ${account}", ("account", account));
   auto recovery = *maybe_recovery;

   context.cancel_deferred(recovery["request_id"].as<uint32_t>());

   remove_pending_recovery(context, account);
   context.console_append_formatted("Recovery for account ${account} vetoed!\n", mutable_variant_object()("account", account));
}

461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493
void apply_eosio_canceldelay(apply_context& context) {
   auto cancel = context.act.data_as<canceldelay>();
   const auto sender_id = cancel.sender_id.convert_to<uint32_t>();
   const auto& generated_transaction_idx = context.controller.get_database().get_index<generated_transaction_multi_index>();
   const auto& generated_index = generated_transaction_idx.indices().get<by_sender_id>();
   const auto& itr = generated_index.lower_bound(boost::make_tuple(config::system_account_name, sender_id));
   FC_ASSERT (itr == generated_index.end() || itr->sender != config::system_account_name || itr->sender_id != sender_id,
              "cannot cancel sender_id=${sid}, there is no deferred transaction with that sender_id",("sid",sender_id));

   auto dtrx = fc::raw::unpack<deferred_transaction>(itr->packed_trx.data(), itr->packed_trx.size());
   set<account_name> accounts;
   for (const auto& act : dtrx.actions) {
      for (const auto& auth : act.authorization) {
         accounts.insert(auth.actor);
      }
   }

   bool found = false;
   for (const auto& auth : context.act.authorization) {
      if (auth.permission == config::active_name && accounts.count(auth.actor)) {
         found = true;
         break;
      }
   }

   FC_ASSERT (found, "canceldelay action must be signed with the \"active\" permission for one of the actors"
                     " provided in the authorizations on the original transaction");
   context.cancel_deferred(sender_id);
}

void apply_eosio_mindelay(apply_context& context) {
   // all processing is performed in chain_controller::check_authorization
}
494

495
} } } // namespace eosio::chain::contracts