Skip to content

Commit

Permalink
Merge pull request #1860 from nathanhourt/bsip40
Browse files Browse the repository at this point in the history
BSIP40 Implementation
  • Loading branch information
nathanielhourt authored Oct 21, 2019
2 parents 7836bcc + 9da10fe commit f182b60
Show file tree
Hide file tree
Showing 49 changed files with 4,321 additions and 84 deletions.
9 changes: 7 additions & 2 deletions libraries/app/database_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <graphene/chain/get_config.hpp>
#include <graphene/chain/hardfork.hpp>
#include <graphene/protocol/pts_address.hpp>
#include <graphene/protocol/restriction_predicate.hpp>

#include <fc/crypto/hex.hpp>
#include <fc/rpc/api_connection.hpp>
Expand Down Expand Up @@ -510,7 +511,7 @@ std::map<std::string, full_account> database_api_impl::get_full_accounts( const
// Add the account's balances
const auto& balances = _db.get_index_type< primary_index< account_balance_index > >().
get_secondary_index< balances_by_account_index >().get_account_balances( account->id );
for( const auto balance : balances )
for( const auto& balance : balances )
{
if(acnt.balances.size() >= api_limit_get_full_accounts_lists) {
acnt.more_data_available.balances = true;
Expand Down Expand Up @@ -738,7 +739,7 @@ vector<asset> database_api_impl::get_account_balances( const std::string& accoun
const auto& balance_index = _db.get_index_type< primary_index< account_balance_index > >();
const auto& balances = balance_index.get_secondary_index< balances_by_account_index >()
.get_account_balances( acnt );
for( const auto balance : balances )
for( const auto& balance : balances )
result.push_back( balance.second->get_balance() );
}
else
Expand Down Expand Up @@ -2002,6 +2003,8 @@ bool database_api_impl::verify_authority( const signed_transaction& trx )const
trx.verify_authority( _db.get_chain_id(),
[this]( account_id_type id ){ return &id(_db).active; },
[this]( account_id_type id ){ return &id(_db).owner; },
[this]( account_id_type id, const operation& op, rejected_predicate_map* rejects ) {
return _db.get_viable_custom_authorities(id, op, rejects); },
allow_non_immediate_owner,
_db.get_global_properties().parameters.max_authority_depth );
return true;
Expand All @@ -2027,6 +2030,8 @@ bool database_api_impl::verify_account_authority( const string& account_name_or_
graphene::chain::verify_authority(ops, keys,
[this]( account_id_type id ){ return &id(_db).active; },
[this]( account_id_type id ){ return &id(_db).owner; },
// Use a no-op lookup for custom authorities; we don't want it even if one does apply for our dummy op
[](auto, auto, auto*) { return vector<authority>(); },
true, MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(_db.head_block_time()) );
}
catch (fc::exception& ex)
Expand Down
1 change: 1 addition & 0 deletions libraries/chain/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ add_library( graphene_chain
htlc_evaluator.cpp
confidential_evaluator.cpp
special_authority_evaluation.cpp
custom_authority_evaluator.cpp
buyback.cpp

account_object.cpp
Expand Down
19 changes: 16 additions & 3 deletions libraries/chain/committee_member_evaluator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,30 @@ void_result committee_member_update_evaluator::do_apply( const committee_member_
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }

void_result committee_member_update_global_parameters_evaluator::do_evaluate(const committee_member_update_global_parameters_operation& o)
void_result committee_member_update_global_parameters_evaluator::do_evaluate(
const committee_member_update_global_parameters_operation& o)
{ try {
FC_ASSERT(trx_state->_is_proposed_trx);

FC_ASSERT( db().head_block_time() > HARDFORK_CORE_1468_TIME || !o.new_parameters.extensions.value.updatable_htlc_options.valid(),
auto now = db().head_block_time();
FC_ASSERT( now > HARDFORK_CORE_1468_TIME || !o.new_parameters.extensions.value.updatable_htlc_options.valid(),
"Unable to set HTLC parameters until hardfork." );
if (!HARDFORK_BSIP_40_PASSED( now )) {
FC_ASSERT( !o.new_parameters.extensions.value.custom_authority_options.valid(),
"Unable to set Custom Authority Options until hardfork BSIP 40." );
FC_ASSERT( !o.new_parameters.current_fees->exists<custom_authority_create_operation>(),
"Unable to set Custom Authority operation fees until hardfork BSIP 40." );
FC_ASSERT( !o.new_parameters.current_fees->exists<custom_authority_update_operation>(),
"Unable to set Custom Authority operation fees until hardfork BSIP 40." );
FC_ASSERT( !o.new_parameters.current_fees->exists<custom_authority_delete_operation>(),
"Unable to set Custom Authority operation fees until hardfork BSIP 40." );
}

return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }

void_result committee_member_update_global_parameters_evaluator::do_apply(const committee_member_update_global_parameters_operation& o)
void_result committee_member_update_global_parameters_evaluator::do_apply(
const committee_member_update_global_parameters_operation& o)
{ try {
db().modify(db().get_global_properties(), [&o](global_property_object& p) {
p.pending_parameters = o.new_parameters;
Expand Down
192 changes: 192 additions & 0 deletions libraries/chain/custom_authority_evaluator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
/*
* Copyright (c) 2019 Contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

#include <graphene/chain/custom_authority_evaluator.hpp>
#include <graphene/chain/custom_authority_object.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/exceptions.hpp>
#include <graphene/chain/hardfork_visitor.hpp>

namespace graphene { namespace chain {

void_result custom_authority_create_evaluator::do_evaluate(const custom_authority_create_operation& op)
{ try {
const database& d = db();
auto now = d.head_block_time();
FC_ASSERT(HARDFORK_BSIP_40_PASSED(now), "Custom active authorities are not yet enabled");

op.account(d);

const auto& config = d.get_global_properties().parameters.extensions.value.custom_authority_options;
FC_ASSERT(config.valid(), "Cannot use custom authorities yet: global configuration not set");
FC_ASSERT(op.valid_to > now, "Custom authority expiration must be in the future");
FC_ASSERT((op.valid_to - now).to_seconds() <= config->max_custom_authority_lifetime_seconds,
"Custom authority lifetime exceeds maximum limit");

bool operation_forked_in = hardfork_visitor(now).visit((operation::tag_type)op.operation_type.value);
FC_ASSERT(operation_forked_in, "Cannot create custom authority for operation which is not valid yet");

auto restriction_count = restriction::restriction_count(op.restrictions);
FC_ASSERT(restriction_count <= config->max_custom_authority_restrictions,
"Custom authority has more than the maximum number of restrictions");

for (const auto& account_weight_pair : op.auth.account_auths)
account_weight_pair.first(d);

const auto& index = d.get_index_type<custom_authority_index>().indices().get<by_account_custom>();
auto range = index.equal_range(op.account);
FC_ASSERT(std::distance(range.first, range.second) < config->max_custom_authorities_per_account,
"Cannot create custom authority: account already has maximum number");
range = index.equal_range(boost::make_tuple(op.account, op.operation_type));
FC_ASSERT(std::distance(range.first, range.second) < config->max_custom_authorities_per_account_op,
"Cannot create custom authority: account already has maximum number for this operation type");

return void_result();
} FC_CAPTURE_AND_RETHROW((op)) }

object_id_type custom_authority_create_evaluator::do_apply(const custom_authority_create_operation& op)
{ try {
database& d = db();

return d.create<custom_authority_object>([&op] (custom_authority_object& obj) mutable {
obj.account = op.account;
obj.enabled = op.enabled;
obj.valid_from = op.valid_from;
obj.valid_to = op.valid_to;
obj.operation_type = op.operation_type;
obj.auth = op.auth;
std::for_each(op.restrictions.begin(), op.restrictions.end(), [&obj](const restriction& r) mutable {
obj.restrictions.insert(std::make_pair(obj.restriction_counter++, r));
});
}).id;
} FC_CAPTURE_AND_RETHROW((op)) }

void_result custom_authority_update_evaluator::do_evaluate(const custom_authority_update_operation& op)
{ try {
const database& d = db();
auto now = d.head_block_time();
old_object = &op.authority_to_update(d);
FC_ASSERT(old_object->account == op.account, "Cannot update a different account's custom authority");

if (op.new_enabled)
FC_ASSERT(*op.new_enabled != old_object->enabled,
"Custom authority update specifies an enabled flag, but flag is not changed");

const auto& config = d.get_global_properties().parameters.extensions.value.custom_authority_options;
auto valid_from = old_object->valid_from;
auto valid_to = old_object->valid_to;
if (op.new_valid_from) {
FC_ASSERT(*op.new_valid_from != old_object->valid_from,
"Custom authority update specifies a new valid from date, but date is not changed");
valid_from = *op.new_valid_from;
}
if (op.new_valid_to) {
FC_ASSERT(*op.new_valid_to != old_object->valid_to,
"Custom authority update specifies a new valid to date, but date is not changed");
FC_ASSERT(*op.new_valid_to > now, "Custom authority expiration must be in the future");
FC_ASSERT((*op.new_valid_to - now).to_seconds() <= config->max_custom_authority_lifetime_seconds,
"Custom authority lifetime exceeds maximum limit");
valid_to = *op.new_valid_to;
}
FC_ASSERT(valid_from < valid_to, "Custom authority validity begin date must be before expiration date");

if (op.new_auth) {
FC_ASSERT(*op.new_auth != old_object->auth,
"Custom authority update specifies a new authentication authority, but authority is not changed");
for (const auto& account_weight_pair : op.new_auth->account_auths)
account_weight_pair.first(d);
}

std::for_each(op.restrictions_to_remove.begin(), op.restrictions_to_remove.end(), [this](uint16_t id) {
FC_ASSERT(old_object->restrictions.count(id) == 1, "Cannot remove restriction ID ${I}: ID not found",
("I", id));
});
if (!op.restrictions_to_add.empty()) {
// Sanity check
if (!old_object->restrictions.empty())
FC_ASSERT((--old_object->restrictions.end())->first < old_object->restriction_counter,
"LOGIC ERROR: Restriction counter overlaps restrictions. Please report this error.");
FC_ASSERT(old_object->restriction_counter + op.restrictions_to_add.size() > old_object->restriction_counter,
"Unable to add restrictions: causes wraparound of restriction IDs");
}

// Add up the restriction counts for all old restrictions not being removed, and all new ones
size_t restriction_count = 0;
for (const auto& restriction_pair : old_object->restrictions)
if (op.restrictions_to_remove.count(restriction_pair.first) == 0)
restriction_count += restriction_pair.second.restriction_count();
restriction_count += restriction::restriction_count(op.restrictions_to_add);
// Check restriction count against limit
FC_ASSERT(restriction_count <= config->max_custom_authority_restrictions,
"Cannot update custom authority: updated authority would exceed the maximum number of restrictions");

get_restriction_predicate(op.restrictions_to_add, old_object->operation_type);
return void_result();
} FC_CAPTURE_AND_RETHROW((op)) }

void_result custom_authority_update_evaluator::do_apply(const custom_authority_update_operation& op)
{ try {
database& d = db();

d.modify(*old_object, [&op](custom_authority_object& obj) {
if (op.new_enabled) obj.enabled = *op.new_enabled;
if (op.new_valid_from) obj.valid_from = *op.new_valid_from;
if (op.new_valid_to) obj.valid_to = *op.new_valid_to;
if (op.new_auth) obj.auth = *op.new_auth;

std::for_each(op.restrictions_to_remove.begin(), op.restrictions_to_remove.end(), [&obj](auto id) mutable {
obj.restrictions.erase(id);
});
std::for_each(op.restrictions_to_add.begin(), op.restrictions_to_add.end(), [&obj](const auto& r) mutable {
obj.restrictions.insert(std::make_pair(obj.restriction_counter++, r));
});

// Clear the predicate cache
obj.clear_predicate_cache();
});

return void_result();
} FC_CAPTURE_AND_RETHROW((op)) }

void_result custom_authority_delete_evaluator::do_evaluate(const custom_authority_delete_operation& op)
{ try {
const database& d = db();

old_object = &op.authority_to_delete(d);
FC_ASSERT(old_object->account == op.account, "Cannot delete a different account's custom authority");

return void_result();
} FC_CAPTURE_AND_RETHROW((op)) }

void_result custom_authority_delete_evaluator::do_apply(const custom_authority_delete_operation& op)
{ try {
database& d = db();

d.remove(*old_object);

return void_result();
} FC_CAPTURE_AND_RETHROW((op)) }

} } // graphene::chain
5 changes: 4 additions & 1 deletion libraries/chain/db_block.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -651,8 +651,11 @@ processed_transaction database::_apply_transaction(const signed_transaction& trx
bool allow_non_immediate_owner = ( head_block_time() >= HARDFORK_CORE_584_TIME );
auto get_active = [this]( account_id_type id ) { return &id(*this).active; };
auto get_owner = [this]( account_id_type id ) { return &id(*this).owner; };
auto get_custom = [this]( account_id_type id, const operation& op, rejected_predicate_map* rejects ) {
return get_viable_custom_authorities(id, op, rejects);
};

trx.verify_authority(chain_id, get_active, get_owner, allow_non_immediate_owner,
trx.verify_authority(chain_id, get_active, get_owner, get_custom, allow_non_immediate_owner,
MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(head_block_time()),
get_global_properties().parameters.max_authority_depth);
}
Expand Down
28 changes: 28 additions & 0 deletions libraries/chain/db_getter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/chain_property_object.hpp>
#include <graphene/chain/global_property_object.hpp>
#include <graphene/chain/custom_authority_object.hpp>

namespace graphene { namespace chain {

Expand Down Expand Up @@ -95,6 +96,33 @@ node_property_object& database::node_properties()
return _node_property_object;
}

vector<authority> database::get_viable_custom_authorities(
account_id_type account, const operation &op,
rejected_predicate_map* rejected_authorities) const
{
const auto& index = get_index_type<custom_authority_index>().indices().get<by_account_custom>();
auto range = index.equal_range(boost::make_tuple(account, unsigned_int(op.which()), true));

auto is_valid = [now=head_block_time()](const custom_authority_object& auth) { return auth.is_valid(now); };
vector<std::reference_wrapper<const custom_authority_object>> valid_auths;
std::copy_if(range.first, range.second, std::back_inserter(valid_auths), is_valid);

vector<authority> results;
for (const auto& cust_auth : valid_auths) {
try {
auto result = cust_auth.get().get_predicate()(op);
if (result.success)
results.emplace_back(cust_auth.get().auth);
else if (rejected_authorities != nullptr)
rejected_authorities->insert(std::make_pair(cust_auth.get().id, std::move(result)));
} catch (fc::exception& e) {
rejected_authorities->insert(std::make_pair(cust_auth.get().id, std::move(e)));
}
}

return results;
}

uint32_t database::last_non_undoable_block_num() const
{
//see https://github.com/bitshares/bitshares-core/issues/377
Expand Down
8 changes: 8 additions & 0 deletions libraries/chain/db_init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
#include <graphene/chain/witness_schedule_object.hpp>
#include <graphene/chain/worker_object.hpp>
#include <graphene/chain/htlc_object.hpp>
#include <graphene/chain/custom_authority_object.hpp>

#include <graphene/chain/account_evaluator.hpp>
#include <graphene/chain/asset_evaluator.hpp>
Expand All @@ -63,6 +64,7 @@
#include <graphene/chain/witness_evaluator.hpp>
#include <graphene/chain/worker_evaluator.hpp>
#include <graphene/chain/htlc_evaluator.hpp>
#include <graphene/chain/custom_authority_evaluator.hpp>

#include <fc/crypto/digest.hpp>

Expand Down Expand Up @@ -127,6 +129,8 @@ const uint8_t worker_object::type_id;
const uint8_t htlc_object::space_id;
const uint8_t htlc_object::type_id;

const uint8_t custom_authority_object::space_id;
const uint8_t custom_authority_object::type_id;

void database::initialize_evaluators()
{
Expand Down Expand Up @@ -178,6 +182,9 @@ void database::initialize_evaluators()
register_evaluator<htlc_create_evaluator>();
register_evaluator<htlc_redeem_evaluator>();
register_evaluator<htlc_extend_evaluator>();
register_evaluator<custom_authority_create_evaluator>();
register_evaluator<custom_authority_update_evaluator>();
register_evaluator<custom_authority_delete_evaluator>();
}

void database::initialize_indexes()
Expand Down Expand Up @@ -207,6 +214,7 @@ void database::initialize_indexes()
add_index< primary_index<balance_index> >();
add_index< primary_index<blinded_balance_index> >();
add_index< primary_index< htlc_index> >();
add_index< primary_index< custom_authority_index> >();

//Implementation object indexes
add_index< primary_index<transaction_index > >();
Expand Down
Loading

0 comments on commit f182b60

Please sign in to comment.