Skip to content

Commit 1a44ac2

Browse files
BSIP 40: Add evaluators, transaction eval code, testing
Implementation passes cursory tests.
1 parent 7c3bdb2 commit 1a44ac2

20 files changed

+527
-65
lines changed

libraries/app/database_api.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -2341,6 +2341,8 @@ bool database_api_impl::verify_authority( const signed_transaction& trx )const
23412341
trx.verify_authority( _db.get_chain_id(),
23422342
[this]( account_id_type id ){ return &id(_db).active; },
23432343
[this]( account_id_type id ){ return &id(_db).owner; },
2344+
[this]( account_id_type id, const operation& op ) {
2345+
return _db.get_viable_custom_authorities(id, op); },
23442346
allow_non_immediate_owner,
23452347
_db.get_global_properties().parameters.max_authority_depth );
23462348
return true;
@@ -2365,6 +2367,8 @@ bool database_api_impl::verify_account_authority( const string& account_name_or_
23652367
graphene::chain::verify_authority(ops, keys,
23662368
[this]( account_id_type id ){ return &id(_db).active; },
23672369
[this]( account_id_type id ){ return &id(_db).owner; },
2370+
// Use a no-op lookup for custom authorities; we don't want it even if one does apply for our dummy op
2371+
[](auto, auto) { return vector<authority>(); },
23682372
true );
23692373
}
23702374
catch (fc::exception& ex)

