未验证 提交 c5cdd81c 编写于 作者: B Bucky Kittinger 提交者: GitHub

Merge branch 'master' into fix/analysis_fixes

......@@ -29,7 +29,7 @@ docker build -t eosio/eos:dawn-v4.0.0 --build-arg branch=dawn-v4.0.0 .
By default, the symbol in eosio.system is set to SYS. You can override this using the symbol argument while building the docker image.
```bash
docker built -t eosio/eos --build-arg symbol=<symbol> .
docker build -t eosio/eos --build-arg symbol=<symbol> .
```
## Start nodeos docker container only
......
......@@ -372,6 +372,7 @@ namespace eosiosystem {
eosio_assert( asset() <= unstake_cpu_quantity, "must unstake a positive amount" );
eosio_assert( asset() <= unstake_net_quantity, "must unstake a positive amount" );
eosio_assert( asset() < unstake_cpu_quantity + unstake_net_quantity, "must unstake a positive amount" );
eosio_assert( _gstate.total_activated_stake >= min_activated_stake, "not enough has been staked for users to unstake" );
changebw( from, receiver, -unstake_net_quantity, -unstake_cpu_quantity, false);
} // undelegatebw
......
#include "eosio.system.hpp"
#include <eosiolib/dispatcher.hpp>
#include "delegate_bandwidth.cpp"
#include "producer_pay.cpp"
#include "delegate_bandwidth.cpp"
#include "voting.cpp"
#include "exchange_state.cpp"
......@@ -104,7 +104,7 @@ namespace eosiosystem {
eosio_assert( current->high_bidder != bidder, "account is already high bidder" );
INLINE_ACTION_SENDER(eosio::token, transfer)( N(eosio.token), {N(eosio.names),N(active)},
{ N(eosio.names), current->high_bidder, asset(current->high_bid),
{ N(eosio.names), current->high_bidder, asset(current->high_bid),
std::string("refund bid on name ")+(name{newname}).to_string() } );
bids.modify( current, bidder, [&]( auto& b ) {
......@@ -125,7 +125,7 @@ namespace eosiosystem {
*
* 2. new accounts must stake a minimal number of tokens (as set in system parameters)
* therefore, this method will execute an inline buyram from receiver for newacnt in
* an amount equal to the current new account creation fee.
* an amount equal to the current new account creation fee.
*/
void native::newaccount( account_name creator,
account_name newact
......@@ -146,7 +146,7 @@ namespace eosiosystem {
if( suffix == newact ) {
name_bid_table bids(_self,_self);
auto current = bids.find( newact );
eosio_assert( current != bids.end(), "no active bid for name" );
eosio_assert( current != bids.end(), "no active bid for name" );
eosio_assert( current->high_bidder == creator, "only high bidder can claim" );
eosio_assert( current->high_bid < 0, "auction for name is not closed yet" );
bids.erase( current );
......@@ -173,7 +173,7 @@ namespace eosiosystem {
}
} /// eosio.system
EOSIO_ABI( eosiosystem::system_contract,
(setram)
......
......@@ -38,12 +38,19 @@ action_trace apply_context::exec_one()
const auto &a = control.get_account(receiver);
privileged = a.privileged;
auto native = control.find_apply_handler(receiver, act.account, act.name);
if (native) {
if( native ) {
if( control.is_producing_block() ) {
control.check_contract_list( receiver );
}
(*native)(*this);
}
if( a.code.size() > 0
&& !(act.account == config::system_account_name && act.name == N(setcode) && receiver == config::system_account_name) ) {
&& !(act.account == config::system_account_name && act.name == N(setcode) && receiver == config::system_account_name) )
{
if( control.is_producing_block() ) {
control.check_contract_list( receiver );
}
try {
control.get_wasm_interface().apply(a.code_version, a.code, *this);
} catch ( const wasm_exit& ){}
......
#include <eosio/chain/controller.hpp>
#include <eosio/chain/block_context.hpp>
#include <eosio/chain/transaction_context.hpp>
#include <eosio/chain/block_log.hpp>
......@@ -37,7 +36,7 @@ struct pending_state {
vector<action_receipt> _actions;
block_context _block_ctx;
controller::block_status _block_status = controller::block_status::incomplete;
void push() {
_db_session.push();
......@@ -58,7 +57,6 @@ struct controller_impl {
controller::config conf;
chain_id_type chain_id;
bool replaying = false;
bool replaying_irreversible = false;
typedef pair<scope_name,action_name> handler_key;
map< account_name, map<handler_key, apply_handler> > apply_handlers;
......@@ -198,26 +196,24 @@ struct controller_impl {
auto end = blog.read_head();
if( end && end->block_num() > 1 ) {
replaying = true;
replaying_irreversible = true;
ilog( "existing block log, attempting to replay ${n} blocks", ("n",end->block_num()) );
auto start = fc::time_point::now();
while( auto next = blog.read_block_by_num( head->block_num + 1 ) ) {
self.push_block( next, true );
self.push_block( next, controller::block_status::irreversible );
if( next->block_num() % 100 == 0 ) {
std::cerr << std::setw(10) << next->block_num() << " of " << end->block_num() <<"\r";
}
}
replaying_irreversible = false;
int unconf = 0;
int rev = 0;
while( auto obj = reversible_blocks.find<reversible_block_object,by_num>(head->block_num+1) ) {
++unconf;
self.push_block( obj->get_block(), true );
++rev;
self.push_block( obj->get_block(), controller::block_status::validated );
}
std::cerr<< "\n";
ilog( "${n} reversible blocks replayed", ("n",unconf) );
ilog( "${n} reversible blocks replayed", ("n",rev) );
auto end = fc::time_point::now();
ilog( "replayed ${n} blocks in seconds, ${mspb} ms/block",
("n", head->block_num)("duration", (end-start).count()/1000000)
......@@ -260,7 +256,7 @@ struct controller_impl {
~controller_impl() {
pending.reset();
fork_db.close();
if (head && blog.read_head())
edump((db.revision())(head->block_num)(blog.read_head()->block_num()));
......@@ -635,7 +631,7 @@ struct controller_impl {
trx_context.billed_cpu_time_us = billed_cpu_time_us;
trace = trx_context.trace;
try {
if (implicit) {
if( implicit ) {
trx_context.init_for_implicit_trx();
} else {
trx_context.init_for_input_trx( trx->packed_trx.get_unprunable_size(),
......@@ -643,6 +639,11 @@ struct controller_impl {
trx->trx.signatures.size() );
}
if( !implicit && pending->_block_status == controller::block_status::incomplete ) {
check_actor_list( trx_context.bill_to_accounts ); // Assumes bill_to_accounts is the set of actors authorizing the transaction
}
trx_context.delay = fc::seconds(trx->trx.delay_sec);
if( !self.skip_auth_check() && !implicit ) {
......@@ -708,7 +709,7 @@ struct controller_impl {
} /// push_transaction
void start_block( block_timestamp_type when, uint16_t confirm_block_count ) {
void start_block( block_timestamp_type when, uint16_t confirm_block_count, controller::block_status s ) {
FC_ASSERT( !pending );
FC_ASSERT( db.revision() == head->block_num, "",
......@@ -720,6 +721,8 @@ struct controller_impl {
pending = db.start_undo_session(true);
pending->_block_status = s;
pending->_pending_block_state = std::make_shared<block_state>( *head, when ); // promotes pending schedule (if any) to active
pending->_pending_block_state->in_current_chain = true;
......@@ -727,6 +730,8 @@ struct controller_impl {
auto was_pending_promoted = pending->_pending_block_state->maybe_promote_pending();
const auto& gpo = db.get<global_property_object>();
if( gpo.proposed_schedule_block_num.valid() && // if there is a proposed schedule that was proposed in a block ...
( *gpo.proposed_schedule_block_num <= pending->_pending_block_state->dpos_irreversible_blocknum ) && // ... that has now become irreversible ...
......@@ -751,9 +756,13 @@ struct controller_impl {
try {
auto onbtrx = std::make_shared<transaction_metadata>( get_on_block_transaction() );
push_transaction( onbtrx, fc::time_point::maximum(), true, self.get_global_properties().configuration.min_transaction_cpu_usage );
} catch ( const fc::exception& e ) {
} catch( const boost::interprocess::bad_alloc& e ) {
elog( "on block transaction failed due to a bad allocation" );
throw;
} catch( const fc::exception& e ) {
wlog( "on block transaction failed, but shouldn't impact block generation, system contract needs update" );
edump((e.to_detail_string()));
} catch ( ... ) {
} catch( ... ) {
wlog( "on block transaction failed, but shouldn't impact block generation, system contract needs update" );
}
......@@ -777,10 +786,10 @@ struct controller_impl {
static_cast<signed_block_header&>(*p->block) = p->header;
} /// sign_block
void apply_block( const signed_block_ptr& b, bool trust ) { try {
void apply_block( const signed_block_ptr& b, controller::block_status s ) { try {
try {
FC_ASSERT( b->block_extensions.size() == 0, "no supported extensions" );
start_block( b->timestamp, b->confirmed );
start_block( b->timestamp, b->confirmed, s );
for( const auto& receipt : b->transactions ) {
if( receipt.trx.contains<packed_transaction>() ) {
......@@ -794,7 +803,7 @@ struct controller_impl {
}
finalize_block();
sign_block( [&]( const auto& ){ return b->producer_signature; }, trust );
sign_block( [&]( const auto& ){ return b->producer_signature; }, false ); //trust );
// this is implied by the signature passing
//FC_ASSERT( b->id() == pending->_pending_block_state->block->id(),
......@@ -810,14 +819,16 @@ struct controller_impl {
} FC_CAPTURE_AND_RETHROW() } /// apply_block
void push_block( const signed_block_ptr& b, bool trust ) {
void push_block( const signed_block_ptr& b, controller::block_status s ) {
// idump((fc::json::to_pretty_string(*b)));
FC_ASSERT(!pending, "it is not valid to push a block when there is a pending block");
try {
FC_ASSERT( b );
FC_ASSERT( s != controller::block_status::incomplete, "invalid block status for a completed block" );
bool trust = !conf.force_all_checks && (s == controller::block_status::irreversible || s == controller::block_status::validated);
auto new_header_state = fork_db.add( b, trust );
emit( self.accepted_block_header, new_header_state );
maybe_switch_forks( trust );
maybe_switch_forks( s );
} FC_LOG_AND_RETHROW( )
}
......@@ -828,12 +839,12 @@ struct controller_impl {
maybe_switch_forks();
}
void maybe_switch_forks( bool trust = false ) {
void maybe_switch_forks( controller::block_status s = controller::block_status::complete ) {
auto new_head = fork_db.head();
if( new_head->header.previous == head->id ) {
try {
apply_block( new_head->block, trust );
apply_block( new_head->block, s );
fork_db.mark_in_current_chain( new_head, true );
fork_db.set_validity( new_head, true );
head = new_head;
......@@ -856,9 +867,10 @@ struct controller_impl {
for( auto ritr = branches.first.rbegin(); ritr != branches.first.rend(); ++ritr) {
optional<fc::exception> except;
try {
apply_block( (*ritr)->block, false /*don't trust*/ );
apply_block( (*ritr)->block, (*ritr)->validated ? controller::block_status::validated : controller::block_status::complete );
head = *ritr;
fork_db.mark_in_current_chain( *ritr, true );
(*ritr)->validated = true;
}
catch (const fc::exception& e) { except = e; }
if (except) {
......@@ -879,7 +891,7 @@ struct controller_impl {
// re-apply good blocks
for( auto ritr = branches.second.rbegin(); ritr != branches.second.rend(); ++ritr ) {
apply_block( (*ritr)->block, true /* we previously validated these blocks*/ );
apply_block( (*ritr)->block, controller::block_status::validated /* we previously validated these blocks*/ );
head = *ritr;
fork_db.mark_in_current_chain( *ritr, true );
}
......@@ -1019,6 +1031,46 @@ struct controller_impl {
}
}
void check_actor_list( const flat_set<account_name>& actors )const {
if( conf.actor_whitelist.size() > 0 ) {
vector<account_name> excluded;
excluded.reserve( actors.size() );
set_difference( actors.begin(), actors.end(),
conf.actor_whitelist.begin(), conf.actor_whitelist.end(),
std::back_inserter(excluded) );
EOS_ASSERT( excluded.size() == 0, actor_whitelist_exception,
"authorizing actor(s) in transaction are not on the actor whitelist: ${actors}",
("actors", excluded)
);
} else if( conf.actor_blacklist.size() > 0 ) {
vector<account_name> blacklisted;
blacklisted.reserve( actors.size() );
set_intersection( actors.begin(), actors.end(),
conf.actor_blacklist.begin(), conf.actor_whitelist.end(),
std::back_inserter(blacklisted)
);
EOS_ASSERT( blacklisted.size() == 0, actor_blacklist_exception,
"authorizing actor(s) in transaction are on the actor blacklist: ${actors}",
("actors", blacklisted)
);
}
}
void check_contract_list( account_name code )const {
if( conf.contract_whitelist.size() > 0 ) {
EOS_ASSERT( conf.contract_whitelist.find( code ) != conf.contract_whitelist.end(),
contract_whitelist_exception,
"account '${code}' is not on the contract whitelist", ("code", code)
);
} else if( conf.contract_blacklist.size() > 0 ) {
EOS_ASSERT( conf.contract_blacklist.find( code ) == conf.contract_blacklist.end(),
contract_blacklist_exception,
"account '${code}' is on the contract blacklist", ("code", code)
);
}
}
/*
bool should_check_tapos()const { return true; }
......@@ -1101,8 +1153,8 @@ chainbase::database& controller::db()const { return my->db; }
fork_database& controller::fork_db()const { return my->fork_db; }
void controller::start_block( block_timestamp_type when, uint16_t confirm_block_count ) {
my->start_block(when, confirm_block_count);
void controller::start_block( block_timestamp_type when, uint16_t confirm_block_count) {
my->start_block(when, confirm_block_count, block_status::incomplete );
}
void controller::finalize_block() {
......@@ -1121,8 +1173,8 @@ void controller::abort_block() {
my->abort_block();
}
void controller::push_block( const signed_block_ptr& b, bool trust ) {
my->push_block( b, trust );
void controller::push_block( const signed_block_ptr& b, block_status s ) {
my->push_block( b, s );
}
void controller::push_confirmation( const header_confirmation& c ) {
......@@ -1288,7 +1340,7 @@ optional<producer_schedule_type> controller::proposed_producers()const {
}
bool controller::skip_auth_check()const {
return my->replaying_irreversible && !my->conf.force_all_checks;
return my->replaying && !my->conf.force_all_checks;
}
bool controller::contracts_console()const {
......@@ -1347,6 +1399,16 @@ vector<transaction_id_type> controller::get_scheduled_transactions() const {
return result;
}
void controller::check_contract_list( account_name code )const {
my->check_contract_list( code );
}
bool controller::is_producing_block()const {
if( !my->pending ) return false;
return (my->pending->_block_status == block_status::incomplete);
}
void controller::validate_referenced_accounts( const transaction& trx )const {
for( const auto& a : trx.context_free_actions ) {
auto* code = my->db.find<account_object, by_name>(a.account);
......
/**
* @file
* @copyright defined in eos/LICENSE.txt
*/
#pragma once
namespace eosio { namespace chain {
class block_context {
public:
enum block_status {
dpos_irreversible = 0, ///< this block has already been applied before by this node and is considered irreversible by DPOS standards
bft_irreversible = 1, ///< this block has already been applied before by this node and is considered irreversible by BFT standards (but not yet DPOS standards)
validated_block = 2, ///< this is a complete block signed by a valid producer and has been previously applied by this node and therefore validated but it is not yet irreversible
completed_block = 3, ///< this is a complete block signed by a valid producer but is not yet irreversible nor has it yet been applied by this node
producing_block = 4, ///< this is an incomplete block that is being produced by a valid producer for their time slot and will be signed by them after the block is completed
speculative_block = 5 ///< this is an incomplete block that is only being speculatively produced by the node (whether it is the node of an active producer or not)
};
block_status status = speculative_block;
bool is_active_producer = false; ///< whether the node applying the block is an active producer (this further modulates behavior when the block status is completed_block)
};
} }
......@@ -36,18 +36,28 @@ namespace eosio { namespace chain {
class controller {
public:
struct config {
path blocks_dir = chain::config::default_blocks_dir_name;
path state_dir = chain::config::default_state_dir_name;
uint64_t state_size = chain::config::default_state_size;
uint64_t reversible_cache_size = chain::config::default_reversible_cache_size;
bool read_only = false;
bool force_all_checks = false;
bool contracts_console = false;
genesis_state genesis;
wasm_interface::vm_type wasm_runtime = chain::config::default_wasm_runtime;
flat_set<account_name> actor_whitelist;
flat_set<account_name> actor_blacklist;
flat_set<account_name> contract_whitelist;
flat_set<account_name> contract_blacklist;
path blocks_dir = chain::config::default_blocks_dir_name;
path state_dir = chain::config::default_state_dir_name;
uint64_t state_size = chain::config::default_state_size;
uint64_t reversible_cache_size = chain::config::default_reversible_cache_size;
bool read_only = false;
bool force_all_checks = false;
bool contracts_console = false;
genesis_state genesis;
wasm_interface::vm_type wasm_runtime = chain::config::default_wasm_runtime;
};
enum class block_status {
irreversible = 0, ///< this block has already been applied before by this node and is considered irreversible
validated = 1, ///< this is a complete block signed by a valid producer and has been previously applied by this node and therefore validated but it is not yet irreversible
complete = 2, ///< this is a complete block signed by a valid producer but is not yet irreversible nor has it yet been applied by this node
incomplete = 3, ///< this is an incomplete block (either being produced by a producer or speculatively produced by a node)
};
controller( const config& cfg );
~controller();
......@@ -101,7 +111,7 @@ namespace eosio { namespace chain {
void commit_block();
void pop_block();
void push_block( const signed_block_ptr& b, bool trust = false /* does the caller trust the block*/ );
void push_block( const signed_block_ptr& b, block_status s = block_status::complete );
/**
* Call this method when a producer confirmation is received, this might update
......@@ -144,6 +154,11 @@ namespace eosio { namespace chain {
block_id_type get_block_id_for_num( uint32_t block_num )const;
void check_contract_list( account_name code )const;
bool is_producing_block()const;
void validate_referenced_accounts( const transaction& t )const;
void validate_expiration( const transaction& t )const;
void validate_tapos( const transaction& t )const;
......@@ -208,6 +223,10 @@ namespace eosio { namespace chain {
} } /// eosio::chain
FC_REFLECT( eosio::chain::controller::config,
(actor_whitelist)
(actor_blacklist)
(contract_whitelist)
(contract_blacklist)
(blocks_dir)
(state_dir)
(state_size)
......
......@@ -105,14 +105,16 @@ namespace eosio { namespace chain {
3010006, "Invalid transaction" )
FC_DECLARE_DERIVED_EXCEPTION( abi_type_exception, chain_type_exception,
3010007, "Invalid ABI" )
FC_DECLARE_DERIVED_EXCEPTION( abi_not_found_exception, chain_type_exception,
3010008, "No ABI found" )
FC_DECLARE_DERIVED_EXCEPTION( block_id_type_exception, chain_type_exception,
3010008, "Invalid block ID" )
3010009, "Invalid block ID" )
FC_DECLARE_DERIVED_EXCEPTION( transaction_id_type_exception, chain_type_exception,
3010009, "Invalid transaction ID" )
3010010, "Invalid transaction ID" )
FC_DECLARE_DERIVED_EXCEPTION( packed_transaction_type_exception, chain_type_exception,
3010010, "Invalid packed transaction" )
3010011, "Invalid packed transaction" )
FC_DECLARE_DERIVED_EXCEPTION( asset_type_exception, chain_type_exception,
3010011, "Invalid asset" )
3010012, "Invalid asset" )
FC_DECLARE_DERIVED_EXCEPTION( fork_database_exception, chain_exception,
......@@ -280,5 +282,16 @@ namespace eosio { namespace chain {
FC_DECLARE_DERIVED_EXCEPTION( wallet_unlocked_exception, wallet_exception,
3120007, "Already unlocked" )
FC_DECLARE_DERIVED_EXCEPTION( whitelist_blacklist_exception, chain_exception,
3130000, "actor or contract whitelist/blacklist exception" )
FC_DECLARE_DERIVED_EXCEPTION( actor_whitelist_exception, chain_exception,
3130001, "Authorizing actor of transaction is not on the whitelist" )
FC_DECLARE_DERIVED_EXCEPTION( actor_blacklist_exception, chain_exception,
3130002, "Authorizing actor of transaction is on the blacklist" )
FC_DECLARE_DERIVED_EXCEPTION( contract_whitelist_exception, chain_exception,
3130003, "Contract to execute is not on the whitelist" )
FC_DECLARE_DERIVED_EXCEPTION( contract_blacklist_exception, chain_exception,
3130004, "Contract to execute is on the blacklist" )
} } // eosio::chain
......@@ -11,9 +11,11 @@ namespace eosio { namespace chain { namespace wasm_constraints {
constexpr unsigned maximum_linear_memory = 33*1024*1024;//bytes
constexpr unsigned maximum_mutable_globals = 1024; //bytes
constexpr unsigned maximum_table_elements = 1024; //elements
constexpr unsigned maximum_section_elements = 1024; //elements
constexpr unsigned maximum_linear_memory_init = 64*1024; //bytes
constexpr unsigned maximum_func_local_bytes = 8192; //bytes
constexpr unsigned maximum_call_depth = 250; //nested calls
constexpr unsigned maximum_code_size = 20*1024*1024;
static constexpr unsigned wasm_page_size = 64*1024;
......
......@@ -6,6 +6,7 @@
#include <eosio/chain/webassembly/runtime_interface.hpp>
#include <eosio/chain/wasm_eosio_injection.hpp>
#include <eosio/chain/transaction_context.hpp>
#include <fc/scoped_exit.hpp>
#include "IR/Module.h"
#include "Runtime/Intrinsics.h"
......@@ -54,6 +55,9 @@ namespace eosio { namespace chain {
{
auto it = instantiation_cache.find(code_id);
if(it == instantiation_cache.end()) {
auto timer_pause = fc::make_scoped_exit([&](){
trx_context.resume_billing_timer();
});
trx_context.pause_billing_timer();
IR::Module module;
try {
......@@ -76,7 +80,6 @@ namespace eosio { namespace chain {
EOS_ASSERT(false, wasm_serialization_error, e.message.c_str());
}
it = instantiation_cache.emplace(code_id, runtime_interface->instantiate_module((const char*)bytes.data(), bytes.size(), parse_initial_memory(module))).first;
trx_context.resume_billing_timer();
}
return it->second;
}
......
......@@ -406,6 +406,8 @@ namespace eosio { namespace chain {
transaction.trx_id = id;
transaction.expiration = expire;
});
} catch( const boost::interprocess::bad_alloc& ) {
throw;
} catch ( ... ) {
EOS_ASSERT( false, tx_duplicate,
"duplicate transaction ${id}", ("id", id ) );
......
......@@ -9,6 +9,7 @@
#include <functional>
#include <unordered_map>
#include <boost/core/typeinfo.hpp>
#include <boost/interprocess/exceptions.hpp>
namespace fc
{
......@@ -382,7 +383,9 @@ namespace fc
FC_MULTILINE_MACRO_END
#define FC_LOG_AND_RETHROW( ) \
catch( fc::exception& er ) { \
catch( const boost::interprocess::bad_alloc& ) {\
throw;\
} catch( fc::exception& er ) { \
wlog( "${details}", ("details",er.to_detail_string()) ); \
FC_RETHROW_EXCEPTION( er, warn, "rethrow" ); \
} catch( const std::exception& e ) { \
......@@ -402,7 +405,9 @@ namespace fc
}
#define FC_CAPTURE_LOG_AND_RETHROW( ... ) \
catch( fc::exception& er ) { \
catch( const boost::interprocess::bad_alloc& ) {\
throw;\
} catch( fc::exception& er ) { \
wlog( "${details}", ("details",er.to_detail_string()) ); \
wdump( __VA_ARGS__ ); \
FC_RETHROW_EXCEPTION( er, warn, "rethrow", FC_FORMAT_ARG_PARAMS(__VA_ARGS__) ); \
......@@ -425,7 +430,9 @@ namespace fc
}
#define FC_CAPTURE_AND_LOG( ... ) \
catch( fc::exception& er ) { \
catch( const boost::interprocess::bad_alloc& ) {\
throw;\
} catch( fc::exception& er ) { \
wlog( "${details}", ("details",er.to_detail_string()) ); \
wdump( __VA_ARGS__ ); \
} catch( const std::exception& e ) { \
......@@ -445,7 +452,9 @@ namespace fc
}
#define FC_LOG_AND_DROP( ... ) \
catch( fc::exception& er ) { \
catch( const boost::interprocess::bad_alloc& ) {\
throw;\
} catch( fc::exception& er ) { \
wlog( "${details}", ("details",er.to_detail_string()) ); \
} catch( const std::exception& e ) { \
fc::exception fce( \
......@@ -468,7 +477,9 @@ namespace fc
* appending the provided log message.
*/
#define FC_RETHROW_EXCEPTIONS( LOG_LEVEL, FORMAT, ... ) \
catch( fc::exception& er ) { \
catch( const boost::interprocess::bad_alloc& ) {\
throw;\
} catch( fc::exception& er ) { \
FC_RETHROW_EXCEPTION( er, LOG_LEVEL, FORMAT, __VA_ARGS__ ); \
} catch( const std::exception& e ) { \
fc::exception fce( \
......@@ -483,7 +494,9 @@ namespace fc
}
#define FC_CAPTURE_AND_RETHROW( ... ) \
catch( fc::exception& er ) { \
catch( const boost::interprocess::bad_alloc& ) {\
throw;\
} catch( fc::exception& er ) { \
FC_RETHROW_EXCEPTION( er, warn, "", FC_FORMAT_ARG_PARAMS(__VA_ARGS__) ); \
} catch( const std::exception& e ) { \
fc::exception fce( \
......@@ -496,4 +509,3 @@ namespace fc
FC_LOG_MESSAGE( warn, "",FC_FORMAT_ARG_PARAMS(__VA_ARGS__)), \
std::current_exception() ); \
}
......@@ -76,6 +76,8 @@ namespace eosio { namespace testing {
static const uint32_t DEFAULT_BILLED_CPU_TIME_US = 2000;
virtual ~base_tester() {};
void init(bool push_genesis = true);
void init(controller::config config);
......@@ -177,12 +179,12 @@ namespace eosio { namespace testing {
}
template< typename KeyType = fc::ecc::private_key_shim >
private_key_type get_private_key( name keyname, string role = "owner" ) const {
static private_key_type get_private_key( name keyname, string role = "owner" ) {
return private_key_type::regenerate<KeyType>(fc::sha256::hash(string(keyname)+role));
}
template< typename KeyType = fc::ecc::private_key_shim >
public_key_type get_public_key( name keyname, string role = "owner" ) const {
static public_key_type get_public_key( name keyname, string role = "owner" ) {
return get_private_key<KeyType>( keyname, role ).get_public_key();
}
......@@ -333,12 +335,19 @@ namespace eosio { namespace testing {
init(true);
}
/*
validating_tester(controller::config config) {
validating_node = std::make_unique<controller>(config);
FC_ASSERT( config.blocks_dir.filename().generic_string() != "."
&& config.state_dir.filename().generic_string() != ".", "invalid path names in controller::config" );
vcfg = config;
vcfg.blocks_dir = vcfg.blocks_dir.parent_path() / std::string("v_").append( vcfg.blocks_dir.filename().generic_string() );
vcfg.state_dir = vcfg.state_dir.parent_path() / std::string("v_").append( vcfg.state_dir.filename().generic_string() );
validating_node = std::make_unique<controller>(vcfg);
validating_node->startup();
init(config);
}
*/
signed_block_ptr produce_block( fc::microseconds skip_time = fc::milliseconds(config::block_interval_ms), uint32_t skip_flag = 0 /*skip_missed_block_penalty*/ )override {
auto sb = _produce_block(skip_time, false, skip_flag | 2);
......
......@@ -6,4 +6,4 @@ set(PublicHeaders
Serialization.h
Timing.h
UTF8.h)
add_custom_target(Inline SOURCES ${PublicHeaders})
\ No newline at end of file
add_custom_target(Inline SOURCES ${PublicHeaders})
......@@ -2,6 +2,7 @@
#include "Platform/Platform.h"
#include "../../../chain/include/eosio/chain/wasm_eosio_constraints.hpp"
#include <string>
#include <vector>
#include <string.h>
......@@ -128,7 +129,12 @@ namespace Serialization
FORCEINLINE void serializeBytes(OutputStream& stream,const U8* bytes,Uptr numBytes)
{ memcpy(stream.advance(numBytes),bytes,numBytes); }
FORCEINLINE void serializeBytes(InputStream& stream,U8* bytes,Uptr numBytes)
{ memcpy(bytes,stream.advance(numBytes),numBytes); }
{
if ( numBytes < eosio::chain::wasm_constraints::wasm_page_size )
memcpy(bytes,stream.advance(numBytes),numBytes);
else
throw FatalSerializationException(std::string("Trying to deserialize bytes of size : " + std::to_string((uint64_t)numBytes)));
}
// Serialize basic C++ types.
template<typename Stream,typename Value>
......@@ -185,12 +191,29 @@ namespace Serialization
};
// Ensure that the input does not encode more than maxBits of data.
enum { numUsedBitsInHighestByte = maxBits - (maxBytes-1) * 7 };
enum { highestByteUsedBitmask = U8(1<<numUsedBitsInHighestByte)-U8(1) };
enum { highestByteSignedBitmask = U8(~U8(highestByteUsedBitmask) & ~U8(0x80)) };
if((bytes[maxBytes-1] & ~highestByteUsedBitmask) != 0
&& ((bytes[maxBytes-1] & ~highestByteUsedBitmask) != U8(highestByteSignedBitmask) || !std::is_signed<Value>::value))
{ throw FatalSerializationException("Invalid LEB encoding: invalid final byte"); }
enum { numUsedBitsInLastByte = maxBits - (maxBytes-1) * 7 };
enum { numUnusedBitsInLast = 8 - numUsedBitsInLastByte };
enum { lastBitUsedMask = U8(1<<(numUsedBitsInLastByte-1)) };
enum { lastByteUsedMask = U8(1<<numUsedBitsInLastByte)-U8(1) };
enum { lastByteSignedMask = U8(~U8(lastByteUsedMask) & ~U8(0x80)) };
const U8 lastByte = bytes[maxBytes-1];
if(!std::is_signed<Value>::value)
{
if((lastByte & ~lastByteUsedMask) != 0)
{
throw FatalSerializationException("Invalid unsigned LEB encoding: unused bits in final byte must be 0");
}
}
else
{
const I8 signBit = I8((lastByte & lastBitUsedMask) << numUnusedBitsInLast);
const I8 signExtendedLastBit = signBit >> numUnusedBitsInLast;
if((lastByte & ~lastByteUsedMask) != (signExtendedLastBit & lastByteSignedMask))
{
throw FatalSerializationException(
"Invalid signed LEB encoding: unused bits in final byte must match the most-significant used bit");
}
}
// Decode the buffer's bytes into the output integer.
value = 0;
......@@ -236,6 +259,7 @@ namespace Serialization
template<typename Stream>
void serialize(Stream& stream,std::string& string)
{
constexpr size_t max_size = eosio::chain::wasm_constraints::maximum_func_local_bytes;
Uptr size = string.size();
serializeVarUInt32(stream,size);
if(Stream::isInput)
......@@ -243,6 +267,8 @@ namespace Serialization
// Advance the stream before resizing the string:
// try to get a serialization exception before making a huge allocation for malformed input.
const U8* inputBytes = stream.advance(size);
if (size >= max_size)
throw FatalSerializationException(std::string("Trying to deserialize string of size : " + std::to_string((uint64_t)size) + ", which is over by "+std::to_string(size - max_size )+" bytes"));
string.resize(size);
memcpy(const_cast<char*>(string.data()),inputBytes,size);
string.shrink_to_fit();
......@@ -253,6 +279,7 @@ namespace Serialization
template<typename Stream,typename Element,typename Allocator,typename SerializeElement>
void serializeArray(Stream& stream,std::vector<Element,Allocator>& vector,SerializeElement serializeElement)
{
constexpr size_t max_size = eosio::chain::wasm_constraints::maximum_func_local_bytes;
Uptr size = vector.size();
serializeVarUInt32(stream,size);
if(Stream::isInput)
......@@ -260,12 +287,14 @@ namespace Serialization
// Grow the vector one element at a time:
// try to get a serialization exception before making a huge allocation for malformed input.
vector.clear();
if (size >= max_size)
throw FatalSerializationException(std::string("Trying to deserialize array of size : " + std::to_string((uint64_t)size) + ", which is over by "+std::to_string(size - max_size )+" bytes"));
for(Uptr index = 0;index < size;++index)
{
vector.push_back(Element());
serializeElement(stream,vector.back());
}
vector.shrink_to_fit();
vector.shrink_to_fit();
}
else
{
......@@ -278,4 +307,4 @@ namespace Serialization
{
serializeArray(stream,vector,[](Stream& stream,Element& element){serialize(stream,element);});
}
}
\ No newline at end of file
}
......@@ -3,6 +3,7 @@
#include "Inline/Timing.h"
#include "Logging/Logging.h"
#include "RuntimePrivate.h"
#include "IR/Validate.h"
#ifdef _DEBUG
// This needs to be 1 to allow debuggers such as Visual Studio to place breakpoints and step through the JITed code.
......@@ -88,6 +89,7 @@ namespace LLVMJIT
{
UnitMemoryManager()
: imageBaseAddress(nullptr)
, numAllocatedImagePages(0)
, isFinalized(false)
, codeSection({0})
, readOnlySection({0})
......@@ -104,7 +106,8 @@ namespace LLVMJIT
}
// Decommit the image pages, but leave them reserved to catch any references to them that might erroneously remain.
Platform::decommitVirtualPages(imageBaseAddress,numAllocatedImagePages);
if(numAllocatedImagePages)
Platform::decommitVirtualPages(imageBaseAddress,numAllocatedImagePages);
}
void registerEHFrames(U8* addr, U64 loadAddr,uintptr_t numBytes) override
......@@ -228,7 +231,8 @@ namespace LLVMJIT
}
~JITUnit()
{
compileLayer->removeModuleSet(handle);
if(handleIsValid)
compileLayer->removeModuleSet(handle);
#ifdef _WIN64
if(pdataCopy) { Platform::deregisterSEHUnwindInfo(reinterpret_cast<Uptr>(pdataCopy)); }
#endif
......@@ -266,6 +270,7 @@ namespace LLVMJIT
std::unique_ptr<ObjectLayer> objectLayer;
std::unique_ptr<CompileLayer> compileLayer;
CompileLayer::ModuleSetHandleT handle;
bool handleIsValid = false;
bool shouldLogMetrics;
struct LoadedObject
......@@ -575,10 +580,7 @@ namespace LLVMJIT
fpm->add(llvm::createCFGSimplificationPass());
fpm->add(llvm::createJumpThreadingPass());
fpm->add(llvm::createConstantPropagationPass());
if( !fpm->doInitialization() ) {
throw std::runtime_error( "do initialization failed" );
}
fpm->doInitialization();
for(auto functionIt = llvmModule->begin();functionIt != llvmModule->end();++functionIt)
{ fpm->run(*functionIt); }
......@@ -597,6 +599,7 @@ namespace LLVMJIT
std::vector<llvm::Module*>{llvmModule},
&memoryManager,
&NullResolver::singleton);
handleIsValid = true;
compileLayer->emitAndFinalize(handle);
if(shouldLogMetrics)
......
#include "../../../chain/include/eosio/chain/wasm_eosio_constraints.hpp"
#include "Inline/BasicTypes.h"
#include "Inline/Serialization.h"
#include "Inline/UTF8.h"
......@@ -487,6 +488,11 @@ namespace WASM
// Deserialize local sets and unpack them into a linear array of local types.
Uptr numLocalSets = 0;
serializeVarUInt32(bodyStream,numLocalSets);
constexpr size_t max_size = eosio::chain::wasm_constraints::maximum_code_size;
if (numBodyBytes >= max_size)
throw FatalSerializationException(std::string("Function body too large"));
for(Uptr setIndex = 0;setIndex < numLocalSets;++setIndex)
{
LocalSet localSet;
......@@ -523,7 +529,6 @@ namespace WASM
};
};
codeValidationStream.finish();
functionDef.code = std::move(irCodeByteStream.getBytes());
}
......@@ -561,6 +566,7 @@ namespace WASM
+ module.memories.imports.size()
+ module.globals.imports.size();
serializeVarUInt32(sectionStream,size);
constexpr size_t max_size = eosio::chain::wasm_constraints::maximum_section_elements;
if(Stream::isInput)
{
for(Uptr index = 0;index < size;++index)
......@@ -584,6 +590,8 @@ namespace WASM
throw FatalSerializationException("invalid import function type index");
}
module.functions.imports.push_back({{functionTypeIndex},std::move(moduleName),std::move(exportName)});
if (module.functions.imports.size() >= max_size)
throw FatalSerializationException(std::string("Too many function imports"));
break;
}
case ObjectKind::table:
......@@ -591,6 +599,8 @@ namespace WASM
TableType tableType;
serialize(sectionStream,tableType);
module.tables.imports.push_back({tableType,std::move(moduleName),std::move(exportName)});
if (module.functions.imports.size() >= max_size)
throw FatalSerializationException(std::string("Too many table imports"));
break;
}
case ObjectKind::memory:
......@@ -598,6 +608,8 @@ namespace WASM
MemoryType memoryType;
serialize(sectionStream,memoryType);
module.memories.imports.push_back({memoryType,std::move(moduleName),std::move(exportName)});
if (module.functions.imports.size() >= max_size)
throw FatalSerializationException(std::string("Too many memory imports"));
break;
}
case ObjectKind::global:
......@@ -605,6 +617,8 @@ namespace WASM
GlobalType globalType;
serialize(sectionStream,globalType);
module.globals.imports.push_back({globalType,std::move(moduleName),std::move(exportName)});
if (module.functions.imports.size() >= max_size)
throw FatalSerializationException(std::string("Too many global imports"));
break;
}
default: throw FatalSerializationException("invalid ObjectKind");
......@@ -661,6 +675,9 @@ namespace WASM
// Grow the vector one element at a time:
// try to get a serialization exception before making a huge allocation for malformed input.
module.functions.defs.clear();
constexpr size_t max_size = eosio::chain::wasm_constraints::maximum_section_elements;
if ( numFunctions >= max_size )
throw FatalSerializationException(std::string("Too many function defs"));
for(Uptr functionIndex = 0;functionIndex < numFunctions;++functionIndex)
{
U32 functionTypeIndex = 0;
......
......@@ -130,6 +130,14 @@ void chain_plugin::set_program_options(options_description& cli, options_descrip
("reversible-blocks-db-size-mb", bpo::value<uint64_t>()->default_value(config::default_reversible_cache_size / (1024 * 1024)), "Maximum size (in MB) of the reversible blocks database")
("contracts-console", bpo::bool_switch()->default_value(false),
"print contract's output to console")
("actor-whitelist", boost::program_options::value<vector<string>>()->composing()->multitoken(),
"Account added to actor whitelist (may specify multiple times)")
("actor-blacklist", boost::program_options::value<vector<string>>()->composing()->multitoken(),
"Account added to actor blacklist (may specify multiple times)")
("contract-whitelist", boost::program_options::value<vector<string>>()->composing()->multitoken(),
"Contract account added to contract whitelist (may specify multiple times)")
("contract-blacklist", boost::program_options::value<vector<string>>()->composing()->multitoken(),
"Contract account added to contract blacklist (may specify multiple times)")
;
#warning TODO: rate limiting
......@@ -162,6 +170,12 @@ void chain_plugin::set_program_options(options_description& cli, options_descrip
;
}
#define LOAD_VALUE_SET(options, name, container) \
if( options.count(name) ) { \
const std::vector<std::string>& ops = options[name].as<std::vector<std::string>>(); \
std::copy(ops.begin(), ops.end(), std::inserter(container, container.end())); \
}
void chain_plugin::plugin_initialize(const variables_map& options) {
ilog("initializing chain plugin");
......@@ -175,7 +189,13 @@ void chain_plugin::plugin_initialize(const variables_map& options) {
my->chain_config = controller::config();
if (options.count("blocks-dir")) {
LOAD_VALUE_SET(options, "actor-whitelist", my->chain_config->actor_whitelist);
LOAD_VALUE_SET(options, "actor-blacklist", my->chain_config->actor_blacklist);
LOAD_VALUE_SET(options, "contract-whitelist", my->chain_config->contract_whitelist);
LOAD_VALUE_SET(options, "contract-blacklist", my->chain_config->contract_blacklist);
if( options.count("blocks-dir") )
{
auto bld = options.at("blocks-dir").as<bfs::path>();
if(bld.is_relative())
my->blocks_dir = app().data_dir() / bld;
......@@ -183,7 +203,7 @@ void chain_plugin::plugin_initialize(const variables_map& options) {
my->blocks_dir = bld;
}
if(options.count("checkpoint"))
if( options.count("checkpoint") )
{
auto cps = options.at("checkpoint").as<vector<string>>();
my->loaded_checkpoints.reserve(cps.size());
......@@ -916,6 +936,8 @@ read_only::abi_json_to_bin_result read_only::abi_json_to_bin( const read_only::a
} EOS_RETHROW_EXCEPTIONS(chain::invalid_action_args_exception,
"'${args}' is invalid args for action '${action}' code '${code}'. expected '${proto}'",
("args", params.args)("action", params.action)("code", params.code)("proto", action_abi_to_variant(abi, action_type)))
} else {
EOS_ASSERT(false, abi_not_found_exception, "No ABI found for ${contract}", ("contract", params.code));
}
return result;
} FC_CAPTURE_AND_RETHROW( (params.code)(params.action)(params.args) )
......@@ -927,6 +949,8 @@ read_only::abi_bin_to_json_result read_only::abi_bin_to_json( const read_only::a
if( abi_serializer::to_abi(code_account.abi, abi) ) {
abi_serializer abis( abi );
result.args = abis.binary_to_variant( abis.get_action_type( params.action ), params.binargs );
} else {
EOS_ASSERT(false, abi_not_found_exception, "No ABI found for ${contract}", ("contract", params.code));
}
return result;
}
......
......@@ -149,8 +149,6 @@ public:
};
struct abi_bin_to_json_result {
fc::variant args;
vector<name> required_scope;
vector<name> required_auth;
};
abi_bin_to_json_result abi_bin_to_json( const abi_bin_to_json_params& params )const;
......@@ -416,6 +414,6 @@ FC_REFLECT( eosio::chain_apis::read_only::producer_info, (producer_name) )
FC_REFLECT( eosio::chain_apis::read_only::abi_json_to_bin_params, (code)(action)(args) )
FC_REFLECT( eosio::chain_apis::read_only::abi_json_to_bin_result, (binargs) )
FC_REFLECT( eosio::chain_apis::read_only::abi_bin_to_json_params, (code)(action)(binargs) )
FC_REFLECT( eosio::chain_apis::read_only::abi_bin_to_json_result, (args)(required_scope)(required_auth) )
FC_REFLECT( eosio::chain_apis::read_only::abi_bin_to_json_result, (args) )
FC_REFLECT( eosio::chain_apis::read_only::get_required_keys_params, (transaction)(available_keys) )
FC_REFLECT( eosio::chain_apis::read_only::get_required_keys_result, (required_keys) )
......@@ -72,6 +72,10 @@ void producer_api_plugin::plugin_startup() {
INVOKE_V_V(producer, resume), 201),
CALL(producer, producer, paused,
INVOKE_R_V(producer, paused), 201),
CALL(producer, producer, get_runtime_options,
INVOKE_R_V(producer, get_runtime_options), 201),
CALL(producer, producer, update_runtime_options,
INVOKE_V_R(producer, update_runtime_options, producer_plugin::runtime_options), 201),
});
}
......
......@@ -17,6 +17,11 @@ class producer_plugin : public appbase::plugin<producer_plugin> {
public:
APPBASE_PLUGIN_REQUIRES((chain_plugin))
struct runtime_options {
fc::optional<int32_t> max_transaction_time;
fc::optional<int32_t> max_irreversible_block_age;
};
producer_plugin();
virtual ~producer_plugin();
......@@ -35,10 +40,14 @@ public:
void pause();
void resume();
bool paused() const;
void update_runtime_options(const runtime_options& options);
runtime_options get_runtime_options() const;
signal<void(const chain::producer_confirmation&)> confirmed_block;
private:
std::shared_ptr<class producer_plugin_impl> my;
};
} //eosiio
} //eosio
FC_REFLECT(eosio::producer_plugin::runtime_options, (max_transaction_time)(max_irreversible_block_age));
......@@ -119,7 +119,6 @@ class producer_plugin_impl : public std::enable_shared_from_this<producer_plugin
boost::program_options::variables_map _options;
bool _production_enabled = false;
bool _pause_production = false;
uint32_t _required_producer_participation = uint32_t(config::required_producer_participation);
uint32_t _production_skip_flags = 0; //eosio::chain::skip_nothing;
std::map<chain::public_key_type, chain::private_key_type> _private_keys;
......@@ -319,7 +318,7 @@ class producer_plugin_impl : public std::enable_shared_from_this<producer_plugin
auto deadline = fc::time_point::now() + fc::milliseconds(_max_transaction_time_ms);
bool deadline_is_subjective = false;
if (_pending_block_mode == pending_block_mode::producing && block_time < deadline) {
if (_max_transaction_time_ms < 0 || (_pending_block_mode == pending_block_mode::producing && block_time < deadline) ) {
deadline_is_subjective = true;
deadline = block_time;
}
......@@ -358,7 +357,7 @@ class producer_plugin_impl : public std::enable_shared_from_this<producer_plugin
}
bool production_disabled_by_policy() {
return !_production_enabled || _pause_production || get_irreversible_block_age() >= _max_irreversible_block_age_us;
return !_production_enabled || _pause_production || (_max_irreversible_block_age_us.count() >= 0 && get_irreversible_block_age() >= _max_irreversible_block_age_us);
}
enum class start_block_result {
......@@ -415,12 +414,6 @@ void producer_plugin::set_program_options(
"Limits the maximum time (in milliseconds) that is allowed a pushed transaction's code to execute before being considered invalid")
("max-irreversible-block-age", bpo::value<int32_t>()->default_value( 30 * 60 ),
"Limits the maximum age (in seconds) of the DPOS Irreversible Block for a chain this node will produce blocks on")
("required-participation", boost::program_options::value<uint32_t>()
->default_value(uint32_t(config::required_producer_participation/config::percent_1))
->notifier([this](uint32_t e) {
my->_required_producer_participation = std::min(e, 100u) * config::percent_1;
}),
"Percent of producers (0-100) that must be participating in order to produce blocks")
("producer-name,p", boost::program_options::value<vector<string>>()->composing()->multitoken(),
"ID of producer controlled by this node (e.g. inita; may specify multiple times)")
("private-key", boost::program_options::value<vector<string>>()->composing()->multitoken()->default_value({fc::json::to_string(private_key_default)},
......@@ -574,6 +567,33 @@ bool producer_plugin::paused() const {
return my->_pause_production;
}
void producer_plugin::update_runtime_options(const runtime_options& options) {
bool check_speculating = false;
if (options.max_transaction_time) {
my->_max_transaction_time_ms = *options.max_transaction_time;
}
if (options.max_irreversible_block_age) {
my->_max_irreversible_block_age_us = fc::seconds(*options.max_irreversible_block_age);
check_speculating = true;
}
if (check_speculating && my->_pending_block_mode == pending_block_mode::speculating) {
chain::controller& chain = app().get_plugin<chain_plugin>().chain();
chain.abort_block();
my->schedule_production_loop();
}
}
producer_plugin::runtime_options producer_plugin::get_runtime_options() const {
return {
my->_max_transaction_time_ms,
my->_max_irreversible_block_age_us.count() < 0 ? -1 : my->_max_irreversible_block_age_us.count() / 1'000'000
};
}
optional<fc::time_point> producer_plugin_impl::calculate_next_block_time(const account_name& producer_name) const {
chain::controller& chain = app().get_plugin<chain_plugin>().chain();
......@@ -665,6 +685,9 @@ producer_plugin_impl::start_block_result producer_plugin_impl::start_block() {
} else if ( _pause_production ) {
elog("Not producing block because production is explicitly paused");
_pending_block_mode = pending_block_mode::speculating;
} else if ( irreversible_block_age >= _max_irreversible_block_age_us ) {
elog("Not producing block because the irreversible block is too old [age:${age}s, max:${max}s]", ("age", irreversible_block_age.count() / 1'000'000)( "max", _max_irreversible_block_age_us.count() / 1'000'000 ));
_pending_block_mode = pending_block_mode::speculating;
}
if (_pending_block_mode == pending_block_mode::producing) {
......@@ -756,7 +779,7 @@ producer_plugin_impl::start_block_result producer_plugin_impl::start_block() {
try {
auto deadline = fc::time_point::now() + fc::milliseconds(_max_transaction_time_ms);
bool deadline_is_subjective = false;
if (_pending_block_mode == pending_block_mode::producing && block_time < deadline) {
if (_max_transaction_time_ms < 0 || ( _pending_block_mode == pending_block_mode::producing && block_time < deadline ) ) {
deadline_is_subjective = true;
deadline = block_time;
}
......@@ -793,7 +816,7 @@ producer_plugin_impl::start_block_result producer_plugin_impl::start_block() {
try {
auto deadline = fc::time_point::now() + fc::milliseconds(_max_transaction_time_ms);
bool deadline_is_subjective = false;
if (_pending_block_mode == pending_block_mode::producing && block_time < deadline) {
if (_max_transaction_time_ms < 0 || ( _pending_block_mode == pending_block_mode::producing && block_time < deadline ) ) {
deadline_is_subjective = true;
deadline = block_time;
}
......
......@@ -222,7 +222,6 @@
printf "\\tExiting now.\\n\\n"
exit 1;
fi
if [ -d "$BUILD_DIR" ]; then
if ! rm -rf "$BUILD_DIR"
then
......
......@@ -215,6 +215,14 @@
printf "\\tExiting now.\\n\\n"
exit 1;
fi
if [ -d "$BUILD_DIR" ]; then
if ! rm -rf "$BUILD_DIR"
then
printf "\\tUnable to remove directory %s. Please remove this directory and run this script %s again. 0\\n" "$BUILD_DIR" "${BASH_SOURCE[0]}"
printf "\\tExiting now.\\n\\n"
exit 1;
fi
fi
printf "\\n\\tBoost 1.67.0 successfully installed at %s/opt/boost_1_67_0.\\n\\n" "${HOME}"
else
printf "\\tBoost 1.67.0 found at %s/opt/boost_1_67_0.\\n" "${HOME}"
......
......@@ -35,7 +35,7 @@ add_test(NAME unit_test_binaryen COMMAND unit_test
-t \!wasm_tests/weighted_cpu_limit_tests
--report_level=detailed --color_output -- --binaryen)
add_test(NAME unit_test_wavm COMMAND unit_test
-t \!wasm_tests/weighted_cpu_limit_tests -t \!wasm_tests/fuzz
-t \!wasm_tests/weighted_cpu_limit_tests
--report_level=detailed --color_output --catch_system_errors=no -- --wavm)
if(ENABLE_COVERAGE_TESTING)
......
......@@ -5049,6 +5049,7 @@ static const char f32_test_wast[] = R"=====(
(call $assert_return_nan (call $nearest (f32.const nan:0x200000)) (i32.const 10016))
))
)=====";
static const char f32_cmp_test_wast[] = R"=====(
(module
(import "env" "require_auth" (func $require_auth (param i64)))
#pragma once
#include <eosio/chain/webassembly/common.hpp>
// These are handcrafted or otherwise tricky to generate with our tool chain
/*
static const char f32_add_wast[] = R"=====(
static const char huge_tables_wast[] = R"=====(
(module
(import "env" "eosio_assert" (func $eosio_assert (param i32 i32)))
(import "env" "sha256" (func $sha256 (param i32 i32 i32)))
(table 0 anyfunc)
(memory $0 1)
(export "memory" (memory $0))
(memory $0 32)
(data (i32.const 4) "hello")
(export "apply" (func $apply))
(func $apply (param $0 i64) (param $1 i64)
(call $eosio_assert (i32.eq (i32.trunc_u/f32 (f32.const 0x3f800000)) (i32.const 0x0)) (i32.const 0))
(func $apply (param $0 i64) (param $1 i64) (param $2 i64)
(call $sha256
(i32.const 4)
(i32.const 5)
(i32.const 16)
)
)
)
)=====";
*/
static const char aligned_ref_wast[] = R"=====(
(module
......
......@@ -117,6 +117,7 @@ BOOST_FIXTURE_TEST_CASE( buysell, eosio_system_tester ) try {
} FC_LOG_AND_RETHROW()
BOOST_FIXTURE_TEST_CASE( stake_unstake, eosio_system_tester ) try {
cross_15_percent_threshold();
BOOST_REQUIRE_EQUAL( core_from_string("0.0000"), get_balance( "alice1111111" ) );
transfer( "eosio", "alice1111111", core_from_string("1000.0000"), "eosio" );
......@@ -174,6 +175,8 @@ BOOST_FIXTURE_TEST_CASE( stake_unstake, eosio_system_tester ) try {
} FC_LOG_AND_RETHROW()
BOOST_FIXTURE_TEST_CASE( stake_unstake_with_transfer, eosio_system_tester ) try {
cross_15_percent_threshold();
issue( "eosio", core_from_string("1000.0000"), config::system_account_name );
issue( "eosio.stake", core_from_string("1000.0000"), config::system_account_name );
BOOST_REQUIRE_EQUAL( core_from_string("0.0000"), get_balance( "alice1111111" ) );
......@@ -229,6 +232,8 @@ BOOST_FIXTURE_TEST_CASE( stake_unstake_with_transfer, eosio_system_tester ) try
} FC_LOG_AND_RETHROW()
BOOST_FIXTURE_TEST_CASE( fail_without_auth, eosio_system_tester ) try {
cross_15_percent_threshold();
issue( "alice1111111", core_from_string("1000.0000"), config::system_account_name );
BOOST_REQUIRE_EQUAL( success(), stake( "eosio", "alice1111111", core_from_string("2000.0000"), core_from_string("1000.0000") ) );
......@@ -313,6 +318,8 @@ BOOST_FIXTURE_TEST_CASE( unstake_negative, eosio_system_tester ) try {
BOOST_FIXTURE_TEST_CASE( unstake_more_than_at_stake, eosio_system_tester ) try {
cross_15_percent_threshold();
issue( "alice1111111", core_from_string("1000.0000"), config::system_account_name );
BOOST_REQUIRE_EQUAL( success(), stake( "alice1111111", core_from_string("200.0000"), core_from_string("100.0000") ) );
......@@ -343,6 +350,8 @@ BOOST_FIXTURE_TEST_CASE( unstake_more_than_at_stake, eosio_system_tester ) try {
BOOST_FIXTURE_TEST_CASE( delegate_to_another_user, eosio_system_tester ) try {
cross_15_percent_threshold();
issue( "alice1111111", core_from_string("1000.0000"), config::system_account_name );
BOOST_REQUIRE_EQUAL( success(), stake ( "alice1111111", "bob111111111", core_from_string("200.0000"), core_from_string("100.0000") ) );
......@@ -397,6 +406,8 @@ BOOST_FIXTURE_TEST_CASE( delegate_to_another_user, eosio_system_tester ) try {
BOOST_FIXTURE_TEST_CASE( stake_unstake_separate, eosio_system_tester ) try {
cross_15_percent_threshold();
issue( "alice1111111", core_from_string("1000.0000"), config::system_account_name );
BOOST_REQUIRE_EQUAL( core_from_string("1000.0000"), get_balance( "alice1111111" ) );
......@@ -433,6 +444,8 @@ BOOST_FIXTURE_TEST_CASE( stake_unstake_separate, eosio_system_tester ) try {
BOOST_FIXTURE_TEST_CASE( adding_stake_partial_unstake, eosio_system_tester ) try {
cross_15_percent_threshold();
issue( "alice1111111", core_from_string("1000.0000"), config::system_account_name );
BOOST_REQUIRE_EQUAL( success(), stake( "alice1111111", "bob111111111", core_from_string("200.0000"), core_from_string("100.0000") ) );
......@@ -472,6 +485,8 @@ BOOST_FIXTURE_TEST_CASE( adding_stake_partial_unstake, eosio_system_tester ) try
} FC_LOG_AND_RETHROW()
BOOST_FIXTURE_TEST_CASE( stake_from_refund, eosio_system_tester ) try {
cross_15_percent_threshold();
issue( "alice1111111", core_from_string("1000.0000"), config::system_account_name );
BOOST_REQUIRE_EQUAL( success(), stake( "alice1111111", "bob111111111", core_from_string("200.0000"), core_from_string("100.0000") ) );
......@@ -593,6 +608,8 @@ BOOST_FIXTURE_TEST_CASE( producer_register_unregister, eosio_system_tester ) try
BOOST_FIXTURE_TEST_CASE( vote_for_producer, eosio_system_tester, * boost::unit_test::tolerance(1e+5) ) try {
cross_15_percent_threshold();
issue( "alice1111111", core_from_string("1000.0000"), config::system_account_name );
fc::variant params = producer_parameters_example(1);
BOOST_REQUIRE_EQUAL( success(), push_action( N(alice1111111), N(regproducer), mvo()
......@@ -976,6 +993,8 @@ BOOST_FIXTURE_TEST_CASE( proxy_register_unregister_keeps_stake, eosio_system_tes
BOOST_FIXTURE_TEST_CASE( proxy_stake_unstake_keeps_proxy_flag, eosio_system_tester ) try {
cross_15_percent_threshold();
BOOST_REQUIRE_EQUAL( success(), push_action( N(alice1111111), N(regproxy), mvo()
("proxy", "alice1111111")
("isproxy", true)
......@@ -1006,6 +1025,8 @@ BOOST_FIXTURE_TEST_CASE( proxy_stake_unstake_keeps_proxy_flag, eosio_system_test
BOOST_FIXTURE_TEST_CASE( proxy_actions_affect_producers, eosio_system_tester, * boost::unit_test::tolerance(1e+5) ) try {
cross_15_percent_threshold();
create_accounts_with_resources( { N(defproducer1), N(defproducer2), N(defproducer3) } );
BOOST_REQUIRE_EQUAL( success(), regproducer( "defproducer1", 1) );
BOOST_REQUIRE_EQUAL( success(), regproducer( "defproducer2", 2) );
......@@ -1552,7 +1573,7 @@ BOOST_FIXTURE_TEST_CASE(multiple_producer_pay, eosio_system_tester, * boost::uni
("producers", vector<account_name>(producer_names.begin(), producer_names.begin()+21))
)
);
produce_block(fc::hours(9));
produce_blocks(8 * 21 * 12);
......@@ -1736,6 +1757,8 @@ BOOST_FIXTURE_TEST_CASE(producer_onblock_check, eosio_system_tester) try {
("producers", vector<account_name>(producer_names.begin(), producer_names.begin()+10))
));
BOOST_CHECK_EQUAL( wasm_assert_msg("not enough has been staked for users to unstake"), unstake( "producvotera", core_from_string("50.0000"), core_from_string("50.0000") ) );
// give a chance for everyone to produce blocks
{
produce_blocks(21 * 12);
......@@ -1807,9 +1830,13 @@ BOOST_FIXTURE_TEST_CASE(producer_onblock_check, eosio_system_tester) try {
BOOST_REQUIRE(0 < get_balance(producer_names.front()).get_amount());
}
BOOST_CHECK_EQUAL( success(), unstake( "producvotera", core_from_string("50.0000"), core_from_string("50.0000") ) );
} FC_LOG_AND_RETHROW()
BOOST_FIXTURE_TEST_CASE( voters_actions_affect_proxy_and_producers, eosio_system_tester, * boost::unit_test::tolerance(1e+6) ) try {
cross_15_percent_threshold();
create_accounts_with_resources( { N(donald111111), N(defproducer1), N(defproducer2), N(defproducer3) } );
BOOST_REQUIRE_EQUAL( success(), regproducer( "defproducer1", 1) );
BOOST_REQUIRE_EQUAL( success(), regproducer( "defproducer2", 2) );
......@@ -2555,7 +2582,7 @@ BOOST_FIXTURE_TEST_CASE( setparams, eosio_system_tester ) try {
auto active_params = control->get_global_properties().configuration;
BOOST_REQUIRE_EQUAL( params.max_block_net_usage, active_params.max_block_net_usage );
BOOST_REQUIRE_EQUAL( params.max_transaction_lifetime, active_params.max_transaction_lifetime );
} FC_LOG_AND_RETHROW()
BOOST_AUTO_TEST_SUITE_END()
......@@ -490,6 +490,48 @@ public:
return producer_names;
}
void cross_15_percent_threshold() {
setup_producer_accounts({N(producer1111)});
regproducer(N(producer1111));
{
signed_transaction trx;
set_transaction_headers(trx);
trx.actions.emplace_back( get_action( config::system_account_name, N(delegatebw),
vector<permission_level>{{config::system_account_name, config::active_name}},
mvo()
("from", name{config::system_account_name})
("receiver", "producer1111")
("stake_net_quantity", core_from_string("150000000.0000") )
("stake_cpu_quantity", core_from_string("0.0000") )
("transfer", 1 )
)
);
trx.actions.emplace_back( get_action( config::system_account_name, N(voteproducer),
vector<permission_level>{{N(producer1111), config::active_name}},
mvo()
("voter", "producer1111")
("proxy", name(0).to_string())
("producers", vector<account_name>(1, N(producer1111)))
)
);
trx.actions.emplace_back( get_action( config::system_account_name, N(undelegatebw),
vector<permission_level>{{N(producer1111), config::active_name}},
mvo()
("from", "producer1111")
("receiver", "producer1111")
("unstake_net_quantity", core_from_string("150000000.0000") )
("unstake_cpu_quantity", core_from_string("0.0000") )
)
);
set_transaction_headers(trx);
trx.sign( get_private_key( config::system_account_name, "active" ), control->get_chain_id() );
trx.sign( get_private_key( N(producer1111), "active" ), control->get_chain_id() );
push_transaction( trx );
}
}
abi_serializer abi_ser;
abi_serializer token_abi_ser;
};
......
......@@ -1071,9 +1071,82 @@ BOOST_FIXTURE_TEST_CASE(eosio_abi, TESTER) try {
produce_block();
} FC_LOG_AND_RETHROW()
BOOST_FIXTURE_TEST_CASE( test_table_key_validation, TESTER ) try {
BOOST_FIXTURE_TEST_CASE( check_big_deserialization, TESTER ) try {
produce_blocks(2);
create_accounts( {N(cbd)} );
produce_block();
std::stringstream ss;
ss << "(module ";
ss << "(export \"apply\" (func $apply))";
ss << " (func $apply (param $0 i64)(param $1 i64)(param $2 i64))";
for(unsigned int i = 0; i < wasm_constraints::maximum_section_elements-2; i++)
ss << " (func " << "$AA_" << i << ")";
ss << ")";
set_code(N(cbd), ss.str().c_str());
produce_blocks(1);
produce_blocks(1);
ss.str("");
ss << "(module ";
ss << "(export \"apply\" (func $apply))";
ss << " (func $apply (param $0 i64)(param $1 i64)(param $2 i64))";
for(unsigned int i = 0; i < wasm_constraints::maximum_section_elements; i++)
ss << " (func " << "$AA_" << i << ")";
ss << ")";
BOOST_CHECK_THROW(set_code(N(cbd), ss.str().c_str()), wasm_serialization_error);
produce_blocks(1);
ss.str("");
ss << "(module ";
ss << "(export \"apply\" (func $apply))";
ss << " (func $apply (param $0 i64)(param $1 i64)(param $2 i64))";
ss << " (func $aa ";
for(unsigned int i = 0; i < wasm_constraints::maximum_code_size; i++)
ss << " (drop (i32.const 3))";
ss << "))";
BOOST_CHECK_THROW(set_code(N(cbd), ss.str().c_str()), fc::assert_exception); // this is caught first by MAX_SIZE_OF_ARRAYS check
produce_blocks(1);
ss.str("");
ss << "(module ";
ss << "(memory $0 1)";
ss << "(data (i32.const 20) \"";
for(unsigned int i = 0; i < wasm_constraints::maximum_func_local_bytes-1; i++)
ss << 'a';
ss << "\")";
ss << "(export \"apply\" (func $apply))";
ss << " (func $apply (param $0 i64)(param $1 i64)(param $2 i64))";
ss << " (func $aa ";
ss << " (drop (i32.const 3))";
ss << "))";
set_code(N(cbd), ss.str().c_str());
produce_blocks(1);
ss.str("");
ss << "(module ";
ss << "(memory $0 1)";
ss << "(data (i32.const 20) \"";
for(unsigned int i = 0; i < wasm_constraints::maximum_func_local_bytes; i++)
ss << 'a';
ss << "\")";
ss << "(export \"apply\" (func $apply))";
ss << " (func $apply (param $0 i64)(param $1 i64)(param $2 i64))";
ss << " (func $aa ";
ss << " (drop (i32.const 3))";
ss << "))";
BOOST_CHECK_THROW(set_code(N(cbd), ss.str().c_str()), wasm_serialization_error);
produce_blocks(1);
} FC_LOG_AND_RETHROW()
BOOST_FIXTURE_TEST_CASE( check_table_maximum, TESTER ) try {
produce_blocks(2);
create_accounts( {N(tbl)} );
......
#include <boost/test/unit_test.hpp>
#include <eosio/testing/tester.hpp>
#include <eosio/testing/tester_network.hpp>
#include <fc/variant_object.hpp>
#include <eosio.token/eosio.token.wast.hpp>
#include <eosio.token/eosio.token.abi.hpp>
#ifdef NON_VALIDATING_TEST
#define TESTER tester
#else
#define TESTER validating_tester
#endif
using namespace eosio;
using namespace eosio::chain;
using namespace eosio::testing;
using mvo = fc::mutable_variant_object;
class whitelist_blacklist_tester {
public:
whitelist_blacklist_tester() {}
static controller::config get_default_chain_configuration( const fc::path& p ) {
controller::config cfg;
cfg.blocks_dir = p / config::default_blocks_dir_name;
cfg.state_dir = p / config::default_state_dir_name;
cfg.state_size = 1024*1024*8;
cfg.reversible_cache_size = 1024*1024*8;
cfg.contracts_console = true;
cfg.genesis.initial_timestamp = fc::time_point::from_iso_string("2020-01-01T00:00:00.000");
cfg.genesis.initial_key = base_tester::get_public_key( config::system_account_name, "active" );
for(int i = 0; i < boost::unit_test::framework::master_test_suite().argc; ++i) {
if(boost::unit_test::framework::master_test_suite().argv[i] == std::string("--binaryen"))
cfg.wasm_runtime = chain::wasm_interface::vm_type::binaryen;
else if(boost::unit_test::framework::master_test_suite().argv[i] == std::string("--wavm"))
cfg.wasm_runtime = chain::wasm_interface::vm_type::wavm;
else
cfg.wasm_runtime = chain::wasm_interface::vm_type::binaryen;
}
return cfg;
}
void init() {
auto cfg = get_default_chain_configuration( tempdir.path() );
cfg.actor_whitelist = actor_whitelist;
cfg.actor_blacklist = actor_blacklist;
cfg.contract_whitelist = contract_whitelist;
cfg.contract_blacklist = contract_blacklist;
chain.emplace(cfg);
chain->create_accounts({N(eosio.token), N(alice), N(bob), N(charlie)});
chain->set_code(N(eosio.token), eosio_token_wast);
chain->set_abi(N(eosio.token), eosio_token_abi);
chain->push_action( N(eosio.token), N(create), N(eosio.token), mvo()
( "issuer", "eosio.token" )
( "maximum_supply", "1000000.00 TOK" )
);
chain->push_action( N(eosio.token), N(issue), N(eosio.token), mvo()
( "to", "eosio.token" )
( "quantity", "1000000.00 TOK" )
( "memo", "issue" )
);
chain->produce_blocks();
}
transaction_trace_ptr transfer( account_name from, account_name to, string quantity = "1.00 TOK" ) {
return chain->push_action( N(eosio.token), N(transfer), from, mvo()
( "from", from )
( "to", to )
( "quantity", quantity )
( "memo", "" )
);
}
fc::temp_directory tempdir; // Must come before chain
fc::optional<TESTER> chain;
flat_set<account_name> actor_whitelist;
flat_set<account_name> actor_blacklist;
flat_set<account_name> contract_whitelist;
flat_set<account_name> contract_blacklist;
};
struct transfer_args {
account_name from;
account_name to;
asset quantity;
string memo;
};
FC_REFLECT( transfer_args, (from)(to)(quantity)(memo) )
BOOST_AUTO_TEST_SUITE(whitelist_blacklist_tests)
BOOST_AUTO_TEST_CASE( actor_whitelist ) { try {
whitelist_blacklist_tester test;
test.actor_whitelist = {N(eosio), N(eosio.token), N(alice)};
test.init();
test.transfer( N(eosio.token), N(alice), "1000.00 TOK" );
test.transfer( N(alice), N(bob), "100.00 TOK" );
BOOST_CHECK_EXCEPTION( test.transfer( N(bob), N(alice) ),
actor_whitelist_exception,
fc_exception_message_is("authorizing actor(s) in transaction are not on the actor whitelist: [\"bob\"]")
);
signed_transaction trx;
trx.actions.emplace_back( vector<permission_level>{{N(alice),config::active_name}, {N(bob),config::active_name}},
N(eosio.token), N(transfer),
fc::raw::pack(transfer_args{
.from = N(alice),
.to = N(bob),
.quantity = asset::from_string("10.00 TOK"),
.memo = ""
})
);
test.chain->set_transaction_headers(trx);
trx.sign( test.chain->get_private_key( N(alice), "active" ), test.chain->control->get_chain_id() );
trx.sign( test.chain->get_private_key( N(bob), "active" ), test.chain->control->get_chain_id() );
BOOST_CHECK_EXCEPTION( test.chain->push_transaction( trx ),
actor_whitelist_exception,
fc_exception_message_starts_with("authorizing actor(s) in transaction are not on the actor whitelist: [\"bob\"]")
);
test.chain->produce_blocks();
} FC_LOG_AND_RETHROW() }
BOOST_AUTO_TEST_CASE( actor_blacklist ) { try {
whitelist_blacklist_tester test;
test.actor_blacklist = {N(bob)};
test.init();
test.transfer( N(eosio.token), N(alice), "1000.00 TOK" );
test.transfer( N(alice), N(bob), "100.00 TOK" );
BOOST_CHECK_EXCEPTION( test.transfer( N(bob), N(alice) ),
actor_blacklist_exception,
fc_exception_message_starts_with("authorizing actor(s) in transaction are on the actor blacklist: [\"bob\"]")
);
signed_transaction trx;
trx.actions.emplace_back( vector<permission_level>{{N(alice),config::active_name}, {N(bob),config::active_name}},
N(eosio.token), N(transfer),
fc::raw::pack(transfer_args{
.from = N(alice),
.to = N(bob),
.quantity = asset::from_string("10.00 TOK"),
.memo = ""
})
);
test.chain->set_transaction_headers(trx);
trx.sign( test.chain->get_private_key( N(alice), "active" ), test.chain->control->get_chain_id() );
trx.sign( test.chain->get_private_key( N(bob), "active" ), test.chain->control->get_chain_id() );
BOOST_CHECK_EXCEPTION( test.chain->push_transaction( trx ),
actor_blacklist_exception,
fc_exception_message_starts_with("authorizing actor(s) in transaction are on the actor blacklist: [\"bob\"]")
);
test.chain->produce_blocks();
} FC_LOG_AND_RETHROW() }
BOOST_AUTO_TEST_CASE( contract_whitelist ) { try {
whitelist_blacklist_tester test;
test.contract_whitelist = {N(eosio), N(eosio.token), N(bob)};
test.init();
test.transfer( N(eosio.token), N(alice), "1000.00 TOK" );
test.transfer( N(alice), N(eosio.token) );
test.transfer( N(alice), N(bob) );
test.transfer( N(alice), N(charlie), "100.00 TOK" );
test.transfer( N(charlie), N(alice) );
test.chain->produce_blocks();
test.chain->set_code(N(bob), eosio_token_wast);
test.chain->set_abi(N(bob), eosio_token_abi);
test.chain->produce_blocks();
test.chain->set_code(N(charlie), eosio_token_wast);
test.chain->set_abi(N(charlie), eosio_token_abi);
test.chain->produce_blocks();
test.transfer( N(alice), N(bob) );
BOOST_CHECK_EXCEPTION( test.transfer( N(alice), N(charlie) ),
contract_whitelist_exception,
fc_exception_message_is("account 'charlie' is not on the contract whitelist")
);
test.chain->push_action( N(bob), N(create), N(bob), mvo()
( "issuer", "bob" )
( "maximum_supply", "1000000.00 CUR" )
);
BOOST_CHECK_EXCEPTION( test.chain->push_action( N(charlie), N(create), N(charlie), mvo()
( "issuer", "charlie" )
( "maximum_supply", "1000000.00 CUR" )
),
contract_whitelist_exception,
fc_exception_message_starts_with("account 'charlie' is not on the contract whitelist")
);
test.chain->produce_blocks();
} FC_LOG_AND_RETHROW() }
BOOST_AUTO_TEST_CASE( contract_blacklist ) { try {
whitelist_blacklist_tester test;
test.contract_blacklist = {N(charlie)};
test.init();
test.transfer( N(eosio.token), N(alice), "1000.00 TOK" );
test.transfer( N(alice), N(eosio.token) );
test.transfer( N(alice), N(bob) );
test.transfer( N(alice), N(charlie), "100.00 TOK" );
test.transfer( N(charlie), N(alice) );
test.chain->produce_blocks();
test.chain->set_code(N(bob), eosio_token_wast);
test.chain->set_abi(N(bob), eosio_token_abi);
test.chain->produce_blocks();
test.chain->set_code(N(charlie), eosio_token_wast);
test.chain->set_abi(N(charlie), eosio_token_abi);
test.chain->produce_blocks();
test.transfer( N(alice), N(bob) );
BOOST_CHECK_EXCEPTION( test.transfer( N(alice), N(charlie) ),
contract_blacklist_exception,
fc_exception_message_is("account 'charlie' is on the contract blacklist")
);
test.chain->push_action( N(bob), N(create), N(bob), mvo()
( "issuer", "bob" )
( "maximum_supply", "1000000.00 CUR" )
);
BOOST_CHECK_EXCEPTION( test.chain->push_action( N(charlie), N(create), N(charlie), mvo()
( "issuer", "charlie" )
( "maximum_supply", "1000000.00 CUR" )
),
contract_blacklist_exception,
fc_exception_message_starts_with("account 'charlie' is on the contract blacklist")
);
test.chain->produce_blocks();
} FC_LOG_AND_RETHROW() }
BOOST_AUTO_TEST_SUITE_END()
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册