/* * Copyright (c) 2015, Cryptonomex, Inc. * All rights reserved. * * This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and * the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification, * are permitted until September 8, 2015, provided that the following conditions are met: * * 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include namespace graphene { namespace chain { void_result proposal_create_evaluator::do_evaluate(const proposal_create_operation& o) { try { const database& d = db(); const auto& global_parameters = d.get_global_properties().parameters; FC_ASSERT( o.expiration_time > d.head_block_time(), "Proposal has already expired on creation." ); FC_ASSERT( o.expiration_time <= d.head_block_time() + global_parameters.maximum_proposal_lifetime, "Proposal expiration time is too far in the future."); FC_ASSERT( !o.review_period_seconds || fc::seconds(*o.review_period_seconds) < (o.expiration_time - d.head_block_time()), "Proposal review period must be less than its overall lifetime." ); { // If we're dealing with the genesis authority, make sure this transaction has a sufficient review period. flat_set auths; for( auto& op : o.proposed_ops ) { operation_get_required_active_authorities(op.op, auths); operation_get_required_owner_authorities(op.op, auths); } if( auths.find(account_id_type()) != auths.end() ) { GRAPHENE_ASSERT( o.review_period_seconds.valid(), proposal_create_review_period_required, "Review period not given, but at least ${min} required", ("min", global_parameters.committee_proposal_review_period) ); GRAPHENE_ASSERT( *o.review_period_seconds >= global_parameters.committee_proposal_review_period, proposal_create_review_period_insufficient, "Review period of ${t} required, but at least ${min} required", ("t", *o.review_period_seconds) ("min", global_parameters.committee_proposal_review_period) ); } } for( const op_wrapper& op : o.proposed_ops ) _proposed_trx.operations.push_back(op.op); _proposed_trx.validate(); return void_result(); } FC_CAPTURE_AND_RETHROW( (o) ) } object_id_type proposal_create_evaluator::do_apply(const proposal_create_operation& o) { try { database& d = db(); const proposal_object& proposal = d.create([&](proposal_object& proposal) { proposal.proposed_transaction = _proposed_trx; proposal.expiration_time = o.expiration_time; if( o.review_period_seconds ) proposal.review_period_time = o.expiration_time - *o.review_period_seconds; //Populate the required approval sets flat_set required_active; for( auto& op : _proposed_trx.operations ) { operation_get_required_active_authorities(op, required_active); operation_get_required_owner_authorities(op, proposal.required_owner_approvals); } //All accounts which must provide both owner and active authority should be omitted from the active authority set; //owner authority approval implies active authority approval. std::set_difference(required_active.begin(), required_active.end(), proposal.required_owner_approvals.begin(), proposal.required_owner_approvals.end(), std::inserter(proposal.required_active_approvals, proposal.required_active_approvals.begin())); }); return proposal.id; } FC_CAPTURE_AND_RETHROW( (o) ) } void_result proposal_update_evaluator::do_evaluate(const proposal_update_operation& o) { try { database& d = db(); _proposal = &o.proposal(d); if( _proposal->review_period_time && d.head_block_time() >= *_proposal->review_period_time ) FC_ASSERT( o.active_approvals_to_add.empty() && o.owner_approvals_to_add.empty(), "This proposal is in its review period. No new approvals may be added." ); for( account_id_type id : o.active_approvals_to_remove ) { FC_ASSERT( _proposal->available_active_approvals.find(id) != _proposal->available_active_approvals.end(), "", ("id", id)("available", _proposal->available_active_approvals) ); } for( account_id_type id : o.owner_approvals_to_remove ) { FC_ASSERT( _proposal->available_owner_approvals.find(id) != _proposal->available_owner_approvals.end(), "", ("id", id)("available", _proposal->available_owner_approvals) ); } /* All authority checks happen outside of evaluators, TODO: verify this is checked elsewhere */ if( (d.get_node_properties().skip_flags & database::skip_authority_check) == 0 ) { for( const auto& id : o.key_approvals_to_add ) { FC_ASSERT( trx_state->signed_by(id) ); } for( const auto& id : o.key_approvals_to_remove ) { FC_ASSERT( trx_state->signed_by(id) ); } } return void_result(); } FC_CAPTURE_AND_RETHROW( (o) ) } void_result proposal_update_evaluator::do_apply(const proposal_update_operation& o) { try { database& d = db(); // Potential optimization: if _executed_proposal is true, we can skip the modify step and make push_proposal skip // signature checks. This isn't done now because I just wrote all the proposals code, and I'm not yet 100% sure the // required approvals are sufficient to authorize the transaction. d.modify(*_proposal, [&o, &d](proposal_object& p) { p.available_active_approvals.insert(o.active_approvals_to_add.begin(), o.active_approvals_to_add.end()); p.available_owner_approvals.insert(o.owner_approvals_to_add.begin(), o.owner_approvals_to_add.end()); for( account_id_type id : o.active_approvals_to_remove ) p.available_active_approvals.erase(id); for( account_id_type id : o.owner_approvals_to_remove ) p.available_owner_approvals.erase(id); for( const auto& id : o.key_approvals_to_add ) p.available_key_approvals.insert(id); for( const auto& id : o.key_approvals_to_remove ) p.available_key_approvals.erase(id); }); // If the proposal has a review period, don't bother attempting to authorize/execute it. // Proposals with a review period may never be executed except at their expiration. if( _proposal->review_period_time ) return void_result(); if( _proposal->is_authorized_to_execute(d) ) { // All required approvals are satisfied. Execute! _executed_proposal = true; try { _processed_transaction = d.push_proposal(*_proposal); } catch(fc::exception& e) { wlog("Proposed transaction ${id} failed to apply once approved with exception:\n----\n${reason}\n----\nWill try again when it expires.", ("id", o.proposal)("reason", e.to_detail_string())); _proposal_failed = true; } } return void_result(); } FC_CAPTURE_AND_RETHROW( (o) ) } void_result proposal_delete_evaluator::do_evaluate(const proposal_delete_operation& o) { try { database& d = db(); _proposal = &o.proposal(d); auto required_approvals = o.using_owner_authority? &_proposal->required_owner_approvals : &_proposal->required_active_approvals; FC_ASSERT( required_approvals->find(o.fee_paying_account) != required_approvals->end(), "Provided authority is not authoritative for this proposal.", ("provided", o.fee_paying_account)("required", *required_approvals)); return void_result(); } FC_CAPTURE_AND_RETHROW( (o) ) } void_result proposal_delete_evaluator::do_apply(const proposal_delete_operation& o) { try { db().remove(*_proposal); return void_result(); } FC_CAPTURE_AND_RETHROW( (o) ) } } } // graphene::chain