libraries/chain/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ add_library( graphene_chain
5555
htlc_evaluator.cpp
5656
confidential_evaluator.cpp
5757
special_authority_evaluation.cpp
58+
custom_authority_evaluator.cpp
5859
buyback.cpp
5960

6061
account_object.cpp
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/*
2+
* Copyright (c) 2019 Contributors.
3+
*
4+
* The MIT License
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
* THE SOFTWARE.
23+
*/
24+
25+
#include <graphene/chain/custom_authority_evaluator.hpp>
26+
#include <graphene/chain/custom_authority_object.hpp>
27+
#include <graphene/chain/account_object.hpp>
28+
#include <graphene/chain/database.hpp>
29+
#include <graphene/chain/exceptions.hpp>
30+
#include <graphene/chain/hardfork.hpp>
31+
32+
namespace graphene { namespace chain {
33+
34+
void_result custom_authority_create_evaluator::do_evaluate(const custom_authority_create_operation& op)
35+
{ try {
36+
const database& d = db();
37+
auto now = d.head_block_time();
38+
FC_ASSERT(HARDFORK_BSIP_40_PASSED(now), "Custom active authorities are not yet enabled");
39+
40+
op.account(d);
41+
42+
const auto& config = global_property_id_type()(d).parameters.extensions.value.custom_authority_options;
43+
FC_ASSERT(config.valid(), "Cannot use custom authorities yet: global configuration not set");
44+
FC_ASSERT(op.valid_to > now, "Custom authority expiration must be in the future");
45+
FC_ASSERT((op.valid_to - now).to_seconds() <= config->max_custom_authority_lifetime_seconds,
46+
"Custom authority lifetime exceeds maximum limit");
47+
48+
FC_ASSERT(op.operation_type.value <= config->max_operation_tag,
49+
"Cannot create custom authority for operation type which is not yet active");
50+
51+
for (const auto& account_weight_pair : op.auth.account_auths)
52+
account_weight_pair.first(d);
53+
54+
const auto& index = d.get_index_type<custom_authority_index>().indices().get<by_account_custom>();
55+
auto range = index.equal_range(op.account);
56+
FC_ASSERT(std::distance(range.first, range.second) < config->max_custom_authorities_per_account,
57+
"Cannot create custom authority for account: account already has maximum number");
58+
59+
predicate = get_restriction_predicate(op.restrictions, op.operation_type);
60+
return void_result();
61+
} FC_CAPTURE_AND_RETHROW((op)) }
62+
63+
object_id_type custom_authority_create_evaluator::do_apply(const custom_authority_create_operation& op)
64+
{ try {
65+
database& d = db();
66+
67+
return d.create<custom_authority_object>([&op, p=std::move(predicate)] (custom_authority_object& obj) mutable {
68+
obj.account = op.account;
69+
obj.enabled = op.enabled;
70+
obj.valid_from = op.valid_from;
71+
obj.valid_to = op.valid_to;
72+
obj.operation_type = op.operation_type;
73+
obj.auth = op.auth;
74+
obj.restrictions = op.restrictions;
75+
76+
obj.predicate_cache = std::move(p);
77+
}).id;
78+
} FC_CAPTURE_AND_RETHROW((op)) }
79+
80+
void_result custom_authority_update_evaluator::do_evaluate(const custom_authority_update_operation& op)
81+
{ try {
82+
const database& d = db();
83+
auto now = d.head_block_time();
84+
FC_ASSERT(HARDFORK_BSIP_40_PASSED(now), "Custom active authorities are not yet enabled");
85+
const auto& old_object = op.authority_to_update(d);
86+
87+
op.account(d);
88+
if (op.new_enabled)
89+
FC_ASSERT(*op.new_enabled != old_object.enabled,
90+
"Custom authority update specifies an enabled flag, but flag is not changed");
91+
92+
const auto& config = global_property_id_type()(d).parameters.extensions.value.custom_authority_options;
93+
if (op.new_valid_from)
94+
FC_ASSERT(*op.new_valid_from != old_object.valid_from,
95+
"Custom authority update specifies a new valid from date, but date is not changed");
96+
if (op.new_valid_to) {
97+
FC_ASSERT(*op.new_valid_to != old_object.valid_to,
98+
"Custom authority update specifies a new valid to date, but date is not changed");
99+
FC_ASSERT(*op.new_valid_to > now, "Custom authority expiration must be in the future");
100+
FC_ASSERT((*op.new_valid_to - now).to_seconds() <= config->max_custom_authority_lifetime_seconds,
101+
"Custom authority lifetime exceeds maximum limit");
102+
}
103+
104+
if (op.new_auth) {
105+
FC_ASSERT(*op.new_auth != old_object.auth,
106+
"Custom authority update specifies a new authentication authority, but authority is not changed");
107+
for (const auto& account_weight_pair : op.new_auth->account_auths)
108+
account_weight_pair.first(d);
109+
}
110+
111+
auto largest_index = *(--op.restrictions_to_remove.end());
112+
FC_ASSERT(largest_index < old_object.restrictions.size(),
113+
"Index of custom authority restriction to remove is out of bounds");
114+
115+
predicate = get_restriction_predicate(op.restrictions_to_add, old_object.operation_type);
116+
return void_result();
117+
} FC_CAPTURE_AND_RETHROW((op)) }
118+
119+
void_result custom_authority_update_evaluator::do_apply(const custom_authority_update_operation& op)
120+
{ try {
121+
database& d = db();
122+
123+
d.modify(op.authority_to_update(d), [&op, p=std::move(predicate)](custom_authority_object& obj) {
124+
if (op.new_enabled) obj.enabled = *op.new_enabled;
125+
if (op.new_valid_from) obj.valid_from = *op.new_valid_from;
126+
if (op.new_valid_to) obj.valid_to = *op.new_valid_to;
127+
if (op.new_auth) obj.auth = *op.new_auth;
128+
129+
// Move restrictions at indexes to be removed to the end, then truncate them.
130+
// Note: we could use partition instead of stable_partition, which would be slightly faster, but would also
131+
// reorder the restrictions. I opted to preserve order as a courtesy to the user, who obviously does care about
132+
// what items are at what indexes (removed items are specified by index)
133+
std::stable_partition(obj.restrictions.begin(), obj.restrictions.end(), [&op, index=0](const auto&) mutable {
134+
return op.restrictions_to_remove.count(index++) == 0;
135+
});
136+
obj.restrictions.resize(obj.restrictions.size() - op.restrictions_to_remove.size());
137+
138+
obj.restrictions.insert(obj.restrictions.end(), op.restrictions_to_add.begin(), op.restrictions_to_add.end());
139+
140+
obj.predicate_cache = std::move(p);
141+
});
142+
143+
return void_result();
144+
} FC_CAPTURE_AND_RETHROW((op)) }
145+
146+
void_result custom_authority_delete_evaluator::do_evaluate(const custom_authority_delete_operation& op)
147+
{ try {
148+
const database& d = db();
149+
FC_ASSERT(HARDFORK_BSIP_40_PASSED(d.head_block_time()), "Custom active authorities are not yet enabled");
150+
151+
op.account(d);
152+
op.authority_to_delete(d);
153+
154+
return void_result();
155+
} FC_CAPTURE_AND_RETHROW((op)) }
156+
157+
void_result custom_authority_delete_evaluator::do_apply(const custom_authority_delete_operation& op)
158+
{ try {
159+
database& d = db();
160+
161+
d.remove(op.authority_to_delete(d));
162+
163+
return void_result();
164+
} FC_CAPTURE_AND_RETHROW((op)) }
165+
166+
} } // graphene::chain

libraries/chain/db_block.cpp

+6-2
Original file line numberDiff line numberDiff line change
@@ -644,11 +644,15 @@ processed_transaction database::_apply_transaction(const signed_transaction& trx
644644
if( !(skip & skip_transaction_signatures) )
645645
{
646646
bool allow_non_immediate_owner = ( head_block_time() >= HARDFORK_CORE_584_TIME );
647-
auto get_active = [&]( account_id_type id ) { return &id(*this).active; };
648-
auto get_owner = [&]( account_id_type id ) { return &id(*this).owner; };
647+
auto get_active = [this]( account_id_type id ) { return &id(*this).active; };
648+
auto get_owner = [this]( account_id_type id ) { return &id(*this).owner; };
649+
auto get_custom = [this]( account_id_type id, const operation& op ) {
650+
return get_viable_custom_authorities(id, op);
651+
};
649652
trx.verify_authority( chain_id,
650653
get_active,
651654
get_owner,
655+
get_custom,
652656
allow_non_immediate_owner,
653657
get_global_properties().parameters.max_authority_depth );
654658
}

libraries/chain/db_getter.cpp

+16
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <graphene/chain/asset_object.hpp>
2828
#include <graphene/chain/chain_property_object.hpp>
2929
#include <graphene/chain/global_property_object.hpp>
30+
#include <graphene/chain/custom_authority_object.hpp>
3031

3132
namespace graphene { namespace chain {
3233

@@ -95,6 +96,21 @@ node_property_object& database::node_properties()
9596
return _node_property_object;
9697
}
9798

99+
vector<authority> database::get_viable_custom_authorities(account_id_type account, const operation &op) const
100+
{
101+
const auto& index = get_index_type<custom_authority_index>().indices().get<by_account_custom>();
102+
auto range = index.equal_range(boost::make_tuple(account, unsigned_int(op.which()), true));
103+
vector<authority> results;
104+
105+
std::for_each(range.first, range.second,
106+
[&results, &op, now=head_block_time()](const custom_authority_object& cust_auth) {
107+
if (cust_auth.is_valid(now) && cust_auth.get_predicate()(op))
108+
results.insert(results.end(), cust_auth.auth);
109+
});
110+
111+
return results;
112+
}
113+
98114
uint32_t database::last_non_undoable_block_num() const
99115
{
100116
//see https://github.com/bitshares/bitshares-core/issues/377

libraries/chain/db_init.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
#include <graphene/chain/witness_evaluator.hpp>
6565
#include <graphene/chain/worker_evaluator.hpp>
6666
#include <graphene/chain/htlc_evaluator.hpp>
67+
#include <graphene/chain/custom_authority_evaluator.hpp>
6768

6869
#include <fc/uint128.hpp>
6970
#include <fc/crypto/digest.hpp>
@@ -182,6 +183,9 @@ void database::initialize_evaluators()
182183
register_evaluator<htlc_create_evaluator>();
183184
register_evaluator<htlc_redeem_evaluator>();
184185
register_evaluator<htlc_extend_evaluator>();
186+
register_evaluator<custom_authority_create_evaluator>();
187+
register_evaluator<custom_authority_update_evaluator>();
188+
register_evaluator<custom_authority_delete_evaluator>();
185189
}
186190

187191
void database::initialize_indexes()

libraries/chain/hardfork.d/BSIP_40.hf

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// BSIP 40 (Custom Active Authorities) hardfork check
2+
#ifndef HARDFORK_BSIP_40_TIME
3+
// Jan 1 2030, midnight; this is a dummy date until a hardfork date is scheduled
4+
#define HARDFORK_BSIP_40_TIME (fc::time_point_sec( 1893456000 ))
5+
#define HARDFORK_BSIP_40_PASSED(now) (now >= HARDFORK_BSIP_40_TIME)
6+
#endif
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright (c) 2019 Contributors.
3+
*
4+
* The MIT License
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
* THE SOFTWARE.
23+
*/
24+
#pragma once
25+
26+
#include <graphene/protocol/restriction_predicate.hpp>
27+
28+
#include <graphene/chain/evaluator.hpp>
29+
30+
namespace graphene { namespace chain {
31+
32+
class custom_authority_create_evaluator : public evaluator<custom_authority_create_evaluator> {
33+
public:
34+
using operation_type = custom_authority_create_operation;
35+
restriction_predicate_function predicate;
36+
37+
void_result do_evaluate(const operation_type& op);
38+
object_id_type do_apply(const operation_type& op);
39+
};
40+
41+
class custom_authority_update_evaluator : public evaluator<custom_authority_update_evaluator> {
42+
public:
43+
using operation_type = custom_authority_update_operation;
44+
restriction_predicate_function predicate;
45+
46+
void_result do_evaluate(const operation_type& op);
47+
void_result do_apply(const operation_type& op);
48+
};
49+
50+
class custom_authority_delete_evaluator : public evaluator<custom_authority_delete_evaluator> {
51+
public:
52+
using operation_type = custom_authority_delete_operation;
53+
54+
void_result do_evaluate(const operation_type& op);
55+
void_result do_apply(const operation_type& op);
56+
};
57+
58+
} } // graphene::chain

libraries/chain/include/graphene/chain/custom_authority_object.hpp

+12-1
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,17 @@ namespace graphene { namespace chain {
4949
authority auth;
5050
vector<restriction> restrictions;
5151

52+
/// Check whether the custom authority is valid
53+
bool is_valid(time_point_sec now) const { return enabled && now >= valid_from && now < valid_to; }
54+
5255
/// Unreflected field to store a cache of the predicate function
53-
optional<restriction_predicate_function> predicate_cache;
56+
mutable optional<restriction_predicate_function> predicate_cache;
57+
58+
/// Get predicate, from cache if possible, and update cache if not (modifies const object!)
59+
restriction_predicate_function get_predicate() const {
60+
if (!predicate_cache.valid()) predicate_cache = get_restriction_predicate(restrictions, operation_type);
61+
return *predicate_cache;
62+
}
5463
};
5564

5665
struct by_account_custom;
@@ -66,6 +75,8 @@ namespace graphene { namespace chain {
6675
composite_key<
6776
custom_authority_object,
6877
member<custom_authority_object, account_id_type, &custom_authority_object::account>,
78+
member<custom_authority_object, unsigned_int, &custom_authority_object::operation_type>,
79+
member<custom_authority_object, bool, &custom_authority_object::enabled>,
6980
member<object, object_id_type, &object::id>
7081
>
7182
>

libraries/chain/include/graphene/chain/database.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ namespace graphene { namespace chain {
279279

280280
node_property_object& node_properties();
281281

282+
vector<authority> get_viable_custom_authorities( account_id_type account, const operation& op )const;
282283

283284
uint32_t last_non_undoable_block_num() const;
284285
//////////////////// db_init.cpp ////////////////////

libraries/chain/proposal_object.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ bool proposal_object::is_authorized_to_execute(database& db) const
3838
available_key_approvals,
3939
[&]( account_id_type id ){ return &id(db).active; },
4040
[&]( account_id_type id ){ return &id(db).owner; },
41+
[&]( account_id_type id, const operation& op ){
42+
return db.get_viable_custom_authorities(id, op); },
4143
allow_non_immediate_owner,
4244
db.get_global_properties().parameters.max_authority_depth,
4345
true, /* allow committee */

libraries/protocol/include/graphene/protocol/authority.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ namespace graphene { namespace protocol {
107107
(a.key_auths == b.key_auths) &&
108108
(a.address_auths == b.address_auths);
109109
}
110+
friend bool operator!= ( const authority& a, const authority& b ) { return !(a==b); }
110111
uint32_t num_auths()const { return account_auths.size() + key_auths.size() + address_auths.size(); }
111112
void clear() { account_auths.clear(); key_auths.clear(); address_auths.clear(); weight_threshold = 0; }
112113

0 commit comments

Comments
 (0)