Merge branch 'feature/SONs-base' into son_165
This commit is contained in:
commit
03e0b19427
91 changed files with 8450 additions and 640 deletions
127
.clang-format
Executable file
127
.clang-format
Executable file
|
|
@ -0,0 +1,127 @@
|
|||
---
|
||||
Language: Cpp
|
||||
# BasedOnStyle: LLVM
|
||||
AccessModifierOffset: -3
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveMacros: false
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
AlignEscapedNewlines: Right
|
||||
AlignOperands: true
|
||||
AlignTrailingComments: true
|
||||
AllowAllArgumentsOnNextLine: true
|
||||
AllowAllConstructorInitializersOnNextLine: false
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: None
|
||||
AllowShortLambdasOnASingleLine: None
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: MultiLine
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: false
|
||||
AfterClass: false
|
||||
AfterControlStatement: false
|
||||
AfterEnum: false
|
||||
AfterFunction: false
|
||||
AfterNamespace: false
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
AfterExternBlock: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: true
|
||||
SplitEmptyRecord: true
|
||||
SplitEmptyNamespace: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBraces: Attach
|
||||
BreakBeforeInheritanceComma: false
|
||||
BreakInheritanceList: BeforeColon
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
BreakConstructorInitializers: AfterColon
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakStringLiterals: false
|
||||
ColumnLimit: 0
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
CompactNamespaces: true
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
ConstructorInitializerIndentWidth: 6
|
||||
ContinuationIndentWidth: 6
|
||||
Cpp11BracedListStyle: true
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
FixNamespaceComments: true
|
||||
ForEachMacros:
|
||||
- foreach
|
||||
- Q_FOREACH
|
||||
- BOOST_FOREACH
|
||||
IncludeBlocks: Preserve
|
||||
IncludeCategories:
|
||||
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
|
||||
Priority: 2
|
||||
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
|
||||
Priority: 3
|
||||
- Regex: '.*'
|
||||
Priority: 1
|
||||
IncludeIsMainRegex: '(Test)?$'
|
||||
IndentCaseLabels: false
|
||||
IndentPPDirectives: None
|
||||
IndentWidth: 3
|
||||
IndentWrappedFunctionNames: false
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
ObjCBinPackProtocolList: Auto
|
||||
ObjCBlockIndentWidth: 2
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PenaltyBreakAssignment: 2
|
||||
PenaltyBreakBeforeFirstCallParameter: 19
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyBreakTemplateDeclaration: 10
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 60
|
||||
PointerAlignment: Right
|
||||
ReflowComments: true
|
||||
SortIncludes: true
|
||||
SortUsingDeclarations: true
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterLogicalNot: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCpp11BracedList: false
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: false
|
||||
SpacesInContainerLiterals: true
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
Standard: Cpp11
|
||||
StatementMacros:
|
||||
- Q_UNUSED
|
||||
- QT_REQUIRE_VERSION
|
||||
TabWidth: 3
|
||||
UseTab: Never
|
||||
...
|
||||
|
||||
|
|
@ -8,6 +8,7 @@ stages:
|
|||
build:
|
||||
stage: build
|
||||
script:
|
||||
- git submodule sync
|
||||
- git submodule update --init --recursive
|
||||
- cmake .
|
||||
- make -j$(nproc)
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ else( WIN32 ) # Apple AND Linux
|
|||
endif( APPLE )
|
||||
|
||||
if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" )
|
||||
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-builtin-memcmp -Wno-parentheses -Wno-invalid-offsetof" )
|
||||
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-builtin-memcmp -Wno-parentheses -Wno-invalid-offsetof -Wno-terminate -Wno-sign-compare" )
|
||||
elseif( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" )
|
||||
if( CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 4.0.0 OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.0.0 )
|
||||
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-invalid-partial-specialization" )
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ RUN \
|
|||
libreadline-dev \
|
||||
libssl-dev \
|
||||
libtool \
|
||||
libzmq3-dev \
|
||||
locales \
|
||||
ntp \
|
||||
pkg-config \
|
||||
|
|
|
|||
|
|
@ -7,9 +7,10 @@ This is a quick introduction to get new developers and witnesses up to speed on
|
|||
The following dependencies were necessary for a clean install of Ubuntu 18.04 LTS:
|
||||
|
||||
```
|
||||
sudo apt-get install gcc-5 g++-5 cmake make libbz2-dev\
|
||||
libdb++-dev libdb-dev libssl-dev openssl libreadline-dev\
|
||||
autoconf libtool git
|
||||
sudo apt-get install autoconf bash build-essential ca-certificates cmake \
|
||||
doxygen git graphviz libbz2-dev libcurl4-openssl-dev libncurses-dev \
|
||||
libreadline-dev libssl-dev libtool libzmq3-dev locales ntp pkg-config \
|
||||
wget
|
||||
```
|
||||
## Build Boost 1.67.0
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ add_library( graphene_app
|
|||
|
||||
# need to link graphene_debug_witness because plugins aren't sufficiently isolated #246
|
||||
#target_link_libraries( graphene_app graphene_market_history graphene_account_history graphene_chain fc graphene_db graphene_net graphene_utilities graphene_debug_witness )
|
||||
target_link_libraries( graphene_app graphene_market_history graphene_account_history graphene_accounts_list graphene_affiliate_stats graphene_chain fc graphene_db graphene_net graphene_time graphene_utilities graphene_debug_witness graphene_bookie )
|
||||
target_link_libraries( graphene_app graphene_market_history graphene_account_history graphene_accounts_list graphene_affiliate_stats graphene_chain fc graphene_db graphene_net graphene_time graphene_utilities graphene_debug_witness graphene_bookie peerplays_sidechain )
|
||||
target_include_directories( graphene_app
|
||||
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../egenesis/include" )
|
||||
|
|
|
|||
|
|
@ -443,6 +443,11 @@ namespace graphene { namespace app {
|
|||
assert( aobj != nullptr );
|
||||
accounts.insert( aobj->son_account );
|
||||
break;
|
||||
} case sidechain_address_object_type:{
|
||||
const auto& aobj = dynamic_cast<const sidechain_address_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
accounts.insert( aobj->sidechain_address_account );
|
||||
break;
|
||||
}
|
||||
case sport_object_type:
|
||||
case event_group_object_type:
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
|
|||
uint64_t get_asset_count()const;
|
||||
|
||||
// Peerplays
|
||||
vector<sport_object> list_sports() const;
|
||||
vector<sport_object> list_sports() const;
|
||||
vector<event_group_object> list_event_groups(sport_id_type sport_id) const;
|
||||
vector<event_object> list_events_in_group(event_group_id_type event_group_id) const;
|
||||
vector<betting_market_group_object> list_betting_market_groups(event_id_type) const;
|
||||
|
|
@ -115,14 +115,14 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
|
|||
vector<asset_object> get_lotteries( asset_id_type stop = asset_id_type(),
|
||||
unsigned limit = 100,
|
||||
asset_id_type start = asset_id_type() )const;
|
||||
vector<asset_object> get_account_lotteries( account_id_type issuer,
|
||||
vector<asset_object> get_account_lotteries( account_id_type issuer,
|
||||
asset_id_type stop,
|
||||
unsigned limit,
|
||||
asset_id_type start )const;
|
||||
asset get_lottery_balance( asset_id_type lottery_id )const;
|
||||
sweeps_vesting_balance_object get_sweeps_vesting_balance_object( account_id_type account )const;
|
||||
asset get_sweeps_vesting_balance_available_for_claim( account_id_type account )const;
|
||||
|
||||
|
||||
// Markets / feeds
|
||||
vector<limit_order_object> get_limit_orders(asset_id_type a, asset_id_type b, uint32_t limit)const;
|
||||
vector<call_order_object> get_call_orders(asset_id_type a, uint32_t limit)const;
|
||||
|
|
@ -152,6 +152,18 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
|
|||
map<string, son_id_type> lookup_son_accounts(const string& lower_bound_name, uint32_t limit)const;
|
||||
uint64_t get_son_count()const;
|
||||
|
||||
// SON wallets
|
||||
optional<son_wallet_object> get_active_son_wallet();
|
||||
optional<son_wallet_object> get_son_wallet_by_time_point(time_point_sec time_point);
|
||||
vector<optional<son_wallet_object>> get_son_wallets(uint32_t limit);
|
||||
|
||||
// Sidechain addresses
|
||||
vector<optional<sidechain_address_object>> get_sidechain_addresses(const vector<sidechain_address_id_type>& sidechain_address_ids)const;
|
||||
vector<optional<sidechain_address_object>> get_sidechain_addresses_by_account(account_id_type account)const;
|
||||
vector<optional<sidechain_address_object>> get_sidechain_addresses_by_sidechain(peerplays_sidechain::sidechain_type sidechain)const;
|
||||
fc::optional<sidechain_address_object> get_sidechain_address_by_account_and_sidechain(account_id_type account, peerplays_sidechain::sidechain_type sidechain)const;
|
||||
uint64_t get_sidechain_addresses_count()const;
|
||||
|
||||
// Votes
|
||||
vector<variant> lookup_vote_ids( const vector<vote_id_type>& votes )const;
|
||||
|
||||
|
|
@ -528,11 +540,11 @@ vector<vector<account_id_type>> database_api::get_key_references( vector<public_
|
|||
vector<vector<account_id_type>> database_api_impl::get_key_references( vector<public_key_type> keys )const
|
||||
{
|
||||
wdump( (keys) );
|
||||
|
||||
|
||||
const auto& idx = _db.get_index_type<account_index>();
|
||||
const auto& aidx = dynamic_cast<const base_primary_index&>(idx);
|
||||
const auto& refs = aidx.get_secondary_index<graphene::chain::account_member_index>();
|
||||
|
||||
|
||||
vector< vector<account_id_type> > final_result;
|
||||
final_result.reserve(keys.size());
|
||||
|
||||
|
|
@ -648,7 +660,7 @@ std::map<std::string, full_account> database_api_impl::get_full_accounts( const
|
|||
const auto& proposal_idx = _db.get_index_type<proposal_index>();
|
||||
const auto& pidx = dynamic_cast<const base_primary_index&>(proposal_idx);
|
||||
const auto& proposals_by_account = pidx.get_secondary_index<graphene::chain::required_approval_index>();
|
||||
|
||||
|
||||
std::map<std::string, full_account> results;
|
||||
|
||||
for (const std::string& account_name_or_id : names_or_ids)
|
||||
|
|
@ -738,7 +750,7 @@ std::map<std::string, full_account> database_api_impl::get_full_accounts( const
|
|||
acnt.withdraws.emplace_back(withdraw);
|
||||
});
|
||||
|
||||
auto pending_payouts_range =
|
||||
auto pending_payouts_range =
|
||||
_db.get_index_type<pending_dividend_payout_balance_for_holder_object_index>().indices().get<by_account_dividend_payout>().equal_range(boost::make_tuple(account->id));
|
||||
|
||||
std::copy(pending_payouts_range.first, pending_payouts_range.second, std::back_inserter(acnt.pending_dividend_payments));
|
||||
|
|
@ -1058,7 +1070,7 @@ vector<asset_object> database_api_impl::get_lotteries( asset_id_type stop,
|
|||
|
||||
return result;
|
||||
}
|
||||
vector<asset_object> database_api::get_account_lotteries( account_id_type issuer,
|
||||
vector<asset_object> database_api::get_account_lotteries( account_id_type issuer,
|
||||
asset_id_type stop,
|
||||
unsigned limit,
|
||||
asset_id_type start )const
|
||||
|
|
@ -1066,7 +1078,7 @@ vector<asset_object> database_api::get_account_lotteries( account_id_type issuer
|
|||
return my->get_account_lotteries( issuer, stop, limit, start );
|
||||
}
|
||||
|
||||
vector<asset_object> database_api_impl::get_account_lotteries( account_id_type issuer,
|
||||
vector<asset_object> database_api_impl::get_account_lotteries( account_id_type issuer,
|
||||
asset_id_type stop,
|
||||
unsigned limit,
|
||||
asset_id_type start )const
|
||||
|
|
@ -1763,6 +1775,136 @@ uint64_t database_api_impl::get_son_count()const
|
|||
return _db.get_index_type<son_index>().indices().size();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// SON Wallets //
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
optional<son_wallet_object> database_api::get_active_son_wallet()
|
||||
{
|
||||
return my->get_active_son_wallet();
|
||||
}
|
||||
|
||||
optional<son_wallet_object> database_api_impl::get_active_son_wallet()
|
||||
{
|
||||
const auto& idx = _db.get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
auto obj = idx.rbegin();
|
||||
if (obj != idx.rend()) {
|
||||
return *obj;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<son_wallet_object> database_api::get_son_wallet_by_time_point(time_point_sec time_point)
|
||||
{
|
||||
return my->get_son_wallet_by_time_point(time_point);
|
||||
}
|
||||
|
||||
optional<son_wallet_object> database_api_impl::get_son_wallet_by_time_point(time_point_sec time_point)
|
||||
{
|
||||
const auto& son_wallets_by_id = _db.get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
for (const son_wallet_object& swo : son_wallets_by_id) {
|
||||
if ((time_point >= swo.valid_from) && (time_point < swo.expires))
|
||||
return swo;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
vector<optional<son_wallet_object>> database_api::get_son_wallets(uint32_t limit)
|
||||
{
|
||||
return my->get_son_wallets(limit);
|
||||
}
|
||||
|
||||
vector<optional<son_wallet_object>> database_api_impl::get_son_wallets(uint32_t limit)
|
||||
{
|
||||
FC_ASSERT( limit <= 1000 );
|
||||
vector<optional<son_wallet_object>> result;
|
||||
const auto& son_wallets_by_id = _db.get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
for (const son_wallet_object& swo : son_wallets_by_id)
|
||||
result.push_back(swo);
|
||||
return result;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// Sidechain Accounts //
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
vector<optional<sidechain_address_object>> database_api::get_sidechain_addresses(const vector<sidechain_address_id_type>& sidechain_address_ids)const
|
||||
{
|
||||
return my->get_sidechain_addresses( sidechain_address_ids );
|
||||
}
|
||||
|
||||
vector<optional<sidechain_address_object>> database_api_impl::get_sidechain_addresses(const vector<sidechain_address_id_type>& sidechain_address_ids)const
|
||||
{
|
||||
vector<optional<sidechain_address_object>> result; result.reserve(sidechain_address_ids.size());
|
||||
std::transform(sidechain_address_ids.begin(), sidechain_address_ids.end(), std::back_inserter(result),
|
||||
[this](sidechain_address_id_type id) -> optional<sidechain_address_object> {
|
||||
if(auto o = _db.find(id))
|
||||
return *o;
|
||||
return {};
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
vector<optional<sidechain_address_object>> database_api::get_sidechain_addresses_by_account(account_id_type account)const
|
||||
{
|
||||
return my->get_sidechain_addresses_by_account( account );
|
||||
}
|
||||
|
||||
vector<optional<sidechain_address_object>> database_api_impl::get_sidechain_addresses_by_account(account_id_type account)const
|
||||
{
|
||||
vector<optional<sidechain_address_object>> result;
|
||||
const auto& sidechain_addresses_range = _db.get_index_type<sidechain_address_index>().indices().get<by_account>().equal_range(account);
|
||||
std::for_each(sidechain_addresses_range.first, sidechain_addresses_range.second,
|
||||
[&result] (const sidechain_address_object& sao) {
|
||||
result.push_back(sao);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
vector<optional<sidechain_address_object>> database_api::get_sidechain_addresses_by_sidechain(peerplays_sidechain::sidechain_type sidechain)const
|
||||
{
|
||||
return my->get_sidechain_addresses_by_sidechain( sidechain );
|
||||
}
|
||||
|
||||
vector<optional<sidechain_address_object>> database_api_impl::get_sidechain_addresses_by_sidechain(peerplays_sidechain::sidechain_type sidechain)const
|
||||
{
|
||||
vector<optional<sidechain_address_object>> result;
|
||||
const auto& sidechain_addresses_range = _db.get_index_type<sidechain_address_index>().indices().get<by_sidechain>().equal_range(sidechain);
|
||||
std::for_each(sidechain_addresses_range.first, sidechain_addresses_range.second,
|
||||
[&result] (const sidechain_address_object& sao) {
|
||||
result.push_back(sao);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
fc::optional<sidechain_address_object> database_api::get_sidechain_address_by_account_and_sidechain(account_id_type account, peerplays_sidechain::sidechain_type sidechain)const
|
||||
{
|
||||
return my->get_sidechain_address_by_account_and_sidechain( account, sidechain );
|
||||
}
|
||||
|
||||
fc::optional<sidechain_address_object> database_api_impl::get_sidechain_address_by_account_and_sidechain(account_id_type account, peerplays_sidechain::sidechain_type sidechain)const
|
||||
{
|
||||
const auto& idx = _db.get_index_type<sidechain_address_index>().indices().get<by_account_and_sidechain>();
|
||||
auto itr = idx.find( boost::make_tuple( account, sidechain ) );
|
||||
if( itr != idx.end() )
|
||||
return *itr;
|
||||
return {};
|
||||
}
|
||||
|
||||
uint64_t database_api::get_sidechain_addresses_count()const
|
||||
{
|
||||
return my->get_sidechain_addresses_count();
|
||||
}
|
||||
|
||||
uint64_t database_api_impl::get_sidechain_addresses_count()const
|
||||
{
|
||||
return _db.get_index_type<sidechain_address_index>().indices().size();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// Votes //
|
||||
|
|
@ -2164,7 +2306,7 @@ vector<tournament_object> database_api::get_tournaments(tournament_id_type stop,
|
|||
|
||||
vector<tournament_object> database_api_impl::get_tournaments(tournament_id_type stop,
|
||||
unsigned limit,
|
||||
tournament_id_type start)
|
||||
tournament_id_type start)
|
||||
{
|
||||
vector<tournament_object> result;
|
||||
const auto& tournament_idx = _db.get_index_type<tournament_index>().indices().get<by_id>();
|
||||
|
|
@ -2191,7 +2333,7 @@ vector<tournament_object> database_api_impl::get_tournaments_by_state(tournament
|
|||
unsigned limit,
|
||||
tournament_id_type start,
|
||||
tournament_state state)
|
||||
{
|
||||
{
|
||||
vector<tournament_object> result;
|
||||
const auto& tournament_idx = _db.get_index_type<tournament_index>().indices().get<by_id>();
|
||||
for (auto elem: tournament_idx) {
|
||||
|
|
@ -2320,7 +2462,7 @@ void database_api_impl::handle_object_changed(bool force_notify, bool full_objec
|
|||
/// if a connection hangs then this could get backed up and result in
|
||||
/// a failure to exit cleanly.
|
||||
//fc::async([capture_this,this,updates,market_broadcast_queue](){
|
||||
//if( _subscribe_callback )
|
||||
//if( _subscribe_callback )
|
||||
// _subscribe_callback( updates );
|
||||
|
||||
for(auto id : ids)
|
||||
|
|
|
|||
|
|
@ -307,6 +307,51 @@ struct get_impacted_account_visitor
|
|||
void operator()( const son_delete_operation& op ){
|
||||
_impacted.insert( op.owner_account );
|
||||
}
|
||||
void operator()( const son_heartbeat_operation& op ){
|
||||
_impacted.insert( op.owner_account );
|
||||
}
|
||||
void operator()( const son_report_down_operation& op ){
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const son_maintenance_operation& op ){
|
||||
_impacted.insert( op.owner_account );
|
||||
}
|
||||
void operator()( const son_wallet_recreate_operation& op ){
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const son_wallet_update_operation& op ){
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const son_wallet_deposit_create_operation& op ){
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const son_wallet_deposit_process_operation& op ){
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const son_wallet_withdraw_create_operation& op ){
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const son_wallet_withdraw_process_operation& op ){
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const sidechain_address_add_operation& op ){
|
||||
_impacted.insert( op.sidechain_address_account );
|
||||
}
|
||||
void operator()( const sidechain_address_update_operation& op ){
|
||||
_impacted.insert( op.sidechain_address_account );
|
||||
}
|
||||
void operator()( const sidechain_address_delete_operation& op ){
|
||||
_impacted.insert( op.sidechain_address_account );
|
||||
}
|
||||
void operator()( const sidechain_transaction_create_operation& op ){
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const sidechain_transaction_sign_operation& op ){
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const sidechain_transaction_send_operation& op ){
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
};
|
||||
|
||||
void operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result )
|
||||
|
|
|
|||
|
|
@ -44,6 +44,8 @@
|
|||
#include <graphene/chain/betting_market_object.hpp>
|
||||
#include <graphene/chain/global_betting_statistics_object.hpp>
|
||||
#include <graphene/chain/son_object.hpp>
|
||||
#include <graphene/chain/son_wallet_object.hpp>
|
||||
#include <graphene/chain/sidechain_address_object.hpp>
|
||||
|
||||
#include <graphene/chain/worker_object.hpp>
|
||||
#include <graphene/chain/witness_object.hpp>
|
||||
|
|
@ -602,6 +604,70 @@ class database_api
|
|||
*/
|
||||
uint64_t get_son_count()const;
|
||||
|
||||
/////////////////////////
|
||||
// SON Wallets //
|
||||
/////////////////////////
|
||||
|
||||
/**
|
||||
* @brief Get active SON wallet
|
||||
* @return Active SON wallet object
|
||||
*/
|
||||
optional<son_wallet_object> get_active_son_wallet();
|
||||
|
||||
/**
|
||||
* @brief Get SON wallet that was active for a given time point
|
||||
* @param time_point Time point
|
||||
* @return SON wallet object, for the wallet that was active for a given time point
|
||||
*/
|
||||
optional<son_wallet_object> get_son_wallet_by_time_point(time_point_sec time_point);
|
||||
|
||||
/**
|
||||
* @brief Get full list of SON wallets
|
||||
* @param limit Maximum number of results to return
|
||||
* @return A list of SON wallet objects
|
||||
*/
|
||||
vector<optional<son_wallet_object>> get_son_wallets(uint32_t limit);
|
||||
|
||||
/////////////////////////
|
||||
// Sidechain Addresses //
|
||||
/////////////////////////
|
||||
|
||||
/**
|
||||
* @brief Get a list of sidechain addresses
|
||||
* @param sidechain_address_ids IDs of the sidechain addresses to retrieve
|
||||
* @return The sidechain accounts corresponding to the provided IDs
|
||||
*
|
||||
* This function has semantics identical to @ref get_objects
|
||||
*/
|
||||
vector<optional<sidechain_address_object>> get_sidechain_addresses(const vector<sidechain_address_id_type>& sidechain_address_ids)const;
|
||||
|
||||
/**
|
||||
* @brief Get the sidechain addresses for a given account
|
||||
* @param account The ID of the account whose sidechain addresses should be retrieved
|
||||
* @return The sidechain addresses objects, or null if the account does not have a sidechain addresses
|
||||
*/
|
||||
vector<optional<sidechain_address_object>> get_sidechain_addresses_by_account(account_id_type account)const;
|
||||
|
||||
/**
|
||||
* @brief Get the sidechain addresses for a given sidechain
|
||||
* @param sidechain Sidechain for which addresses should be retrieved
|
||||
* @return The sidechain addresses objects, or null if the sidechain does not have any addresses
|
||||
*/
|
||||
vector<optional<sidechain_address_object>> get_sidechain_addresses_by_sidechain(peerplays_sidechain::sidechain_type sidechain)const;
|
||||
|
||||
/**
|
||||
* @brief Get the sidechain addresses for a given account and sidechain
|
||||
* @param account The ID of the account whose sidechain addresses should be retrieved
|
||||
* @param sidechain Sidechain for which address should be retrieved
|
||||
* @return The sidechain addresses objects, or null if the account does not have a sidechain addresses for a given sidechain
|
||||
*/
|
||||
fc::optional<sidechain_address_object> get_sidechain_address_by_account_and_sidechain(account_id_type account, peerplays_sidechain::sidechain_type sidechain)const;
|
||||
|
||||
/**
|
||||
* @brief Get the total number of sidechain addresses registered with the blockchain
|
||||
*/
|
||||
uint64_t get_sidechain_addresses_count()const;
|
||||
|
||||
/// WORKERS
|
||||
|
||||
/**
|
||||
|
|
@ -814,6 +880,18 @@ FC_API(graphene::app::database_api,
|
|||
(lookup_son_accounts)
|
||||
(get_son_count)
|
||||
|
||||
// SON wallets
|
||||
(get_active_son_wallet)
|
||||
(get_son_wallet_by_time_point)
|
||||
(get_son_wallets)
|
||||
|
||||
// Sidechain addresses
|
||||
(get_sidechain_addresses)
|
||||
(get_sidechain_addresses_by_account)
|
||||
(get_sidechain_addresses_by_sidechain)
|
||||
(get_sidechain_address_by_account_and_sidechain)
|
||||
(get_sidechain_addresses_count)
|
||||
|
||||
// workers
|
||||
(get_workers_by_account)
|
||||
// Votes
|
||||
|
|
|
|||
8
libraries/chain/CMakeLists.txt
Normal file → Executable file
8
libraries/chain/CMakeLists.txt
Normal file → Executable file
|
|
@ -19,6 +19,7 @@ if( GRAPHENE_DISABLE_UNITY_BUILD )
|
|||
db_maint.cpp
|
||||
db_management.cpp
|
||||
db_market.cpp
|
||||
db_sidechain.cpp
|
||||
db_update.cpp
|
||||
db_witness_schedule.cpp
|
||||
)
|
||||
|
|
@ -116,6 +117,13 @@ add_library( graphene_chain
|
|||
son_evaluator.cpp
|
||||
son_object.cpp
|
||||
|
||||
son_wallet_evaluator.cpp
|
||||
son_wallet_deposit_evaluator.cpp
|
||||
son_wallet_withdraw_evaluator.cpp
|
||||
|
||||
sidechain_address_evaluator.cpp
|
||||
sidechain_transaction_evaluator.cpp
|
||||
|
||||
${HEADERS}
|
||||
${PROTOCOL_HEADERS}
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/include/graphene/chain/hardfork.hpp"
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
#include "db_maint.cpp"
|
||||
#include "db_management.cpp"
|
||||
#include "db_market.cpp"
|
||||
#include "db_sidechain.cpp"
|
||||
#include "db_update.cpp"
|
||||
#include "db_witness_schedule.cpp"
|
||||
#include "db_notify.cpp"
|
||||
|
|
@ -336,8 +336,6 @@ processed_transaction database::validate_transaction( const signed_transaction&
|
|||
|
||||
processed_transaction database::push_proposal(const proposal_object& proposal)
|
||||
{ try {
|
||||
FC_ASSERT( _undo_db.size() < _undo_db.max_size(), "Undo database is full!" );
|
||||
|
||||
transaction_evaluation_state eval_state(this);
|
||||
eval_state._is_proposed_trx = true;
|
||||
|
||||
|
|
@ -347,6 +345,8 @@ processed_transaction database::push_proposal(const proposal_object& proposal)
|
|||
size_t old_applied_ops_size = _applied_ops.size();
|
||||
|
||||
try {
|
||||
if( _undo_db.size() >= _undo_db.max_size() )
|
||||
_undo_db.set_max_size( _undo_db.size() + 1 );
|
||||
auto session = _undo_db.start_undo_session(true);
|
||||
for( auto& op : proposal.proposed_transaction.operations )
|
||||
eval_state.operation_results.emplace_back(apply_operation(eval_state, op));
|
||||
|
|
@ -427,34 +427,6 @@ signed_block database::_generate_block(
|
|||
_pending_tx_session.reset();
|
||||
_pending_tx_session = _undo_db.start_undo_session();
|
||||
|
||||
if( head_block_time() > HARDFORK_SON_TIME )
|
||||
{
|
||||
// Approve proposals raised by me in previous schedule or before
|
||||
process_son_proposals( witness_obj, block_signing_private_key );
|
||||
// Check for new SON Deregistration Proposals to be raised
|
||||
std::set<son_id_type> sons_to_be_dereg = get_sons_to_be_deregistered();
|
||||
if(sons_to_be_dereg.size() > 0)
|
||||
{
|
||||
// We shouldn't raise proposals for the SONs for which a de-reg
|
||||
// proposal is already raised.
|
||||
std::set<son_id_type> sons_being_dereg = get_sons_being_deregistered();
|
||||
for( auto& son : sons_to_be_dereg)
|
||||
{
|
||||
// New SON to be deregistered
|
||||
if(sons_being_dereg.find(son) == sons_being_dereg.end())
|
||||
{
|
||||
// Creating the de-reg proposal
|
||||
auto op = create_son_deregister_proposal(son, witness_obj);
|
||||
if(op.valid())
|
||||
{
|
||||
// Signing and pushing into the txs to be included in the block
|
||||
_pending_tx.insert( _pending_tx.begin(), create_signed_transaction( block_signing_private_key, *op ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t postponed_tx_count = 0;
|
||||
// pop pending state (reset to head block state)
|
||||
for( const processed_transaction& tx : _pending_tx )
|
||||
|
|
@ -649,8 +621,13 @@ void database::_apply_block( const signed_block& next_block )
|
|||
_current_virtual_op = 0;
|
||||
}
|
||||
|
||||
if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM)
|
||||
update_witness_schedule(next_block);
|
||||
if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM) {
|
||||
update_witness_schedule(next_block);
|
||||
if(global_props.active_sons.size() > 0) {
|
||||
update_son_schedule(next_block);
|
||||
}
|
||||
}
|
||||
|
||||
const uint32_t missed = update_witness_missed_blocks( next_block );
|
||||
update_global_dynamic_data( next_block, missed );
|
||||
update_signing_witness(signing_witness, next_block);
|
||||
|
|
@ -678,8 +655,13 @@ void database::_apply_block( const signed_block& next_block )
|
|||
// update_global_dynamic_data() as perhaps these methods only need
|
||||
// to be called for header validation?
|
||||
update_maintenance_flag( maint_needed );
|
||||
if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM)
|
||||
update_witness_schedule();
|
||||
if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM) {
|
||||
update_witness_schedule();
|
||||
if(global_props.active_sons.size() > 0) {
|
||||
update_son_schedule();
|
||||
}
|
||||
}
|
||||
|
||||
if( !_node_property_object.debug_updates.empty() )
|
||||
apply_debug_updates();
|
||||
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ std::set<son_id_type> database::get_sons_to_be_deregistered()
|
|||
// TODO : We need to add a function that returns if we can deregister SON
|
||||
// i.e. with introduction of PW code, we have to make a decision if the SON
|
||||
// is needed for release of funds from the PW
|
||||
if(head_block_time() - stats.last_down_timestamp >= fc::hours(SON_DEREGISTER_TIME))
|
||||
if(head_block_time() - stats.last_down_timestamp >= fc::seconds(get_global_properties().parameters.son_deregister_time()))
|
||||
{
|
||||
ret.insert(son.id);
|
||||
}
|
||||
|
|
@ -179,14 +179,29 @@ std::set<son_id_type> database::get_sons_to_be_deregistered()
|
|||
return ret;
|
||||
}
|
||||
|
||||
fc::optional<operation> database::create_son_deregister_proposal(const son_id_type& son_id, const witness_object& current_witness )
|
||||
std::set<son_id_type> database::get_sons_being_reported_down()
|
||||
{
|
||||
std::set<son_id_type> ret;
|
||||
const auto& son_proposal_idx = get_index_type<son_proposal_index>().indices().get< by_id >();
|
||||
|
||||
for( auto& son_proposal : son_proposal_idx )
|
||||
{
|
||||
if(son_proposal.proposal_type == son_proposal_type::son_report_down_proposal)
|
||||
{
|
||||
ret.insert(son_proposal.son_id);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
fc::optional<operation> database::create_son_deregister_proposal( son_id_type son_id, account_id_type paying_son )
|
||||
{
|
||||
son_delete_operation son_dereg_op;
|
||||
son_dereg_op.payer = current_witness.witness_account;
|
||||
son_dereg_op.payer = get_global_properties().parameters.son_account();
|
||||
son_dereg_op.son_id = son_id;
|
||||
|
||||
proposal_create_operation proposal_op;
|
||||
proposal_op.fee_paying_account = current_witness.witness_account;
|
||||
proposal_op.fee_paying_account = paying_son;
|
||||
proposal_op.proposed_ops.push_back( op_wrapper( son_dereg_op ) );
|
||||
uint32_t lifetime = ( get_global_properties().parameters.block_interval * get_global_properties().active_witnesses.size() ) * 3;
|
||||
proposal_op.expiration_time = time_point_sec( head_block_time().sec_since_epoch() + lifetime );
|
||||
|
|
@ -207,52 +222,13 @@ signed_transaction database::create_signed_transaction( const fc::ecc::private_k
|
|||
return processed_trx;
|
||||
}
|
||||
|
||||
void database::process_son_proposals( const witness_object& current_witness, const fc::ecc::private_key& private_key )
|
||||
{
|
||||
const auto& son_proposal_idx = get_index_type<son_proposal_index>().indices().get< by_id >();
|
||||
const auto& proposal_idx = get_index_type<proposal_index>().indices().get< by_id >();
|
||||
|
||||
auto approve_proposal = [ & ]( const proposal_id_type& id )
|
||||
{
|
||||
proposal_update_operation puo;
|
||||
puo.fee_paying_account = current_witness.witness_account;
|
||||
puo.proposal = id;
|
||||
puo.active_approvals_to_add = { current_witness.witness_account };
|
||||
_pending_tx.insert( _pending_tx.begin(), create_signed_transaction( private_key, puo ) );
|
||||
};
|
||||
|
||||
for( auto& son_proposal : son_proposal_idx )
|
||||
{
|
||||
const auto& proposal = proposal_idx.find( son_proposal.proposal_id );
|
||||
FC_ASSERT( proposal != proposal_idx.end() );
|
||||
if( proposal->proposer == current_witness.witness_account)
|
||||
{
|
||||
approve_proposal( proposal->id );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void database::remove_son_proposal( const proposal_object& proposal )
|
||||
{ try {
|
||||
if( proposal.proposed_transaction.operations.size() == 1 &&
|
||||
( proposal.proposed_transaction.operations.back().which() == operation::tag<son_delete_operation>::value) )
|
||||
{
|
||||
const auto& son_proposal_idx = get_index_type<son_proposal_index>().indices().get<by_proposal>();
|
||||
auto son_proposal_itr = son_proposal_idx.find( proposal.id );
|
||||
if( son_proposal_itr == son_proposal_idx.end() ) {
|
||||
return;
|
||||
}
|
||||
remove( *son_proposal_itr );
|
||||
}
|
||||
} FC_CAPTURE_AND_RETHROW( (proposal) ) }
|
||||
|
||||
bool database::is_son_dereg_valid( const son_id_type& son_id )
|
||||
bool database::is_son_dereg_valid( son_id_type son_id )
|
||||
{
|
||||
const auto& son_idx = get_index_type<son_index>().indices().get< by_id >();
|
||||
auto son = son_idx.find( son_id );
|
||||
FC_ASSERT( son != son_idx.end() );
|
||||
bool ret = ( son->status == son_status::in_maintenance &&
|
||||
(head_block_time() - son->statistics(*this).last_down_timestamp >= fc::hours(SON_DEREGISTER_TIME)));
|
||||
(head_block_time() - son->statistics(*this).last_down_timestamp >= fc::seconds(get_global_properties().parameters.son_deregister_time())));
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -56,6 +56,11 @@
|
|||
#include <graphene/chain/global_betting_statistics_object.hpp>
|
||||
#include <graphene/chain/son_object.hpp>
|
||||
#include <graphene/chain/son_proposal_object.hpp>
|
||||
#include <graphene/chain/son_wallet_object.hpp>
|
||||
#include <graphene/chain/son_wallet_deposit_object.hpp>
|
||||
#include <graphene/chain/son_wallet_withdraw_object.hpp>
|
||||
#include <graphene/chain/sidechain_address_object.hpp>
|
||||
#include <graphene/chain/sidechain_transaction_object.hpp>
|
||||
|
||||
#include <graphene/chain/account_evaluator.hpp>
|
||||
#include <graphene/chain/asset_evaluator.hpp>
|
||||
|
|
@ -78,6 +83,11 @@
|
|||
#include <graphene/chain/betting_market_evaluator.hpp>
|
||||
#include <graphene/chain/tournament_evaluator.hpp>
|
||||
#include <graphene/chain/son_evaluator.hpp>
|
||||
#include <graphene/chain/son_wallet_evaluator.hpp>
|
||||
#include <graphene/chain/son_wallet_deposit_evaluator.hpp>
|
||||
#include <graphene/chain/son_wallet_withdraw_evaluator.hpp>
|
||||
#include <graphene/chain/sidechain_address_evaluator.hpp>
|
||||
#include <graphene/chain/sidechain_transaction_evaluator.hpp>
|
||||
|
||||
#include <graphene/chain/protocol/fee_schedule.hpp>
|
||||
|
||||
|
|
@ -247,6 +257,21 @@ void database::initialize_evaluators()
|
|||
register_evaluator<create_son_evaluator>();
|
||||
register_evaluator<update_son_evaluator>();
|
||||
register_evaluator<delete_son_evaluator>();
|
||||
register_evaluator<son_heartbeat_evaluator>();
|
||||
register_evaluator<son_report_down_evaluator>();
|
||||
register_evaluator<son_maintenance_evaluator>();
|
||||
register_evaluator<recreate_son_wallet_evaluator>();
|
||||
register_evaluator<update_son_wallet_evaluator>();
|
||||
register_evaluator<create_son_wallet_deposit_evaluator>();
|
||||
register_evaluator<process_son_wallet_deposit_evaluator>();
|
||||
register_evaluator<create_son_wallet_withdraw_evaluator>();
|
||||
register_evaluator<process_son_wallet_withdraw_evaluator>();
|
||||
register_evaluator<add_sidechain_address_evaluator>();
|
||||
register_evaluator<update_sidechain_address_evaluator>();
|
||||
register_evaluator<delete_sidechain_address_evaluator>();
|
||||
register_evaluator<sidechain_transaction_create_evaluator>();
|
||||
register_evaluator<sidechain_transaction_sign_evaluator>();
|
||||
register_evaluator<sidechain_transaction_send_evaluator>();
|
||||
}
|
||||
|
||||
void database::initialize_indexes()
|
||||
|
|
@ -263,7 +288,7 @@ void database::initialize_indexes()
|
|||
acnt_index->add_secondary_index<account_referrer_index>();
|
||||
|
||||
add_index< primary_index<committee_member_index, 8> >(); // 256 members per chunk
|
||||
add_index< primary_index<son_index, 8> >(); // 256 sons per chunk
|
||||
add_index< primary_index<son_index> >();
|
||||
add_index< primary_index<witness_index, 10> >(); // 1024 witnesses per chunk
|
||||
add_index< primary_index<limit_order_index > >();
|
||||
add_index< primary_index<call_order_index > >();
|
||||
|
|
@ -291,6 +316,13 @@ void database::initialize_indexes()
|
|||
add_index< primary_index<game_index> >();
|
||||
add_index< primary_index<son_proposal_index> >();
|
||||
|
||||
add_index< primary_index<son_wallet_index> >();
|
||||
add_index< primary_index<son_wallet_deposit_index> >();
|
||||
add_index< primary_index<son_wallet_withdraw_index> >();
|
||||
|
||||
add_index< primary_index<sidechain_address_index> >();
|
||||
add_index< primary_index<sidechain_transaction_index> >();
|
||||
|
||||
//Implementation object indexes
|
||||
add_index< primary_index<transaction_index > >();
|
||||
|
||||
|
|
@ -306,6 +338,7 @@ void database::initialize_indexes()
|
|||
add_index< primary_index<flat_index< block_summary_object >> >();
|
||||
add_index< primary_index<simple_index<chain_property_object > > >();
|
||||
add_index< primary_index<simple_index<witness_schedule_object > > >();
|
||||
add_index< primary_index<simple_index<son_schedule_object > > >();
|
||||
add_index< primary_index<simple_index<budget_record_object > > >();
|
||||
add_index< primary_index< special_authority_index > >();
|
||||
add_index< primary_index< buyback_index > >();
|
||||
|
|
@ -939,6 +972,29 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
});
|
||||
assert( wso.id == witness_schedule_id_type() );
|
||||
|
||||
// Initialize witness schedule
|
||||
#ifndef NDEBUG
|
||||
const son_schedule_object& sso =
|
||||
#endif
|
||||
create<son_schedule_object>([&](son_schedule_object& _sso)
|
||||
{
|
||||
// for scheduled
|
||||
memset(_sso.rng_seed.begin(), 0, _sso.rng_seed.size());
|
||||
|
||||
witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV);
|
||||
|
||||
auto init_witnesses = get_global_properties().active_witnesses;
|
||||
|
||||
_sso.scheduler = son_scheduler();
|
||||
_sso.scheduler._min_token_count = std::max(int(init_witnesses.size()) / 2, 1);
|
||||
|
||||
|
||||
_sso.last_scheduling_block = 0;
|
||||
|
||||
_sso.recent_slots_filled = fc::uint128::max_value();
|
||||
});
|
||||
assert( sso.id == son_schedule_id_type() );
|
||||
|
||||
// Enable fees
|
||||
modify(get_global_properties(), [&genesis_state](global_property_object& p) {
|
||||
p.parameters.current_fees = genesis_state.initial_parameters.current_fees;
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@
|
|||
#include <graphene/chain/global_property_object.hpp>
|
||||
#include <graphene/chain/market_object.hpp>
|
||||
#include <graphene/chain/special_authority_object.hpp>
|
||||
#include <graphene/chain/son_object.hpp>
|
||||
#include <graphene/chain/vesting_balance_object.hpp>
|
||||
#include <graphene/chain/vote_count.hpp>
|
||||
#include <graphene/chain/witness_object.hpp>
|
||||
|
|
@ -122,7 +123,7 @@ void database::pay_sons()
|
|||
time_point_sec now = head_block_time();
|
||||
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
|
||||
// Current requirement is that we have to pay every 24 hours, so the following check
|
||||
if( dpo.son_budget.value > 0 && now - dpo.last_son_payout_time >= fc::days(1)) {
|
||||
if( dpo.son_budget.value > 0 && ((now - dpo.last_son_payout_time) >= fc::seconds(get_global_properties().parameters.son_pay_time()))) {
|
||||
uint64_t total_txs_signed = 0;
|
||||
share_type son_budget = dpo.son_budget;
|
||||
get_index_type<son_stats_index>().inspect_all_objects([this, &total_txs_signed](const object& o) {
|
||||
|
|
@ -137,7 +138,8 @@ void database::pay_sons()
|
|||
if(s.txs_signed > 0){
|
||||
auto son_params = get_global_properties().parameters;
|
||||
share_type pay = (s.txs_signed * son_budget.value)/total_txs_signed;
|
||||
|
||||
// TODO: Remove me after QA
|
||||
ilog( "pay ${p} to ${s} for ${t} transactions signed", ("p", pay.value)("s", s.id)("t",s.txs_signed) );
|
||||
const auto& idx = get_index_type<son_index>().indices().get<by_id>();
|
||||
auto son_obj = idx.find( s.owner );
|
||||
modify( *son_obj, [&]( son_object& _son_obj)
|
||||
|
|
@ -152,6 +154,7 @@ void database::pay_sons()
|
|||
//Reset the tx counter in each son statistics object
|
||||
modify( s, [&]( son_statistics_object& _s)
|
||||
{
|
||||
_s.total_txs_signed += _s.txs_signed;
|
||||
_s.txs_signed = 0;
|
||||
});
|
||||
}
|
||||
|
|
@ -164,6 +167,156 @@ void database::pay_sons()
|
|||
}
|
||||
}
|
||||
|
||||
void database::update_son_metrics(const vector<son_info>& curr_active_sons)
|
||||
{
|
||||
vector<son_id_type> current_sons;
|
||||
|
||||
current_sons.reserve(curr_active_sons.size());
|
||||
std::transform(curr_active_sons.begin(), curr_active_sons.end(),
|
||||
std::inserter(current_sons, current_sons.end()),
|
||||
[](const son_info &swi) {
|
||||
return swi.son_id;
|
||||
});
|
||||
|
||||
const auto& son_idx = get_index_type<son_index>().indices().get< by_id >();
|
||||
for( auto& son : son_idx )
|
||||
{
|
||||
auto& stats = son.statistics(*this);
|
||||
bool is_active_son = (std::find(current_sons.begin(), current_sons.end(), son.id) != current_sons.end());
|
||||
modify( stats, [&]( son_statistics_object& _stats )
|
||||
{
|
||||
_stats.total_downtime += _stats.current_interval_downtime;
|
||||
_stats.current_interval_downtime = 0;
|
||||
if(is_active_son)
|
||||
{
|
||||
_stats.total_voted_time = _stats.total_voted_time + get_global_properties().parameters.maintenance_interval;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void database::update_son_statuses(const vector<son_info>& curr_active_sons, const vector<son_info>& new_active_sons)
|
||||
{
|
||||
vector<son_id_type> current_sons, new_sons;
|
||||
vector<son_id_type> sons_to_remove, sons_to_add;
|
||||
const auto& idx = get_index_type<son_index>().indices().get<by_id>();
|
||||
|
||||
current_sons.reserve(curr_active_sons.size());
|
||||
std::transform(curr_active_sons.begin(), curr_active_sons.end(),
|
||||
std::inserter(current_sons, current_sons.end()),
|
||||
[](const son_info &swi) {
|
||||
return swi.son_id;
|
||||
});
|
||||
|
||||
new_sons.reserve(new_active_sons.size());
|
||||
std::transform(new_active_sons.begin(), new_active_sons.end(),
|
||||
std::inserter(new_sons, new_sons.end()),
|
||||
[](const son_info &swi) {
|
||||
return swi.son_id;
|
||||
});
|
||||
|
||||
// find all cur_active_sons members that is not in new_active_sons
|
||||
for_each(current_sons.begin(), current_sons.end(),
|
||||
[&sons_to_remove, &new_sons](const son_id_type& si)
|
||||
{
|
||||
if(std::find(new_sons.begin(), new_sons.end(), si) ==
|
||||
new_sons.end())
|
||||
{
|
||||
sons_to_remove.push_back(si);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
for( const auto& sid : sons_to_remove )
|
||||
{
|
||||
auto son = idx.find( sid );
|
||||
if(son == idx.end()) // SON is deleted already
|
||||
continue;
|
||||
// keep maintenance status for nodes becoming inactive
|
||||
if(son->status == son_status::active)
|
||||
{
|
||||
modify( *son, [&]( son_object& obj ){
|
||||
obj.status = son_status::inactive;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// find all new_active_sons members that is not in cur_active_sons
|
||||
for_each(new_sons.begin(), new_sons.end(),
|
||||
[&sons_to_add, ¤t_sons](const son_id_type& si)
|
||||
{
|
||||
if(std::find(current_sons.begin(), current_sons.end(), si) ==
|
||||
current_sons.end())
|
||||
{
|
||||
sons_to_add.push_back(si);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
for( const auto& sid : sons_to_add )
|
||||
{
|
||||
auto son = idx.find( sid );
|
||||
FC_ASSERT(son != idx.end(), "Invalid SON in active list, id={sonid}.", ("sonid", sid));
|
||||
// keep maintenance status for new nodes
|
||||
if(son->status == son_status::inactive)
|
||||
{
|
||||
modify( *son, [&]( son_object& obj ){
|
||||
obj.status = son_status::active;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ilog("New SONS");
|
||||
for(size_t i = 0; i < new_sons.size(); i++) {
|
||||
auto son = idx.find( new_sons[i] );
|
||||
if(son == idx.end()) // SON is deleted already
|
||||
continue;
|
||||
ilog( "${s}, status = ${ss}, total_votes = ${sv}", ("s", new_sons[i])("ss", son->status)("sv", son->total_votes) );
|
||||
}
|
||||
|
||||
if( sons_to_remove.size() > 0 )
|
||||
{
|
||||
remove_inactive_son_proposals(sons_to_remove);
|
||||
}
|
||||
}
|
||||
|
||||
void database::update_son_wallet(const vector<son_info>& new_active_sons)
|
||||
{
|
||||
bool should_recreate_pw = true;
|
||||
|
||||
// Expire for current son_wallet_object wallet, if exists
|
||||
const auto& idx_swi = get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
auto obj = idx_swi.rbegin();
|
||||
if (obj != idx_swi.rend()) {
|
||||
// Compare current wallet SONs and to-be lists of active sons
|
||||
auto cur_wallet_sons = (*obj).sons;
|
||||
|
||||
bool wallet_son_sets_equal = (cur_wallet_sons.size() == new_active_sons.size());
|
||||
if (wallet_son_sets_equal) {
|
||||
for( size_t i = 0; i < cur_wallet_sons.size(); i++ ) {
|
||||
wallet_son_sets_equal = wallet_son_sets_equal && cur_wallet_sons.at(i) == new_active_sons.at(i);
|
||||
}
|
||||
}
|
||||
|
||||
should_recreate_pw = !wallet_son_sets_equal;
|
||||
|
||||
if (should_recreate_pw) {
|
||||
modify(*obj, [&, obj](son_wallet_object &swo) {
|
||||
swo.expires = head_block_time();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (should_recreate_pw) {
|
||||
// Create new son_wallet_object, to initiate wallet recreation
|
||||
create<son_wallet_object>( [&]( son_wallet_object& obj ) {
|
||||
obj.valid_from = head_block_time();
|
||||
obj.expires = time_point_sec::maximum();
|
||||
obj.sons.insert(obj.sons.end(), new_active_sons.begin(), new_active_sons.end());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void database::pay_workers( share_type& budget )
|
||||
{
|
||||
// ilog("Processing payroll! Available budget is ${b}", ("b", budget));
|
||||
|
|
@ -399,67 +552,116 @@ void database::update_active_sons()
|
|||
|
||||
const auto& all_sons = get_index_type<son_index>().indices();
|
||||
|
||||
auto& local_vote_buffer_ref = _vote_tally_buffer;
|
||||
for( const son_object& son : all_sons )
|
||||
{
|
||||
modify( son, [&]( son_object& obj ){
|
||||
obj.total_votes = _vote_tally_buffer[son.vote_id];
|
||||
if(son.status == son_status::request_maintenance)
|
||||
{
|
||||
auto& stats = son.statistics(*this);
|
||||
modify( stats, [&]( son_statistics_object& _s){
|
||||
_s.last_down_timestamp = head_block_time();
|
||||
});
|
||||
}
|
||||
modify( son, [local_vote_buffer_ref]( son_object& obj ){
|
||||
obj.total_votes = local_vote_buffer_ref[obj.vote_id];
|
||||
if(obj.status == son_status::request_maintenance)
|
||||
obj.status = son_status::in_maintenance;
|
||||
});
|
||||
}
|
||||
|
||||
// Update SON authority
|
||||
modify( get(GRAPHENE_SON_ACCOUNT_ID), [&]( account_object& a )
|
||||
if( gpo.parameters.son_account() != GRAPHENE_NULL_ACCOUNT )
|
||||
{
|
||||
if( head_block_time() < HARDFORK_533_TIME )
|
||||
modify( get(gpo.parameters.son_account()), [&]( account_object& a )
|
||||
{
|
||||
uint64_t total_votes = 0;
|
||||
map<account_id_type, uint64_t> weights;
|
||||
a.active.weight_threshold = 0;
|
||||
a.active.clear();
|
||||
|
||||
for( const son_object& son : sons )
|
||||
if( head_block_time() < HARDFORK_533_TIME )
|
||||
{
|
||||
weights.emplace(son.son_account, _vote_tally_buffer[son.vote_id]);
|
||||
total_votes += _vote_tally_buffer[son.vote_id];
|
||||
}
|
||||
uint64_t total_votes = 0;
|
||||
map<account_id_type, uint64_t> weights;
|
||||
a.active.weight_threshold = 0;
|
||||
a.active.account_auths.clear();
|
||||
|
||||
// total_votes is 64 bits. Subtract the number of leading low bits from 64 to get the number of useful bits,
|
||||
// then I want to keep the most significant 16 bits of what's left.
|
||||
int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0);
|
||||
for( const auto& weight : weights )
|
||||
for( const son_object& son : sons )
|
||||
{
|
||||
weights.emplace(son.son_account, _vote_tally_buffer[son.vote_id]);
|
||||
total_votes += _vote_tally_buffer[son.vote_id];
|
||||
}
|
||||
|
||||
// total_votes is 64 bits. Subtract the number of leading low bits from 64 to get the number of useful bits,
|
||||
// then I want to keep the most significant 16 bits of what's left.
|
||||
int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0);
|
||||
for( const auto& weight : weights )
|
||||
{
|
||||
// Ensure that everyone has at least one vote. Zero weights aren't allowed.
|
||||
uint16_t votes = std::max((weight.second >> bits_to_drop), uint64_t(1) );
|
||||
a.active.account_auths[weight.first] += votes;
|
||||
a.active.weight_threshold += votes;
|
||||
}
|
||||
|
||||
a.active.weight_threshold *= 2;
|
||||
a.active.weight_threshold /= 3;
|
||||
a.active.weight_threshold += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ensure that everyone has at least one vote. Zero weights aren't allowed.
|
||||
uint16_t votes = std::max((weight.second >> bits_to_drop), uint64_t(1) );
|
||||
a.active.account_auths[weight.first] += votes;
|
||||
a.active.weight_threshold += votes;
|
||||
vote_counter vc;
|
||||
for( const son_object& son : sons )
|
||||
vc.add( son.son_account, std::max(_vote_tally_buffer[son.vote_id], UINT64_C(1)) );
|
||||
vc.finish_2_3( a.active );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
|
||||
a.active.weight_threshold /= 2;
|
||||
a.active.weight_threshold += 1;
|
||||
// Compare current and to-be lists of active sons
|
||||
auto cur_active_sons = gpo.active_sons;
|
||||
vector<son_info> new_active_sons;
|
||||
for( const son_object& son : sons ) {
|
||||
son_info swi;
|
||||
swi.son_id = son.id;
|
||||
swi.total_votes = son.total_votes;
|
||||
swi.signing_key = son.signing_key;
|
||||
swi.sidechain_public_keys = son.sidechain_public_keys;
|
||||
new_active_sons.push_back(swi);
|
||||
}
|
||||
|
||||
bool son_sets_equal = (cur_active_sons.size() == new_active_sons.size());
|
||||
if (son_sets_equal) {
|
||||
for( size_t i = 0; i < cur_active_sons.size(); i++ ) {
|
||||
son_sets_equal = son_sets_equal && cur_active_sons.at(i) == new_active_sons.at(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
vote_counter vc;
|
||||
for( const son_object& son : sons )
|
||||
vc.add( son.son_account, std::max(_vote_tally_buffer[son.vote_id], UINT64_C(1)) );
|
||||
vc.finish( a.active );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
if (son_sets_equal) {
|
||||
ilog( "Active SONs set NOT CHANGED" );
|
||||
} else {
|
||||
ilog( "Active SONs set CHANGED" );
|
||||
|
||||
update_son_wallet(new_active_sons);
|
||||
update_son_statuses(cur_active_sons, new_active_sons);
|
||||
}
|
||||
|
||||
// Update son performance metrics
|
||||
update_son_metrics(cur_active_sons);
|
||||
|
||||
modify(gpo, [&]( global_property_object& gp ){
|
||||
gp.active_sons.clear();
|
||||
gp.active_sons.reserve(sons.size());
|
||||
std::transform(sons.begin(), sons.end(),
|
||||
std::inserter(gp.active_sons, gp.active_sons.end()),
|
||||
[](const son_object& s) {
|
||||
return s.id;
|
||||
});
|
||||
gp.active_sons.reserve(new_active_sons.size());
|
||||
gp.active_sons.insert(gp.active_sons.end(), new_active_sons.begin(), new_active_sons.end());
|
||||
});
|
||||
|
||||
//const witness_schedule_object& wso = witness_schedule_id_type()(*this);
|
||||
//modify(wso, [&](witness_schedule_object& _wso)
|
||||
//{
|
||||
// _wso.scheduler.update(gpo.active_witnesses);
|
||||
//});
|
||||
const son_schedule_object& sso = son_schedule_id_type()(*this);
|
||||
modify(sso, [&](son_schedule_object& _sso)
|
||||
{
|
||||
flat_set<son_id_type> active_sons;
|
||||
active_sons.reserve(gpo.active_sons.size());
|
||||
std::transform(gpo.active_sons.begin(), gpo.active_sons.end(),
|
||||
std::inserter(active_sons, active_sons.end()),
|
||||
[](const son_info& swi) {
|
||||
return swi.son_id;
|
||||
});
|
||||
_sso.scheduler.update(active_sons);
|
||||
});
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
void database::initialize_budget_record( fc::time_point_sec now, budget_record& rec )const
|
||||
|
|
@ -1375,6 +1577,30 @@ void process_dividend_assets(database& db)
|
|||
}
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
void perform_son_tasks(database& db)
|
||||
{
|
||||
const global_property_object& gpo = db.get_global_properties();
|
||||
if(gpo.parameters.son_account() == GRAPHENE_NULL_ACCOUNT && db.head_block_time() >= HARDFORK_SON_TIME)
|
||||
{
|
||||
const auto& son_account = db.create<account_object>([&](account_object& a) {
|
||||
a.name = "son-account";
|
||||
a.statistics = db.create<account_statistics_object>([&](account_statistics_object& s){s.owner = a.id;}).id;
|
||||
a.owner.weight_threshold = 1;
|
||||
a.active.weight_threshold = 0;
|
||||
a.registrar = a.lifetime_referrer = a.referrer = a.id;
|
||||
a.membership_expiration_date = time_point_sec::maximum();
|
||||
a.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;
|
||||
a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;
|
||||
});
|
||||
|
||||
db.modify( gpo, [&]( global_property_object& gpo ) {
|
||||
gpo.parameters.extensions.value.son_account = son_account.get_id();
|
||||
if( gpo.pending_parameters )
|
||||
gpo.pending_parameters->extensions.value.son_account = son_account.get_id();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void database::perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props)
|
||||
{ try {
|
||||
const auto& gpo = get_global_properties();
|
||||
|
|
@ -1383,6 +1609,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
|
|||
create_buyback_orders(*this);
|
||||
|
||||
process_dividend_assets(*this);
|
||||
perform_son_tasks(*this);
|
||||
|
||||
struct vote_tally_helper {
|
||||
database& d;
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ database::database() :
|
|||
{
|
||||
initialize_indexes();
|
||||
initialize_evaluators();
|
||||
initialize_db_sidechain();
|
||||
}
|
||||
|
||||
database::~database()
|
||||
|
|
|
|||
|
|
@ -294,6 +294,51 @@ struct get_impacted_account_visitor
|
|||
void operator()( const son_delete_operation& op ) {
|
||||
_impacted.insert( op.owner_account );
|
||||
}
|
||||
void operator()( const son_heartbeat_operation& op ) {
|
||||
_impacted.insert( op.owner_account );
|
||||
}
|
||||
void operator()( const son_report_down_operation& op ) {
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const son_maintenance_operation& op ) {
|
||||
_impacted.insert( op.owner_account );
|
||||
}
|
||||
void operator()( const son_wallet_recreate_operation& op ) {
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const son_wallet_update_operation& op ) {
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const son_wallet_deposit_create_operation& op ) {
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const son_wallet_deposit_process_operation& op ) {
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const son_wallet_withdraw_create_operation& op ) {
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const son_wallet_withdraw_process_operation& op ) {
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const sidechain_address_add_operation& op ) {
|
||||
_impacted.insert( op.sidechain_address_account );
|
||||
}
|
||||
void operator()( const sidechain_address_update_operation& op ) {
|
||||
_impacted.insert( op.sidechain_address_account );
|
||||
}
|
||||
void operator()( const sidechain_address_delete_operation& op ) {
|
||||
_impacted.insert( op.sidechain_address_account );
|
||||
}
|
||||
void operator()( const sidechain_transaction_create_operation& op ) {
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const sidechain_transaction_sign_operation& op ) {
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const sidechain_transaction_send_operation& op ) {
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
};
|
||||
|
||||
void operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result )
|
||||
|
|
@ -387,6 +432,19 @@ void get_relevant_accounts( const object* obj, flat_set<account_id_type>& accoun
|
|||
assert( aobj != nullptr );
|
||||
accounts.insert( aobj->son_account );
|
||||
break;
|
||||
} case son_wallet_object_type:{
|
||||
break;
|
||||
} case son_wallet_deposit_object_type:{
|
||||
break;
|
||||
} case son_wallet_withdraw_object_type:{
|
||||
break;
|
||||
} case sidechain_address_object_type:{
|
||||
const auto& aobj = dynamic_cast<const sidechain_address_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
accounts.insert( aobj->sidechain_address_account );
|
||||
break;
|
||||
} case sidechain_transaction_object_type:{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10
libraries/chain/db_sidechain.cpp
Normal file
10
libraries/chain/db_sidechain.cpp
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#include <graphene/chain/database.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
void database::initialize_db_sidechain()
|
||||
{
|
||||
recreate_primary_wallet = false;
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
@ -224,6 +224,7 @@ void database::clear_expired_proposals()
|
|||
elog("Failed to apply proposed transaction on its expiration. Deleting it.\n${proposal}\n${error}",
|
||||
("proposal", proposal)("error", e.to_detail_string()));
|
||||
}
|
||||
remove_son_proposal(proposal);
|
||||
remove(proposal);
|
||||
}
|
||||
}
|
||||
|
|
@ -651,4 +652,50 @@ void database::update_betting_markets(fc::time_point_sec current_block_time)
|
|||
remove_completed_events();
|
||||
}
|
||||
|
||||
void database::remove_son_proposal( const proposal_object& proposal )
|
||||
{ try {
|
||||
if( proposal.proposed_transaction.operations.size() == 1 &&
|
||||
( proposal.proposed_transaction.operations.back().which() == operation::tag<son_delete_operation>::value ||
|
||||
proposal.proposed_transaction.operations.back().which() == operation::tag<son_report_down_operation>::value) )
|
||||
{
|
||||
const auto& son_proposal_idx = get_index_type<son_proposal_index>().indices().get<by_proposal>();
|
||||
auto son_proposal_itr = son_proposal_idx.find( proposal.id );
|
||||
if( son_proposal_itr == son_proposal_idx.end() ) {
|
||||
return;
|
||||
}
|
||||
remove( *son_proposal_itr );
|
||||
}
|
||||
} FC_CAPTURE_AND_RETHROW( (proposal) ) }
|
||||
|
||||
void database::remove_inactive_son_down_proposals( const vector<son_id_type>& son_ids_to_remove )
|
||||
{
|
||||
const auto& son_proposal_idx = get_index_type<son_proposal_index>().indices().get< by_id >();
|
||||
std::vector<proposal_id_type> proposals_to_remove;
|
||||
|
||||
for( auto& son_proposal : son_proposal_idx )
|
||||
{
|
||||
if(son_proposal.proposal_type == son_proposal_type::son_report_down_proposal)
|
||||
{
|
||||
auto it = std::find(son_ids_to_remove.begin(), son_ids_to_remove.end(), son_proposal.son_id);
|
||||
if (it != son_ids_to_remove.end())
|
||||
{
|
||||
ilog( "Removing inactive proposal ${p} for son ${s}", ("p", son_proposal.proposal_id) ("s",son_proposal.son_id));
|
||||
proposals_to_remove.push_back(son_proposal.proposal_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for( auto& proposal_id : proposals_to_remove )
|
||||
{
|
||||
const auto& proposal_obj = proposal_id(*this);
|
||||
remove_son_proposal(proposal_obj);
|
||||
remove(proposal_obj);
|
||||
}
|
||||
}
|
||||
|
||||
void database::remove_inactive_son_proposals( const vector<son_id_type>& son_ids_to_remove )
|
||||
{
|
||||
remove_inactive_son_down_proposals( son_ids_to_remove );
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@
|
|||
#include <graphene/chain/global_property_object.hpp>
|
||||
#include <graphene/chain/witness_object.hpp>
|
||||
#include <graphene/chain/witness_schedule_object.hpp>
|
||||
#include <graphene/chain/son_object.hpp>
|
||||
#include <graphene/chain/son_info.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
|
|
@ -72,6 +74,47 @@ witness_id_type database::get_scheduled_witness( uint32_t slot_num )const
|
|||
return wid;
|
||||
}
|
||||
|
||||
son_id_type database::get_scheduled_son( uint32_t slot_num )const
|
||||
{
|
||||
son_id_type sid;
|
||||
const global_property_object& gpo = get_global_properties();
|
||||
if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM)
|
||||
{
|
||||
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
|
||||
const son_schedule_object& sso = son_schedule_id_type()(*this);
|
||||
uint64_t current_aslot = dpo.current_aslot + slot_num;
|
||||
return sso.current_shuffled_sons[ current_aslot % sso.current_shuffled_sons.size() ];
|
||||
}
|
||||
if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM &&
|
||||
slot_num != 0 )
|
||||
{
|
||||
const son_schedule_object& sso = son_schedule_id_type()(*this);
|
||||
// ask the near scheduler who goes in the given slot
|
||||
bool slot_is_near = sso.scheduler.get_slot(slot_num-1, sid);
|
||||
if(! slot_is_near)
|
||||
{
|
||||
// if the near scheduler doesn't know, we have to extend it to
|
||||
// a far scheduler.
|
||||
// n.b. instantiating it is slow, but block gaps long enough to
|
||||
// need it are likely pretty rare.
|
||||
|
||||
witness_scheduler_rng far_rng(sso.rng_seed.begin(), GRAPHENE_FAR_SCHEDULE_CTR_IV);
|
||||
|
||||
far_future_son_scheduler far_scheduler =
|
||||
far_future_son_scheduler(sso.scheduler, far_rng);
|
||||
if(!far_scheduler.get_slot(slot_num-1, sid))
|
||||
{
|
||||
// no scheduled son -- somebody set up us the bomb
|
||||
// n.b. this code path is impossible, the present
|
||||
// implementation of far_future_son_scheduler
|
||||
// returns true unconditionally
|
||||
assert( false );
|
||||
}
|
||||
}
|
||||
}
|
||||
return sid;
|
||||
}
|
||||
|
||||
fc::time_point_sec database::get_slot_time(uint32_t slot_num)const
|
||||
{
|
||||
if( slot_num == 0 )
|
||||
|
|
@ -146,6 +189,41 @@ void database::update_witness_schedule()
|
|||
}
|
||||
}
|
||||
|
||||
void database::update_son_schedule()
|
||||
{
|
||||
const son_schedule_object& sso = son_schedule_id_type()(*this);
|
||||
const global_property_object& gpo = get_global_properties();
|
||||
|
||||
if( head_block_num() % gpo.active_sons.size() == 0 )
|
||||
{
|
||||
modify( sso, [&]( son_schedule_object& _sso )
|
||||
{
|
||||
_sso.current_shuffled_sons.clear();
|
||||
_sso.current_shuffled_sons.reserve( gpo.active_sons.size() );
|
||||
|
||||
for( const son_info& w : gpo.active_sons )
|
||||
_sso.current_shuffled_sons.push_back( w.son_id );
|
||||
|
||||
auto now_hi = uint64_t(head_block_time().sec_since_epoch()) << 32;
|
||||
for( uint32_t i = 0; i < _sso.current_shuffled_sons.size(); ++i )
|
||||
{
|
||||
/// High performance random generator
|
||||
/// http://xorshift.di.unimi.it/
|
||||
uint64_t k = now_hi + uint64_t(i)*2685821657736338717ULL;
|
||||
k ^= (k >> 12);
|
||||
k ^= (k << 25);
|
||||
k ^= (k >> 27);
|
||||
k *= 2685821657736338717ULL;
|
||||
|
||||
uint32_t jmax = _sso.current_shuffled_sons.size() - i;
|
||||
uint32_t j = i + k%jmax;
|
||||
std::swap( _sso.current_shuffled_sons[i],
|
||||
_sso.current_shuffled_sons[j] );
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
vector<witness_id_type> database::get_near_witness_schedule()const
|
||||
{
|
||||
const witness_schedule_object& wso = witness_schedule_id_type()(*this);
|
||||
|
|
@ -226,6 +304,71 @@ void database::update_witness_schedule(const signed_block& next_block)
|
|||
idump( ( double(total_time/1000000.0)/calls) );
|
||||
}
|
||||
|
||||
void database::update_son_schedule(const signed_block& next_block)
|
||||
{
|
||||
auto start = fc::time_point::now();
|
||||
const global_property_object& gpo = get_global_properties();
|
||||
const son_schedule_object& sso = get(son_schedule_id_type());
|
||||
uint32_t schedule_needs_filled = gpo.active_sons.size();
|
||||
uint32_t schedule_slot = get_slot_at_time(next_block.timestamp);
|
||||
|
||||
// We shouldn't be able to generate _pending_block with timestamp
|
||||
// in the past, and incoming blocks from the network with timestamp
|
||||
// in the past shouldn't be able to make it this far without
|
||||
// triggering FC_ASSERT elsewhere
|
||||
|
||||
assert( schedule_slot > 0 );
|
||||
|
||||
son_id_type first_son;
|
||||
bool slot_is_near = sso.scheduler.get_slot( schedule_slot-1, first_son );
|
||||
|
||||
son_id_type son;
|
||||
|
||||
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
|
||||
|
||||
assert( dpo.random.data_size() == witness_scheduler_rng::seed_length );
|
||||
assert( witness_scheduler_rng::seed_length == sso.rng_seed.size() );
|
||||
|
||||
modify(sso, [&](son_schedule_object& _sso)
|
||||
{
|
||||
_sso.slots_since_genesis += schedule_slot;
|
||||
witness_scheduler_rng rng(sso.rng_seed.data, _sso.slots_since_genesis);
|
||||
|
||||
_sso.scheduler._min_token_count = std::max(int(gpo.active_sons.size()) / 2, 1);
|
||||
|
||||
if( slot_is_near )
|
||||
{
|
||||
uint32_t drain = schedule_slot;
|
||||
while( drain > 0 )
|
||||
{
|
||||
if( _sso.scheduler.size() == 0 )
|
||||
break;
|
||||
_sso.scheduler.consume_schedule();
|
||||
--drain;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_sso.scheduler.reset_schedule( first_son );
|
||||
}
|
||||
while( !_sso.scheduler.get_slot(schedule_needs_filled, son) )
|
||||
{
|
||||
if( _sso.scheduler.produce_schedule(rng) & emit_turn )
|
||||
memcpy(_sso.rng_seed.begin(), dpo.random.data(), dpo.random.data_size());
|
||||
}
|
||||
_sso.last_scheduling_block = next_block.block_num();
|
||||
_sso.recent_slots_filled = (
|
||||
(_sso.recent_slots_filled << 1)
|
||||
+ 1) << (schedule_slot - 1);
|
||||
});
|
||||
auto end = fc::time_point::now();
|
||||
static uint64_t total_time = 0;
|
||||
static uint64_t calls = 0;
|
||||
total_time += (end - start).count();
|
||||
if( ++calls % 1000 == 0 )
|
||||
idump( ( double(total_time/1000000.0)/calls) );
|
||||
}
|
||||
|
||||
uint32_t database::update_witness_missed_blocks( const signed_block& b )
|
||||
{
|
||||
uint32_t missed_blocks = get_slot_at_time( b.timestamp );
|
||||
|
|
|
|||
|
|
@ -108,6 +108,11 @@ fc::variant_object get_config()
|
|||
result[ "GRAPHENE_RELAXED_COMMITTEE_ACCOUNT" ] = fc::variant(GRAPHENE_RELAXED_COMMITTEE_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS);
|
||||
result[ "GRAPHENE_NULL_ACCOUNT" ] = fc::variant(GRAPHENE_NULL_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS);
|
||||
result[ "GRAPHENE_TEMP_ACCOUNT" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS);
|
||||
result[ "GRAPHENE_PROXY_TO_SELF_ACCOUNT" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS);
|
||||
result[ "GRAPHENE_RAKE_FEE_ACCOUNT_ID" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS);
|
||||
result[ "GRAPHENE_NULL_WITNESS" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS);
|
||||
result[ "GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS);
|
||||
result[ "GRAPHENE_DEFAULT_RAKE_FEE_PERCENTAGE" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// SON HARDFORK Monday, September 21, 2020 1:43:11 PM
|
||||
// SON HARDFORK Wednesday, January 1, 2020 12:00:00 AM - 1577836800
|
||||
// SON HARDFORK Monday, September 21, 2020 1:43:11 PM - 1600695791
|
||||
#ifndef HARDFORK_SON_TIME
|
||||
#include <ctime>
|
||||
#define HARDFORK_SON_TIME (fc::time_point_sec( time(NULL) - (60 * 60) ))
|
||||
#define HARDFORK_SON_TIME (fc::time_point_sec( 1577836800 ))
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -175,8 +175,6 @@
|
|||
#define GRAPHENE_PROXY_TO_SELF_ACCOUNT (graphene::chain::account_id_type(5))
|
||||
///
|
||||
#define GRAPHENE_RAKE_FEE_ACCOUNT_ID (graphene::chain::account_id_type(6))
|
||||
///
|
||||
#define GRAPHENE_SON_ACCOUNT_ID (graphene::chain::account_id_type(7))
|
||||
/// Sentinel value used in the scheduler.
|
||||
#define GRAPHENE_NULL_WITNESS (graphene::chain::witness_id_type(0))
|
||||
///@}
|
||||
|
|
@ -234,7 +232,10 @@
|
|||
#define MIN_SON_MEMBER_COUNT 15
|
||||
#define SON_VESTING_AMOUNT (50*GRAPHENE_BLOCKCHAIN_PRECISION) // 50 PPY
|
||||
#define SON_VESTING_PERIOD (60*60*24*2) // 2 days
|
||||
#define SON_DEREGISTER_TIME (12) // 12 Hours
|
||||
#define SON_DEREGISTER_TIME (60*60*12) // 12 Hours in seconds
|
||||
#define SON_HEARTBEAT_FREQUENCY (60*3) // 3 minutes in seconds
|
||||
#define SON_DOWN_TIME (60*3*2) // 2 Heartbeats in seconds
|
||||
#define SON_PAY_TIME (60*60*24) // 1 day
|
||||
#define MIN_SON_PAY_DAILY_MAX (GRAPHENE_BLOCKCHAIN_PRECISION * int64_t(200))
|
||||
#define SWEEPS_DEFAULT_DISTRIBUTION_PERCENTAGE (2*GRAPHENE_1_PERCENT)
|
||||
#define SWEEPS_DEFAULT_DISTRIBUTION_ASSET (graphene::chain::asset_id_type(0))
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@
|
|||
|
||||
#include <graphene/chain/protocol/protocol.hpp>
|
||||
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
|
||||
#include <fc/log/logger.hpp>
|
||||
|
||||
#include <map>
|
||||
|
|
@ -240,6 +242,22 @@ namespace graphene { namespace chain {
|
|||
*/
|
||||
witness_id_type get_scheduled_witness(uint32_t slot_num)const;
|
||||
|
||||
/**
|
||||
* @brief Get the son scheduled for block production in a slot.
|
||||
*
|
||||
* slot_num always corresponds to a time in the future.
|
||||
*
|
||||
* If slot_num == 1, returns the next scheduled son.
|
||||
* If slot_num == 2, returns the next scheduled son after
|
||||
* 1 block gap.
|
||||
*
|
||||
* Use the get_slot_time() and get_slot_at_time() functions
|
||||
* to convert between slot_num and timestamp.
|
||||
*
|
||||
* Passing slot_num == 0 returns GRAPHENE_NULL_WITNESS
|
||||
*/
|
||||
son_id_type get_scheduled_son(uint32_t slot_num)const;
|
||||
|
||||
/**
|
||||
* Get the time at which the given slot occurs.
|
||||
*
|
||||
|
|
@ -263,6 +281,8 @@ namespace graphene { namespace chain {
|
|||
vector<witness_id_type> get_near_witness_schedule()const;
|
||||
void update_witness_schedule();
|
||||
void update_witness_schedule(const signed_block& next_block);
|
||||
void update_son_schedule();
|
||||
void update_son_schedule(const signed_block& next_block);
|
||||
|
||||
void check_lottery_end_by_participants( asset_id_type asset_id );
|
||||
void check_ending_lotteries();
|
||||
|
|
@ -280,12 +300,11 @@ namespace graphene { namespace chain {
|
|||
std::vector<uint32_t> get_seeds( asset_id_type for_asset, uint8_t count_winners )const;
|
||||
uint64_t get_random_bits( uint64_t bound );
|
||||
std::set<son_id_type> get_sons_being_deregistered();
|
||||
std::set<son_id_type> get_sons_being_reported_down();
|
||||
std::set<son_id_type> get_sons_to_be_deregistered();
|
||||
fc::optional<operation> create_son_deregister_proposal(const son_id_type& son_id, const witness_object& current_witness );
|
||||
fc::optional<operation> create_son_deregister_proposal( son_id_type son_id, account_id_type paying_son );
|
||||
signed_transaction create_signed_transaction( const fc::ecc::private_key& signing_private_key, const operation& op );
|
||||
void process_son_proposals( const witness_object& current_witness, const fc::ecc::private_key& private_key );
|
||||
void remove_son_proposal( const proposal_object& proposal );
|
||||
bool is_son_dereg_valid( const son_id_type& son_id );
|
||||
bool is_son_dereg_valid( son_id_type son_id );
|
||||
|
||||
time_point_sec head_block_time()const;
|
||||
uint32_t head_block_num()const;
|
||||
|
|
@ -527,7 +546,13 @@ namespace graphene { namespace chain {
|
|||
void perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props);
|
||||
void update_active_witnesses();
|
||||
void update_active_committee_members();
|
||||
void update_son_metrics( const vector<son_info>& curr_active_sons );
|
||||
void update_active_sons();
|
||||
void remove_son_proposal( const proposal_object& proposal );
|
||||
void remove_inactive_son_down_proposals( const vector<son_id_type>& son_ids_to_remove );
|
||||
void remove_inactive_son_proposals( const vector<son_id_type>& son_ids_to_remove );
|
||||
void update_son_statuses( const vector<son_info>& cur_active_sons, const vector<son_info>& new_active_sons );
|
||||
void update_son_wallet( const vector<son_info>& new_active_sons );
|
||||
void update_worker_votes();
|
||||
|
||||
template<class... Types>
|
||||
|
|
@ -582,6 +607,13 @@ namespace graphene { namespace chain {
|
|||
* database::close() has not been called, or failed during execution.
|
||||
*/
|
||||
bool _opened = false;
|
||||
|
||||
/////////////////////// db_sidechain.cpp ////////////////////
|
||||
public:
|
||||
bool recreate_primary_wallet;
|
||||
void initialize_db_sidechain();
|
||||
protected:
|
||||
private:
|
||||
};
|
||||
|
||||
namespace detail
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include <graphene/chain/protocol/chain_parameters.hpp>
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/son_info.hpp>
|
||||
#include <graphene/db/object.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
|
@ -51,7 +52,7 @@ namespace graphene { namespace chain {
|
|||
uint32_t next_available_vote_id = 0;
|
||||
vector<committee_member_id_type> active_committee_members; // updated once per maintenance interval
|
||||
flat_set<witness_id_type> active_witnesses; // updated once per maintenance interval
|
||||
vector<son_id_type> active_sons; // updated once per maintenance interval
|
||||
vector<son_info> active_sons; // updated once per maintenance interval
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -78,7 +79,7 @@ namespace graphene { namespace chain {
|
|||
time_point_sec last_budget_time;
|
||||
share_type witness_budget;
|
||||
//Last SON Payout time, it can be different to the maintenance interval time
|
||||
time_point_sec last_son_payout_time;
|
||||
time_point_sec last_son_payout_time = HARDFORK_SON_TIME;
|
||||
share_type son_budget = 0;
|
||||
uint32_t accounts_registered_this_interval = 0;
|
||||
/**
|
||||
|
|
@ -137,6 +138,8 @@ FC_REFLECT_DERIVED( graphene::chain::dynamic_global_property_object, (graphene::
|
|||
(next_maintenance_time)
|
||||
(last_budget_time)
|
||||
(witness_budget)
|
||||
(last_son_payout_time)
|
||||
(son_budget)
|
||||
(accounts_registered_this_interval)
|
||||
(recently_missed_count)
|
||||
(current_aslot)
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ namespace graphene { namespace chain {
|
|||
void operator()( const T &v ) const {}
|
||||
|
||||
void operator()( const son_delete_operation &v );
|
||||
void operator()( const son_report_down_operation &v );
|
||||
};
|
||||
|
||||
class proposal_create_evaluator : public evaluator<proposal_create_evaluator>
|
||||
|
|
|
|||
|
|
@ -46,6 +46,11 @@ namespace graphene { namespace chain {
|
|||
optional < uint32_t > son_vesting_amount;
|
||||
optional < uint32_t > son_vesting_period;
|
||||
optional < uint32_t > son_pay_daily_max;
|
||||
optional < uint32_t > son_pay_time;
|
||||
optional < uint32_t > son_deregister_time;
|
||||
optional < uint32_t > son_heartbeat_frequency;
|
||||
optional < uint32_t > son_down_time;
|
||||
optional < account_id_type > son_account;
|
||||
};
|
||||
|
||||
struct chain_parameters
|
||||
|
|
@ -138,6 +143,21 @@ namespace graphene { namespace chain {
|
|||
inline uint16_t son_pay_daily_max()const {
|
||||
return extensions.value.son_pay_daily_max.valid() ? *extensions.value.son_pay_daily_max : MIN_SON_PAY_DAILY_MAX;
|
||||
}
|
||||
inline uint16_t son_pay_time()const {
|
||||
return extensions.value.son_pay_time.valid() ? *extensions.value.son_pay_time : SON_PAY_TIME;
|
||||
}
|
||||
inline uint16_t son_deregister_time()const {
|
||||
return extensions.value.son_deregister_time.valid() ? *extensions.value.son_deregister_time : SON_DEREGISTER_TIME;
|
||||
}
|
||||
inline uint16_t son_heartbeat_frequency()const {
|
||||
return extensions.value.son_heartbeat_frequency.valid() ? *extensions.value.son_heartbeat_frequency : SON_HEARTBEAT_FREQUENCY;
|
||||
}
|
||||
inline uint16_t son_down_time()const {
|
||||
return extensions.value.son_down_time.valid() ? *extensions.value.son_down_time : SON_DOWN_TIME;
|
||||
}
|
||||
inline account_id_type son_account() const {
|
||||
return extensions.value.son_account.valid() ? *extensions.value.son_account : GRAPHENE_NULL_ACCOUNT;
|
||||
}
|
||||
};
|
||||
|
||||
} } // graphene::chain
|
||||
|
|
@ -155,6 +175,11 @@ FC_REFLECT( graphene::chain::parameter_extension,
|
|||
(son_vesting_amount)
|
||||
(son_vesting_period)
|
||||
(son_pay_daily_max)
|
||||
(son_pay_time)
|
||||
(son_deregister_time)
|
||||
(son_heartbeat_frequency)
|
||||
(son_down_time)
|
||||
(son_account)
|
||||
)
|
||||
|
||||
FC_REFLECT( graphene::chain::chain_parameters,
|
||||
|
|
|
|||
|
|
@ -46,6 +46,11 @@
|
|||
#include <graphene/chain/protocol/betting_market.hpp>
|
||||
#include <graphene/chain/protocol/tournament.hpp>
|
||||
#include <graphene/chain/protocol/son.hpp>
|
||||
#include <graphene/chain/protocol/sidechain_address.hpp>
|
||||
#include <graphene/chain/protocol/son_wallet.hpp>
|
||||
#include <graphene/chain/protocol/son_wallet_deposit.hpp>
|
||||
#include <graphene/chain/protocol/son_wallet_withdraw.hpp>
|
||||
#include <graphene/chain/protocol/sidechain_transaction.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
|
|
@ -139,7 +144,22 @@ namespace graphene { namespace chain {
|
|||
sweeps_vesting_claim_operation,
|
||||
son_create_operation,
|
||||
son_update_operation,
|
||||
son_delete_operation
|
||||
son_delete_operation,
|
||||
son_heartbeat_operation,
|
||||
son_report_down_operation,
|
||||
son_maintenance_operation,
|
||||
son_wallet_recreate_operation,
|
||||
son_wallet_update_operation,
|
||||
son_wallet_deposit_create_operation,
|
||||
son_wallet_deposit_process_operation,
|
||||
son_wallet_withdraw_create_operation,
|
||||
son_wallet_withdraw_process_operation,
|
||||
sidechain_address_add_operation,
|
||||
sidechain_address_update_operation,
|
||||
sidechain_address_delete_operation,
|
||||
sidechain_transaction_create_operation,
|
||||
sidechain_transaction_sign_operation,
|
||||
sidechain_transaction_send_operation
|
||||
> operation;
|
||||
|
||||
/// @} // operations group
|
||||
|
|
|
|||
|
|
@ -0,0 +1,64 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/base.hpp>
|
||||
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
struct sidechain_address_add_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
account_id_type sidechain_address_account;
|
||||
graphene::peerplays_sidechain::sidechain_type sidechain;
|
||||
string deposit_address;
|
||||
string withdraw_address;
|
||||
|
||||
account_id_type fee_payer()const { return sidechain_address_account; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
struct sidechain_address_update_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
sidechain_address_id_type sidechain_address_id;
|
||||
account_id_type sidechain_address_account;
|
||||
graphene::peerplays_sidechain::sidechain_type sidechain;
|
||||
optional<string> deposit_address;
|
||||
optional<string> withdraw_address;
|
||||
|
||||
account_id_type fee_payer()const { return sidechain_address_account; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
struct sidechain_address_delete_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
sidechain_address_id_type sidechain_address_id;
|
||||
account_id_type sidechain_address_account;
|
||||
graphene::peerplays_sidechain::sidechain_type sidechain;
|
||||
|
||||
account_id_type fee_payer()const { return sidechain_address_account; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
} } // namespace graphene::chain
|
||||
|
||||
FC_REFLECT(graphene::chain::sidechain_address_add_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::sidechain_address_add_operation, (fee)
|
||||
(sidechain_address_account)(sidechain)(deposit_address)(withdraw_address) )
|
||||
|
||||
FC_REFLECT(graphene::chain::sidechain_address_update_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::sidechain_address_update_operation, (fee)
|
||||
(sidechain_address_id)
|
||||
(sidechain_address_account)(sidechain)(deposit_address)(withdraw_address) )
|
||||
|
||||
FC_REFLECT(graphene::chain::sidechain_address_delete_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::sidechain_address_delete_operation, (fee)
|
||||
(sidechain_address_id)
|
||||
(sidechain_address_account)(sidechain) )
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/base.hpp>
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
struct sidechain_transaction_create_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
|
||||
sidechain_type sidechain;
|
||||
object_id_type object_id;
|
||||
std::string transaction;
|
||||
std::vector<son_id_type> signers;
|
||||
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
struct sidechain_transaction_sign_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
|
||||
sidechain_transaction_id_type sidechain_transaction_id;
|
||||
std::string transaction;
|
||||
block_id_type block;
|
||||
bool complete;
|
||||
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
share_type calculate_fee( const fee_parameters_type& k )const { return 0; }
|
||||
};
|
||||
|
||||
struct sidechain_transaction_send_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
|
||||
sidechain_transaction_id_type sidechain_transaction_id;
|
||||
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
share_type calculate_fee( const fee_parameters_type& k )const { return 0; }
|
||||
};
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT( graphene::chain::sidechain_transaction_create_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::sidechain_transaction_create_operation, (fee)(payer)
|
||||
(sidechain)
|
||||
(object_id)
|
||||
(transaction)
|
||||
(signers) )
|
||||
|
||||
FC_REFLECT( graphene::chain::sidechain_transaction_sign_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::sidechain_transaction_sign_operation, (fee)(payer)
|
||||
(sidechain_transaction_id)
|
||||
(transaction)
|
||||
(block)
|
||||
(complete) )
|
||||
|
||||
FC_REFLECT( graphene::chain::sidechain_transaction_send_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::sidechain_transaction_send_operation, (fee)(payer)
|
||||
(sidechain_transaction_id) )
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/base.hpp>
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
|
|
@ -12,6 +13,7 @@ namespace graphene { namespace chain {
|
|||
std::string url;
|
||||
vesting_balance_id_type deposit;
|
||||
public_key_type signing_key;
|
||||
flat_map<peerplays_sidechain::sidechain_type, string> sidechain_public_keys;
|
||||
vesting_balance_id_type pay_vb;
|
||||
|
||||
account_id_type fee_payer()const { return owner_account; }
|
||||
|
|
@ -28,6 +30,7 @@ namespace graphene { namespace chain {
|
|||
optional<std::string> new_url;
|
||||
optional<vesting_balance_id_type> new_deposit;
|
||||
optional<public_key_type> new_signing_key;
|
||||
optional<flat_map<peerplays_sidechain::sidechain_type, string>> new_sidechain_public_keys;
|
||||
optional<vesting_balance_id_type> new_pay_vb;
|
||||
|
||||
account_id_type fee_payer()const { return owner_account; }
|
||||
|
|
@ -47,15 +50,71 @@ namespace graphene { namespace chain {
|
|||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
struct son_heartbeat_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
son_id_type son_id;
|
||||
account_id_type owner_account;
|
||||
time_point_sec ts;
|
||||
|
||||
account_id_type fee_payer()const { return owner_account; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
|
||||
struct son_report_down_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
son_id_type son_id;
|
||||
account_id_type payer;
|
||||
time_point_sec down_ts;
|
||||
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
enum class son_maintenance_request_type
|
||||
{
|
||||
request_maintenance,
|
||||
cancel_request_maintenance
|
||||
};
|
||||
|
||||
struct son_maintenance_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
son_id_type son_id;
|
||||
account_id_type owner_account;
|
||||
son_maintenance_request_type request_type = son_maintenance_request_type::request_maintenance;
|
||||
|
||||
account_id_type fee_payer()const { return owner_account; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
} } // namespace graphene::chain
|
||||
|
||||
FC_REFLECT(graphene::chain::son_create_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::son_create_operation, (fee)(owner_account)(url)(deposit)(signing_key)
|
||||
FC_REFLECT(graphene::chain::son_create_operation, (fee)(owner_account)(url)(deposit)(signing_key)(sidechain_public_keys)
|
||||
(pay_vb) )
|
||||
|
||||
FC_REFLECT(graphene::chain::son_update_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::son_update_operation, (fee)(son_id)(owner_account)(new_url)(new_deposit)
|
||||
(new_signing_key)(new_pay_vb) )
|
||||
(new_signing_key)(new_sidechain_public_keys)(new_pay_vb) )
|
||||
|
||||
FC_REFLECT(graphene::chain::son_delete_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::son_delete_operation, (fee)(son_id)(payer)(owner_account) )
|
||||
|
||||
FC_REFLECT(graphene::chain::son_heartbeat_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::son_heartbeat_operation, (fee)(son_id)(owner_account)(ts) )
|
||||
|
||||
FC_REFLECT(graphene::chain::son_report_down_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::son_report_down_operation, (fee)(son_id)(payer)(down_ts) )
|
||||
|
||||
FC_REFLECT_ENUM( graphene::chain::son_maintenance_request_type, (request_maintenance)(cancel_request_maintenance) )
|
||||
FC_REFLECT(graphene::chain::son_maintenance_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::son_maintenance_operation, (fee)(son_id)(owner_account)(request_type) )
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/base.hpp>
|
||||
#include <graphene/chain/son_info.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
struct son_wallet_recreate_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
|
||||
vector<son_info> sons;
|
||||
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
struct son_wallet_update_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
|
||||
son_wallet_id_type son_wallet_id;
|
||||
graphene::peerplays_sidechain::sidechain_type sidechain;
|
||||
string address;
|
||||
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
} } // namespace graphene::chain
|
||||
|
||||
FC_REFLECT(graphene::chain::son_wallet_recreate_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::son_wallet_recreate_operation, (fee)(payer)(sons) )
|
||||
FC_REFLECT(graphene::chain::son_wallet_update_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::son_wallet_update_operation, (fee)(payer)(son_wallet_id)(sidechain)(address) )
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/base.hpp>
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
|
||||
#include <fc/safe.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
struct son_wallet_deposit_create_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
|
||||
son_id_type son_id;
|
||||
fc::time_point_sec timestamp;
|
||||
sidechain_type sidechain;
|
||||
std::string sidechain_uid;
|
||||
std::string sidechain_transaction_id;
|
||||
std::string sidechain_from;
|
||||
std::string sidechain_to;
|
||||
std::string sidechain_currency;
|
||||
fc::safe<int64_t> sidechain_amount;
|
||||
chain::account_id_type peerplays_from;
|
||||
chain::account_id_type peerplays_to;
|
||||
chain::asset peerplays_asset;
|
||||
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
struct son_wallet_deposit_process_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
|
||||
son_wallet_deposit_id_type son_wallet_deposit_id;
|
||||
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
} } // namespace graphene::chain
|
||||
|
||||
FC_REFLECT(graphene::chain::son_wallet_deposit_create_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::son_wallet_deposit_create_operation, (fee)(payer)
|
||||
(son_id) (timestamp) (sidechain)
|
||||
(sidechain_uid) (sidechain_transaction_id) (sidechain_from) (sidechain_to) (sidechain_currency) (sidechain_amount)
|
||||
(peerplays_from) (peerplays_to) (peerplays_asset) )
|
||||
|
||||
FC_REFLECT(graphene::chain::son_wallet_deposit_process_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::son_wallet_deposit_process_operation, (fee)(payer)
|
||||
(son_wallet_deposit_id) )
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/base.hpp>
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
|
||||
#include <fc/safe.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
struct son_wallet_withdraw_create_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
|
||||
son_id_type son_id;
|
||||
fc::time_point_sec timestamp;
|
||||
sidechain_type sidechain;
|
||||
std::string peerplays_uid;
|
||||
std::string peerplays_transaction_id;
|
||||
chain::account_id_type peerplays_from;
|
||||
chain::asset peerplays_asset;
|
||||
sidechain_type withdraw_sidechain;
|
||||
std::string withdraw_address;
|
||||
std::string withdraw_currency;
|
||||
safe<int64_t> withdraw_amount;
|
||||
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
struct son_wallet_withdraw_process_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
|
||||
son_wallet_withdraw_id_type son_wallet_withdraw_id;
|
||||
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
} } // namespace graphene::chain
|
||||
|
||||
FC_REFLECT(graphene::chain::son_wallet_withdraw_create_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::son_wallet_withdraw_create_operation, (fee)(payer)
|
||||
(son_id) (timestamp) (sidechain)
|
||||
(peerplays_uid) (peerplays_transaction_id) (peerplays_from) (peerplays_asset)
|
||||
(withdraw_sidechain) (withdraw_address) (withdraw_currency) (withdraw_amount) )
|
||||
|
||||
FC_REFLECT(graphene::chain::son_wallet_withdraw_process_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::son_wallet_withdraw_process_operation, (fee)(payer)
|
||||
(son_wallet_withdraw_id) )
|
||||
|
|
@ -147,6 +147,11 @@ namespace graphene { namespace chain {
|
|||
bet_object_type,
|
||||
son_object_type,
|
||||
son_proposal_object_type,
|
||||
son_wallet_object_type,
|
||||
son_wallet_deposit_object_type,
|
||||
son_wallet_withdraw_object_type,
|
||||
sidechain_address_object_type,
|
||||
sidechain_transaction_object_type,
|
||||
OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types
|
||||
};
|
||||
|
||||
|
|
@ -176,7 +181,8 @@ namespace graphene { namespace chain {
|
|||
impl_global_betting_statistics_object_type,
|
||||
impl_lottery_balance_object_type,
|
||||
impl_sweeps_vesting_balance_object_type,
|
||||
impl_son_statistics_object_type
|
||||
impl_son_statistics_object_type,
|
||||
impl_son_schedule_object_type
|
||||
};
|
||||
|
||||
//typedef fc::unsigned_int object_id_type;
|
||||
|
|
@ -209,6 +215,11 @@ namespace graphene { namespace chain {
|
|||
class bet_object;
|
||||
class son_object;
|
||||
class son_proposal_object;
|
||||
class son_wallet_object;
|
||||
class son_wallet_deposit_object;
|
||||
class son_wallet_withdraw_object;
|
||||
class sidechain_address_object;
|
||||
class sidechain_transaction_object;
|
||||
|
||||
typedef object_id< protocol_ids, account_object_type, account_object> account_id_type;
|
||||
typedef object_id< protocol_ids, asset_object_type, asset_object> asset_id_type;
|
||||
|
|
@ -237,6 +248,11 @@ namespace graphene { namespace chain {
|
|||
typedef object_id< protocol_ids, bet_object_type, bet_object> bet_id_type;
|
||||
typedef object_id< protocol_ids, son_object_type, son_object> son_id_type;
|
||||
typedef object_id< protocol_ids, son_proposal_object_type, son_proposal_object> son_proposal_id_type;
|
||||
typedef object_id< protocol_ids, son_wallet_object_type, son_wallet_object> son_wallet_id_type;
|
||||
typedef object_id< protocol_ids, son_wallet_deposit_object_type, son_wallet_deposit_object> son_wallet_deposit_id_type;
|
||||
typedef object_id< protocol_ids, son_wallet_withdraw_object_type, son_wallet_withdraw_object> son_wallet_withdraw_id_type;
|
||||
typedef object_id< protocol_ids, sidechain_address_object_type, sidechain_address_object> sidechain_address_id_type;
|
||||
typedef object_id< protocol_ids, sidechain_transaction_object_type,sidechain_transaction_object> sidechain_transaction_id_type;
|
||||
|
||||
// implementation types
|
||||
class global_property_object;
|
||||
|
|
@ -261,6 +277,7 @@ namespace graphene { namespace chain {
|
|||
class lottery_balance_object;
|
||||
class sweeps_vesting_balance_object;
|
||||
class son_statistics_object;
|
||||
class son_schedule_object;
|
||||
|
||||
typedef object_id< implementation_ids, impl_global_property_object_type, global_property_object> global_property_id_type;
|
||||
typedef object_id< implementation_ids, impl_dynamic_global_property_object_type, dynamic_global_property_object> dynamic_global_property_id_type;
|
||||
|
|
@ -290,6 +307,7 @@ namespace graphene { namespace chain {
|
|||
typedef object_id< implementation_ids, impl_lottery_balance_object_type, lottery_balance_object > lottery_balance_id_type;
|
||||
typedef object_id< implementation_ids, impl_sweeps_vesting_balance_object_type, sweeps_vesting_balance_object> sweeps_vesting_balance_id_type;
|
||||
typedef object_id< implementation_ids, impl_son_statistics_object_type, son_statistics_object > son_statistics_id_type;
|
||||
typedef object_id< implementation_ids, impl_son_schedule_object_type, son_schedule_object> son_schedule_id_type;
|
||||
|
||||
typedef fc::array<char, GRAPHENE_MAX_ASSET_SYMBOL_LENGTH> symbol_type;
|
||||
typedef fc::ripemd160 block_id_type;
|
||||
|
|
@ -421,6 +439,11 @@ FC_REFLECT_ENUM( graphene::chain::object_type,
|
|||
(bet_object_type)
|
||||
(son_object_type)
|
||||
(son_proposal_object_type)
|
||||
(son_wallet_object_type)
|
||||
(son_wallet_deposit_object_type)
|
||||
(son_wallet_withdraw_object_type)
|
||||
(sidechain_address_object_type)
|
||||
(sidechain_transaction_object_type)
|
||||
(OBJECT_TYPE_COUNT)
|
||||
)
|
||||
FC_REFLECT_ENUM( graphene::chain::impl_object_type,
|
||||
|
|
@ -449,6 +472,7 @@ FC_REFLECT_ENUM( graphene::chain::impl_object_type,
|
|||
(impl_lottery_balance_object_type)
|
||||
(impl_sweeps_vesting_balance_object_type)
|
||||
(impl_son_statistics_object_type)
|
||||
(impl_son_schedule_object_type)
|
||||
)
|
||||
|
||||
FC_REFLECT_TYPENAME( graphene::chain::share_type )
|
||||
|
|
@ -493,6 +517,11 @@ FC_REFLECT_TYPENAME( graphene::chain::global_betting_statistics_id_type )
|
|||
FC_REFLECT_TYPENAME( graphene::chain::tournament_details_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::son_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::son_proposal_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::son_wallet_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::son_wallet_deposit_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::son_wallet_withdraw_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::sidechain_address_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::sidechain_transaction_id_type )
|
||||
|
||||
|
||||
FC_REFLECT( graphene::chain::void_t, )
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/evaluator.hpp>
|
||||
#include <graphene/chain/protocol/sidechain_address.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class add_sidechain_address_evaluator : public evaluator<add_sidechain_address_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef sidechain_address_add_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const sidechain_address_add_operation& o);
|
||||
object_id_type do_apply(const sidechain_address_add_operation& o);
|
||||
};
|
||||
|
||||
class update_sidechain_address_evaluator : public evaluator<update_sidechain_address_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef sidechain_address_update_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const sidechain_address_update_operation& o);
|
||||
object_id_type do_apply(const sidechain_address_update_operation& o);
|
||||
};
|
||||
|
||||
class delete_sidechain_address_evaluator : public evaluator<delete_sidechain_address_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef sidechain_address_delete_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const sidechain_address_delete_operation& o);
|
||||
void_result do_apply(const sidechain_address_delete_operation& o);
|
||||
};
|
||||
|
||||
} } // namespace graphene::chain
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/db/object.hpp>
|
||||
#include <graphene/db/generic_index.hpp>
|
||||
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
#include <boost/multi_index/composite_key.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
using namespace graphene::db;
|
||||
|
||||
/**
|
||||
* @class sidechain_address_object
|
||||
* @brief tracks information about a sidechain addresses for user accounts.
|
||||
* @ingroup object
|
||||
*/
|
||||
class sidechain_address_object : public abstract_object<sidechain_address_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = sidechain_address_object_type;
|
||||
|
||||
account_id_type sidechain_address_account;
|
||||
graphene::peerplays_sidechain::sidechain_type sidechain;
|
||||
string deposit_address;
|
||||
string withdraw_address;
|
||||
|
||||
sidechain_address_object() :
|
||||
sidechain(graphene::peerplays_sidechain::sidechain_type::bitcoin),
|
||||
deposit_address(""),
|
||||
withdraw_address("") {}
|
||||
};
|
||||
|
||||
struct by_account;
|
||||
struct by_sidechain;
|
||||
struct by_account_and_sidechain;
|
||||
struct by_sidechain_and_deposit_address;
|
||||
using sidechain_address_multi_index_type = multi_index_container<
|
||||
sidechain_address_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>,
|
||||
member<object, object_id_type, &object::id>
|
||||
>,
|
||||
ordered_non_unique< tag<by_account>,
|
||||
member<sidechain_address_object, account_id_type, &sidechain_address_object::sidechain_address_account>
|
||||
>,
|
||||
ordered_non_unique< tag<by_sidechain>,
|
||||
member<sidechain_address_object, peerplays_sidechain::sidechain_type, &sidechain_address_object::sidechain>
|
||||
>,
|
||||
ordered_unique< tag<by_account_and_sidechain>,
|
||||
composite_key<sidechain_address_object,
|
||||
member<sidechain_address_object, account_id_type, &sidechain_address_object::sidechain_address_account>,
|
||||
member<sidechain_address_object, peerplays_sidechain::sidechain_type, &sidechain_address_object::sidechain>
|
||||
>
|
||||
>,
|
||||
ordered_unique< tag<by_sidechain_and_deposit_address>,
|
||||
composite_key<sidechain_address_object,
|
||||
member<sidechain_address_object, peerplays_sidechain::sidechain_type, &sidechain_address_object::sidechain>,
|
||||
member<sidechain_address_object, std::string, &sidechain_address_object::deposit_address>
|
||||
>
|
||||
>
|
||||
>
|
||||
>;
|
||||
using sidechain_address_index = generic_index<sidechain_address_object, sidechain_address_multi_index_type>;
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::sidechain_address_object, (graphene::db::object),
|
||||
(sidechain_address_account) (sidechain) (deposit_address) (withdraw_address) )
|
||||
26
libraries/chain/include/graphene/chain/sidechain_defs.hpp
Normal file
26
libraries/chain/include/graphene/chain/sidechain_defs.hpp
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
#include <fc/reflect/reflect.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
enum class sidechain_type {
|
||||
bitcoin,
|
||||
ethereum,
|
||||
eos,
|
||||
peerplays
|
||||
};
|
||||
|
||||
} }
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
using sidechain_type = graphene::chain::sidechain_type;
|
||||
|
||||
} }
|
||||
|
||||
FC_REFLECT_ENUM(graphene::chain::sidechain_type,
|
||||
(bitcoin)
|
||||
(ethereum)
|
||||
(eos)
|
||||
(peerplays) )
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/evaluator.hpp>
|
||||
#include <graphene/chain/protocol/sidechain_transaction.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class sidechain_transaction_create_evaluator : public evaluator<sidechain_transaction_create_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef sidechain_transaction_create_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const sidechain_transaction_create_operation& o);
|
||||
object_id_type do_apply(const sidechain_transaction_create_operation& o);
|
||||
};
|
||||
|
||||
class sidechain_transaction_sign_evaluator : public evaluator<sidechain_transaction_sign_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef sidechain_transaction_sign_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const sidechain_transaction_sign_operation& o);
|
||||
object_id_type do_apply(const sidechain_transaction_sign_operation& o);
|
||||
};
|
||||
|
||||
class sidechain_transaction_send_evaluator : public evaluator<sidechain_transaction_send_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef sidechain_transaction_send_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const sidechain_transaction_send_operation& o);
|
||||
object_id_type do_apply(const sidechain_transaction_send_operation& o);
|
||||
};
|
||||
|
||||
} } // namespace graphene::chain
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
#pragma once
|
||||
#include <boost/multi_index/composite_key.hpp>
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
using namespace graphene::db;
|
||||
|
||||
/**
|
||||
* @class sidechain_transaction_object
|
||||
* @brief tracks state of sidechain transaction during signing process.
|
||||
* @ingroup object
|
||||
*/
|
||||
class sidechain_transaction_object : public abstract_object<sidechain_transaction_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = sidechain_transaction_object_type;
|
||||
|
||||
sidechain_type sidechain;
|
||||
object_id_type object_id;
|
||||
std::string transaction;
|
||||
std::vector<std::pair<son_id_type, bool>> signers;
|
||||
|
||||
block_id_type block;
|
||||
bool valid = false;
|
||||
bool complete = false;
|
||||
bool sent = false;
|
||||
};
|
||||
|
||||
struct by_object_id;
|
||||
struct by_sidechain_and_complete;
|
||||
struct by_sidechain_and_complete_and_sent;
|
||||
using sidechain_transaction_multi_index_type = multi_index_container<
|
||||
sidechain_transaction_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>,
|
||||
member<object, object_id_type, &object::id>
|
||||
>,
|
||||
ordered_unique< tag<by_object_id>,
|
||||
member<sidechain_transaction_object, object_id_type, &sidechain_transaction_object::object_id>
|
||||
>,
|
||||
ordered_non_unique< tag<by_sidechain_and_complete>,
|
||||
composite_key<sidechain_transaction_object,
|
||||
member<sidechain_transaction_object, sidechain_type, &sidechain_transaction_object::sidechain>,
|
||||
member<sidechain_transaction_object, bool, &sidechain_transaction_object::complete>
|
||||
>
|
||||
>,
|
||||
ordered_non_unique< tag<by_sidechain_and_complete_and_sent>,
|
||||
composite_key<sidechain_transaction_object,
|
||||
member<sidechain_transaction_object, sidechain_type, &sidechain_transaction_object::sidechain>,
|
||||
member<sidechain_transaction_object, bool, &sidechain_transaction_object::complete>,
|
||||
member<sidechain_transaction_object, bool, &sidechain_transaction_object::sent>
|
||||
>
|
||||
>
|
||||
>
|
||||
>;
|
||||
using sidechain_transaction_index = generic_index<sidechain_transaction_object, sidechain_transaction_multi_index_type>;
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::sidechain_transaction_object, (graphene::db::object ),
|
||||
(sidechain)
|
||||
(object_id)
|
||||
(transaction)
|
||||
(signers)
|
||||
(block)
|
||||
(valid)
|
||||
(complete)
|
||||
(sent) )
|
||||
|
|
@ -31,4 +31,31 @@ public:
|
|||
void_result do_apply(const son_delete_operation& o);
|
||||
};
|
||||
|
||||
class son_heartbeat_evaluator : public evaluator<son_heartbeat_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef son_heartbeat_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const son_heartbeat_operation& o);
|
||||
object_id_type do_apply(const son_heartbeat_operation& o);
|
||||
};
|
||||
|
||||
class son_report_down_evaluator : public evaluator<son_report_down_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef son_report_down_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const son_report_down_operation& o);
|
||||
object_id_type do_apply(const son_report_down_operation& o);
|
||||
};
|
||||
|
||||
class son_maintenance_evaluator : public evaluator<son_maintenance_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef son_maintenance_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const son_maintenance_operation& o);
|
||||
object_id_type do_apply(const son_maintenance_operation& o);
|
||||
};
|
||||
|
||||
} } // namespace graphene::chain
|
||||
|
|
|
|||
36
libraries/chain/include/graphene/chain/son_info.hpp
Normal file
36
libraries/chain/include/graphene/chain/son_info.hpp
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
using namespace graphene::db;
|
||||
|
||||
/**
|
||||
* @class son_info
|
||||
* @brief tracks information about a SON info required to re/create primary wallet
|
||||
* @ingroup object
|
||||
*/
|
||||
struct son_info {
|
||||
son_id_type son_id;
|
||||
uint64_t total_votes = 0;
|
||||
public_key_type signing_key;
|
||||
flat_map<peerplays_sidechain::sidechain_type, string> sidechain_public_keys;
|
||||
|
||||
bool operator==(const son_info& rhs) {
|
||||
bool son_sets_equal =
|
||||
(son_id == rhs.son_id) &&
|
||||
(total_votes == rhs.total_votes) &&
|
||||
(signing_key == rhs.signing_key) &&
|
||||
(sidechain_public_keys.size() == rhs.sidechain_public_keys.size());
|
||||
|
||||
if (son_sets_equal) {
|
||||
// Compare sidechain public keys
|
||||
}
|
||||
return son_sets_equal;
|
||||
}
|
||||
};
|
||||
|
||||
} }
|
||||
|
||||
FC_REFLECT( graphene::chain::son_info,
|
||||
(son_id)(total_votes)(signing_key)(sidechain_public_keys) )
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/db/object.hpp>
|
||||
#include <graphene/db/generic_index.hpp>
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
using namespace graphene::db;
|
||||
|
|
@ -10,7 +11,9 @@ namespace graphene { namespace chain {
|
|||
{
|
||||
inactive,
|
||||
active,
|
||||
in_maintenance
|
||||
request_maintenance,
|
||||
in_maintenance,
|
||||
deregistered
|
||||
};
|
||||
/**
|
||||
* @class son_statistics_object
|
||||
|
|
@ -27,12 +30,24 @@ namespace graphene { namespace chain {
|
|||
static const uint8_t type_id = impl_son_statistics_object_type;
|
||||
|
||||
son_id_type owner;
|
||||
// Lifetime total transactions signed
|
||||
uint64_t total_txs_signed = 0;
|
||||
// Transactions signed since the last son payouts
|
||||
uint64_t txs_signed = 0;
|
||||
// Total Voted Active time i.e. duration selected as part of voted active SONs
|
||||
uint64_t total_voted_time = 0;
|
||||
// Total Downtime barring the current down time in seconds, used for stats to present to user
|
||||
uint64_t total_downtime = 0;
|
||||
// Current Interval Downtime since last maintenance
|
||||
uint64_t current_interval_downtime = 0;
|
||||
// Down timestamp, if son status is in_maintenance use this
|
||||
fc::time_point_sec last_down_timestamp;
|
||||
// Last Active heartbeat timestamp
|
||||
fc::time_point_sec last_active_timestamp;
|
||||
// Total sidechain transactions reported by SON network while SON was active
|
||||
uint64_t total_sidechain_txs_reported = 0;
|
||||
// Sidechain transactions reported by this SON
|
||||
uint64_t sidechain_txs_reported = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -55,6 +70,7 @@ namespace graphene { namespace chain {
|
|||
vesting_balance_id_type pay_vb;
|
||||
son_statistics_id_type statistics;
|
||||
son_status status = son_status::inactive;
|
||||
flat_map<peerplays_sidechain::sidechain_type, string> sidechain_public_keys;
|
||||
|
||||
void pay_son_fee(share_type pay, database& db);
|
||||
};
|
||||
|
|
@ -77,23 +93,37 @@ namespace graphene { namespace chain {
|
|||
>;
|
||||
using son_index = generic_index<son_object, son_multi_index_type>;
|
||||
|
||||
struct by_owner;
|
||||
using son_stats_multi_index_type = multi_index_container<
|
||||
son_statistics_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >
|
||||
ordered_unique< tag<by_id>,
|
||||
member<object, object_id_type, &object::id>
|
||||
>,
|
||||
ordered_unique< tag<by_owner>,
|
||||
member<son_statistics_object, son_id_type, &son_statistics_object::owner>
|
||||
>
|
||||
>
|
||||
>;
|
||||
|
||||
using son_stats_index = generic_index<son_statistics_object, son_stats_multi_index_type>;
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_ENUM(graphene::chain::son_status, (inactive)(active)(in_maintenance) )
|
||||
FC_REFLECT_ENUM(graphene::chain::son_status, (inactive)(active)(request_maintenance)(in_maintenance)(deregistered) )
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::son_object, (graphene::db::object),
|
||||
(son_account)(vote_id)(total_votes)(url)(deposit)(signing_key)(pay_vb) )
|
||||
(son_account)(vote_id)(total_votes)(url)(deposit)(signing_key)(pay_vb)(statistics)(status)(sidechain_public_keys) )
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::son_statistics_object,
|
||||
(graphene::db::object),
|
||||
(owner)
|
||||
(total_txs_signed)
|
||||
(txs_signed)
|
||||
(total_voted_time)
|
||||
(total_downtime)
|
||||
(current_interval_downtime)
|
||||
(last_down_timestamp)
|
||||
(last_active_timestamp)
|
||||
(total_sidechain_txs_reported)
|
||||
(sidechain_txs_reported)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ namespace graphene { namespace chain {
|
|||
|
||||
enum class son_proposal_type
|
||||
{
|
||||
son_deregister_proposal
|
||||
son_deregister_proposal,
|
||||
son_report_down_proposal
|
||||
};
|
||||
|
||||
class son_proposal_object : public abstract_object<son_proposal_object>
|
||||
|
|
@ -36,6 +37,6 @@ using son_proposal_index = generic_index<son_proposal_object, son_proposal_multi
|
|||
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_ENUM(graphene::chain::son_proposal_type, (son_deregister_proposal) )
|
||||
FC_REFLECT_ENUM( graphene::chain::son_proposal_type, (son_deregister_proposal)(son_report_down_proposal) )
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::son_proposal_object, (graphene::chain::object), (proposal_id)(son_id)(proposal_type) )
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/evaluator.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class create_son_wallet_deposit_evaluator : public evaluator<create_son_wallet_deposit_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef son_wallet_deposit_create_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const son_wallet_deposit_create_operation& o);
|
||||
object_id_type do_apply(const son_wallet_deposit_create_operation& o);
|
||||
};
|
||||
|
||||
class process_son_wallet_deposit_evaluator : public evaluator<process_son_wallet_deposit_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef son_wallet_deposit_process_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const son_wallet_deposit_process_operation& o);
|
||||
object_id_type do_apply(const son_wallet_deposit_process_operation& o);
|
||||
};
|
||||
|
||||
} } // namespace graphene::chain
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/asset.hpp>
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
using namespace graphene::db;
|
||||
|
||||
/**
|
||||
* @class son_wallet_deposit_object
|
||||
* @brief tracks information about a SON wallet deposit.
|
||||
* @ingroup object
|
||||
*/
|
||||
class son_wallet_deposit_object : public abstract_object<son_wallet_deposit_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = son_wallet_deposit_object_type;
|
||||
|
||||
time_point_sec timestamp;
|
||||
sidechain_type sidechain;
|
||||
std::string sidechain_uid;
|
||||
std::string sidechain_transaction_id;
|
||||
std::string sidechain_from;
|
||||
std::string sidechain_to;
|
||||
std::string sidechain_currency;
|
||||
safe<int64_t> sidechain_amount;
|
||||
chain::account_id_type peerplays_from;
|
||||
chain::account_id_type peerplays_to;
|
||||
chain::asset peerplays_asset;
|
||||
|
||||
std::set<son_id_type> expected_reports;
|
||||
std::set<son_id_type> received_reports;
|
||||
|
||||
bool processed;
|
||||
};
|
||||
|
||||
struct by_sidechain;
|
||||
struct by_sidechain_uid;
|
||||
struct by_processed;
|
||||
struct by_sidechain_and_processed;
|
||||
using son_wallet_deposit_multi_index_type = multi_index_container<
|
||||
son_wallet_deposit_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>,
|
||||
member<object, object_id_type, &object::id>
|
||||
>,
|
||||
ordered_non_unique< tag<by_sidechain>,
|
||||
member<son_wallet_deposit_object, sidechain_type, &son_wallet_deposit_object::sidechain>
|
||||
>,
|
||||
ordered_unique< tag<by_sidechain_uid>,
|
||||
member<son_wallet_deposit_object, std::string, &son_wallet_deposit_object::sidechain_uid>
|
||||
>,
|
||||
ordered_non_unique< tag<by_processed>,
|
||||
member<son_wallet_deposit_object, bool, &son_wallet_deposit_object::processed>
|
||||
>,
|
||||
ordered_non_unique< tag<by_sidechain_and_processed>,
|
||||
composite_key<son_wallet_deposit_object,
|
||||
member<son_wallet_deposit_object, sidechain_type, &son_wallet_deposit_object::sidechain>,
|
||||
member<son_wallet_deposit_object, bool, &son_wallet_deposit_object::processed>
|
||||
>
|
||||
>
|
||||
>
|
||||
>;
|
||||
using son_wallet_deposit_index = generic_index<son_wallet_deposit_object, son_wallet_deposit_multi_index_type>;
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::son_wallet_deposit_object, (graphene::db::object),
|
||||
(timestamp) (sidechain)
|
||||
(sidechain_uid) (sidechain_transaction_id) (sidechain_from) (sidechain_to) (sidechain_currency) (sidechain_amount)
|
||||
(peerplays_from) (peerplays_to) (peerplays_asset)
|
||||
(expected_reports) (received_reports)
|
||||
(processed) )
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/evaluator.hpp>
|
||||
#include <graphene/chain/protocol/son_wallet.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class recreate_son_wallet_evaluator : public evaluator<recreate_son_wallet_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef son_wallet_recreate_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const son_wallet_recreate_operation& o);
|
||||
object_id_type do_apply(const son_wallet_recreate_operation& o);
|
||||
};
|
||||
|
||||
class update_son_wallet_evaluator : public evaluator<update_son_wallet_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef son_wallet_update_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const son_wallet_update_operation& o);
|
||||
object_id_type do_apply(const son_wallet_update_operation& o);
|
||||
};
|
||||
|
||||
} } // namespace graphene::chain
|
||||
47
libraries/chain/include/graphene/chain/son_wallet_object.hpp
Normal file
47
libraries/chain/include/graphene/chain/son_wallet_object.hpp
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/chain/son_info.hpp>
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
using namespace graphene::db;
|
||||
|
||||
/**
|
||||
* @class son_wallet_object
|
||||
* @brief tracks information about a SON wallet.
|
||||
* @ingroup object
|
||||
*/
|
||||
class son_wallet_object : public abstract_object<son_wallet_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = son_wallet_object_type;
|
||||
|
||||
time_point_sec valid_from;
|
||||
time_point_sec expires;
|
||||
|
||||
flat_map<sidechain_type, string> addresses;
|
||||
vector<son_info> sons;
|
||||
};
|
||||
|
||||
struct by_valid_from;
|
||||
struct by_expires;
|
||||
using son_wallet_multi_index_type = multi_index_container<
|
||||
son_wallet_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>,
|
||||
member<object, object_id_type, &object::id>
|
||||
>,
|
||||
ordered_unique< tag<by_valid_from>,
|
||||
member<son_wallet_object, time_point_sec, &son_wallet_object::valid_from>
|
||||
>,
|
||||
ordered_unique< tag<by_expires>,
|
||||
member<son_wallet_object, time_point_sec, &son_wallet_object::expires>
|
||||
>
|
||||
>
|
||||
>;
|
||||
using son_wallet_index = generic_index<son_wallet_object, son_wallet_multi_index_type>;
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::son_wallet_object, (graphene::db::object),
|
||||
(valid_from) (expires) (addresses) (sons) )
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/evaluator.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class create_son_wallet_withdraw_evaluator : public evaluator<create_son_wallet_withdraw_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef son_wallet_withdraw_create_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const son_wallet_withdraw_create_operation& o);
|
||||
object_id_type do_apply(const son_wallet_withdraw_create_operation& o);
|
||||
};
|
||||
|
||||
class process_son_wallet_withdraw_evaluator : public evaluator<process_son_wallet_withdraw_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef son_wallet_withdraw_process_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const son_wallet_withdraw_process_operation& o);
|
||||
object_id_type do_apply(const son_wallet_withdraw_process_operation& o);
|
||||
};
|
||||
|
||||
} } // namespace graphene::chain
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/asset.hpp>
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
using namespace graphene::db;
|
||||
|
||||
/**
|
||||
* @class son_wallet_withdraw_object
|
||||
* @brief tracks information about a SON wallet withdrawal.
|
||||
* @ingroup object
|
||||
*/
|
||||
class son_wallet_withdraw_object : public abstract_object<son_wallet_withdraw_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = son_wallet_withdraw_object_type;
|
||||
|
||||
time_point_sec timestamp;
|
||||
sidechain_type sidechain;
|
||||
std::string peerplays_uid;
|
||||
std::string peerplays_transaction_id;
|
||||
chain::account_id_type peerplays_from;
|
||||
chain::asset peerplays_asset;
|
||||
sidechain_type withdraw_sidechain;
|
||||
std::string withdraw_address;
|
||||
std::string withdraw_currency;
|
||||
safe<int64_t> withdraw_amount;
|
||||
|
||||
std::set<son_id_type> expected_reports;
|
||||
std::set<son_id_type> received_reports;
|
||||
|
||||
bool processed;
|
||||
};
|
||||
|
||||
struct by_peerplays_uid;
|
||||
struct by_withdraw_sidechain;
|
||||
struct by_processed;
|
||||
struct by_withdraw_sidechain_and_processed;
|
||||
using son_wallet_withdraw_multi_index_type = multi_index_container<
|
||||
son_wallet_withdraw_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>,
|
||||
member<object, object_id_type, &object::id>
|
||||
>,
|
||||
ordered_unique< tag<by_peerplays_uid>,
|
||||
member<son_wallet_withdraw_object, std::string, &son_wallet_withdraw_object::peerplays_uid>
|
||||
>,
|
||||
ordered_non_unique< tag<by_withdraw_sidechain>,
|
||||
member<son_wallet_withdraw_object, sidechain_type, &son_wallet_withdraw_object::withdraw_sidechain>
|
||||
>,
|
||||
ordered_non_unique< tag<by_processed>,
|
||||
member<son_wallet_withdraw_object, bool, &son_wallet_withdraw_object::processed>
|
||||
>,
|
||||
ordered_non_unique< tag<by_withdraw_sidechain_and_processed>,
|
||||
composite_key<son_wallet_withdraw_object,
|
||||
member<son_wallet_withdraw_object, sidechain_type, &son_wallet_withdraw_object::withdraw_sidechain>,
|
||||
member<son_wallet_withdraw_object, bool, &son_wallet_withdraw_object::processed>
|
||||
>
|
||||
>
|
||||
>
|
||||
>;
|
||||
using son_wallet_withdraw_index = generic_index<son_wallet_withdraw_object, son_wallet_withdraw_multi_index_type>;
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::son_wallet_withdraw_object, (graphene::db::object),
|
||||
(timestamp) (sidechain)
|
||||
(peerplays_uid) (peerplays_transaction_id) (peerplays_from) (peerplays_asset)
|
||||
(withdraw_sidechain) (withdraw_address) (withdraw_currency) (withdraw_amount)
|
||||
(expected_reports) (received_reports)
|
||||
(processed) )
|
||||
|
|
@ -63,6 +63,17 @@ struct vote_counter
|
|||
out_auth = auth;
|
||||
}
|
||||
|
||||
void finish_2_3( authority& out_auth )
|
||||
{
|
||||
if( total_votes == 0 )
|
||||
return;
|
||||
assert( total_votes <= std::numeric_limits<uint32_t>::max() );
|
||||
uint32_t weight = uint32_t( total_votes );
|
||||
weight = (weight * 2 / 3) + 1;
|
||||
auth.weight_threshold = weight;
|
||||
out_auth = auth;
|
||||
}
|
||||
|
||||
bool is_empty()const
|
||||
{
|
||||
return (total_votes == 0);
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
namespace graphene { namespace chain {
|
||||
|
||||
class witness_schedule_object;
|
||||
class son_schedule_object;
|
||||
|
||||
typedef hash_ctr_rng<
|
||||
/* HashClass = */ fc::sha256,
|
||||
|
|
@ -53,6 +54,22 @@ typedef generic_far_future_witness_scheduler<
|
|||
/* debug = */ true
|
||||
> far_future_witness_scheduler;
|
||||
|
||||
typedef generic_witness_scheduler<
|
||||
/* WitnessID = */ son_id_type,
|
||||
/* RNG = */ witness_scheduler_rng,
|
||||
/* CountType = */ decltype( chain_parameters::maximum_son_count ),
|
||||
/* OffsetType = */ uint32_t,
|
||||
/* debug = */ true
|
||||
> son_scheduler;
|
||||
|
||||
typedef generic_far_future_witness_scheduler<
|
||||
/* WitnessID = */ son_id_type,
|
||||
/* RNG = */ witness_scheduler_rng,
|
||||
/* CountType = */ decltype( chain_parameters::maximum_son_count ),
|
||||
/* OffsetType = */ uint32_t,
|
||||
/* debug = */ true
|
||||
> far_future_son_scheduler;
|
||||
|
||||
class witness_schedule_object : public graphene::db::abstract_object<witness_schedule_object>
|
||||
{
|
||||
public:
|
||||
|
|
@ -73,6 +90,26 @@ class witness_schedule_object : public graphene::db::abstract_object<witness_sch
|
|||
fc::uint128 recent_slots_filled;
|
||||
};
|
||||
|
||||
class son_schedule_object : public graphene::db::abstract_object<son_schedule_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = implementation_ids;
|
||||
static const uint8_t type_id = impl_son_schedule_object_type;
|
||||
|
||||
vector< son_id_type > current_shuffled_sons;
|
||||
|
||||
son_scheduler scheduler;
|
||||
uint32_t last_scheduling_block;
|
||||
uint64_t slots_since_genesis = 0;
|
||||
fc::array< char, sizeof(secret_hash_type) > rng_seed;
|
||||
|
||||
/**
|
||||
* Not necessary for consensus, but used for figuring out the participation rate.
|
||||
* The nth bit is 0 if the nth slot was unfilled, else it is 1.
|
||||
*/
|
||||
fc::uint128 recent_slots_filled;
|
||||
};
|
||||
|
||||
} }
|
||||
|
||||
|
||||
|
|
@ -96,3 +133,23 @@ FC_REFLECT_DERIVED(
|
|||
(recent_slots_filled)
|
||||
(current_shuffled_witnesses)
|
||||
)
|
||||
FC_REFLECT( graphene::chain::son_scheduler,
|
||||
(_turns)
|
||||
(_tokens)
|
||||
(_min_token_count)
|
||||
(_ineligible_waiting_for_token)
|
||||
(_ineligible_no_turn)
|
||||
(_eligible)
|
||||
(_schedule)
|
||||
(_lame_duck)
|
||||
)
|
||||
FC_REFLECT_DERIVED(
|
||||
graphene::chain::son_schedule_object,
|
||||
(graphene::db::object),
|
||||
(scheduler)
|
||||
(last_scheduling_block)
|
||||
(slots_since_genesis)
|
||||
(rng_seed)
|
||||
(recent_slots_filled)
|
||||
(current_shuffled_sons)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -148,6 +148,18 @@ struct proposal_operation_hardfork_visitor
|
|||
FC_ASSERT( block_time >= HARDFORK_SON_TIME, "son_delete_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const son_heartbeat_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_SON_TIME, "son_heartbeat_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const son_report_down_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_SON_TIME, "son_report_down_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const son_maintenance_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_SON_TIME, "son_maintenance_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
// loop and self visit in proposals
|
||||
void operator()(const proposal_create_operation &v) const {
|
||||
for (const op_wrapper &op : v.proposed_ops)
|
||||
|
|
@ -164,6 +176,15 @@ void son_hardfork_visitor::operator()( const son_delete_operation &v )
|
|||
});
|
||||
}
|
||||
|
||||
void son_hardfork_visitor::operator()( const son_report_down_operation &v )
|
||||
{
|
||||
db.create<son_proposal_object>([&]( son_proposal_object& son_prop ) {
|
||||
son_prop.proposal_type = son_proposal_type::son_report_down_proposal;
|
||||
son_prop.proposal_id = prop_id;
|
||||
son_prop.son_id = v.son_id;
|
||||
});
|
||||
}
|
||||
|
||||
void_result proposal_create_evaluator::do_evaluate(const proposal_create_operation& o)
|
||||
{ try {
|
||||
const database& d = db();
|
||||
|
|
|
|||
|
|
@ -171,15 +171,22 @@ void account_options::validate() const
|
|||
{
|
||||
auto needed_witnesses = num_witness;
|
||||
auto needed_committee = num_committee;
|
||||
auto needed_sons = num_son;
|
||||
|
||||
for( vote_id_type id : votes )
|
||||
if( id.type() == vote_id_type::witness && needed_witnesses )
|
||||
--needed_witnesses;
|
||||
else if ( id.type() == vote_id_type::committee && needed_committee )
|
||||
--needed_committee;
|
||||
else if ( id.type() == vote_id_type::son && needed_sons )
|
||||
--needed_sons;
|
||||
|
||||
FC_ASSERT( needed_witnesses == 0 && needed_committee == 0,
|
||||
"May not specify fewer witnesses or committee members than the number voted for.");
|
||||
FC_ASSERT( needed_witnesses == 0,
|
||||
"May not specify fewer witnesses than the number voted for.");
|
||||
FC_ASSERT( needed_committee == 0,
|
||||
"May not specify fewer committee members than the number voted for.");
|
||||
FC_ASSERT( needed_sons == 0,
|
||||
"May not specify fewer SONs than the number voted for.");
|
||||
}
|
||||
|
||||
void affiliate_reward_distribution::validate() const
|
||||
|
|
|
|||
68
libraries/chain/sidechain_address_evaluator.cpp
Normal file
68
libraries/chain/sidechain_address_evaluator.cpp
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
#include <graphene/chain/sidechain_address_evaluator.hpp>
|
||||
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/sidechain_address_object.hpp>
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
void_result add_sidechain_address_evaluator::do_evaluate(const sidechain_address_add_operation& op)
|
||||
{ try{
|
||||
|
||||
const auto& idx = db().get_index_type<sidechain_address_index>().indices().get<by_account_and_sidechain>();
|
||||
FC_ASSERT( idx.find(boost::make_tuple(op.sidechain_address_account, op.sidechain)) == idx.end(), "Duplicated item" );
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type add_sidechain_address_evaluator::do_apply(const sidechain_address_add_operation& op)
|
||||
{ try {
|
||||
const auto& new_sidechain_address_object = db().create<sidechain_address_object>( [&]( sidechain_address_object& obj ){
|
||||
obj.sidechain_address_account = op.sidechain_address_account;
|
||||
obj.sidechain = op.sidechain;
|
||||
obj.deposit_address = op.deposit_address;
|
||||
obj.withdraw_address = op.withdraw_address;
|
||||
});
|
||||
return new_sidechain_address_object.id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result update_sidechain_address_evaluator::do_evaluate(const sidechain_address_update_operation& op)
|
||||
{ try {
|
||||
FC_ASSERT(db().get(op.sidechain_address_id).sidechain_address_account == op.sidechain_address_account);
|
||||
const auto& idx = db().get_index_type<sidechain_address_index>().indices().get<by_id>();
|
||||
FC_ASSERT( idx.find(op.sidechain_address_id) != idx.end() );
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type update_sidechain_address_evaluator::do_apply(const sidechain_address_update_operation& op)
|
||||
{ try {
|
||||
const auto& idx = db().get_index_type<sidechain_address_index>().indices().get<by_id>();
|
||||
auto itr = idx.find(op.sidechain_address_id);
|
||||
if(itr != idx.end())
|
||||
{
|
||||
db().modify(*itr, [&op](sidechain_address_object &sao) {
|
||||
if(op.deposit_address.valid()) sao.deposit_address = *op.deposit_address;
|
||||
if(op.withdraw_address.valid()) sao.withdraw_address = *op.withdraw_address;
|
||||
});
|
||||
}
|
||||
return op.sidechain_address_id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result delete_sidechain_address_evaluator::do_evaluate(const sidechain_address_delete_operation& op)
|
||||
{ try {
|
||||
FC_ASSERT(db().get(op.sidechain_address_id).sidechain_address_account == op.sidechain_address_account);
|
||||
const auto& idx = db().get_index_type<sidechain_address_index>().indices().get<by_id>();
|
||||
FC_ASSERT( idx.find(op.sidechain_address_id) != idx.end() );
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result delete_sidechain_address_evaluator::do_apply(const sidechain_address_delete_operation& op)
|
||||
{ try {
|
||||
const auto& idx = db().get_index_type<sidechain_address_index>().indices().get<by_id>();
|
||||
auto sidechain_address = idx.find(op.sidechain_address_id);
|
||||
if(sidechain_address != idx.end()) {
|
||||
db().remove(*sidechain_address);
|
||||
}
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
} } // namespace graphene::chain
|
||||
128
libraries/chain/sidechain_transaction_evaluator.cpp
Normal file
128
libraries/chain/sidechain_transaction_evaluator.cpp
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
#include <graphene/chain/sidechain_transaction_evaluator.hpp>
|
||||
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/proposal_object.hpp>
|
||||
#include <graphene/chain/sidechain_transaction_object.hpp>
|
||||
#include <graphene/chain/son_object.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
void_result sidechain_transaction_create_evaluator::do_evaluate(const sidechain_transaction_create_operation &op)
|
||||
{ try {
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
|
||||
FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." );
|
||||
|
||||
FC_ASSERT((op.object_id.is<son_wallet_id_type>() || op.object_id.is<son_wallet_deposit_id_type>() || op.object_id.is<son_wallet_withdraw_id_type>()), "Invalid object id");
|
||||
|
||||
const auto &sto_idx = db().get_index_type<sidechain_transaction_index>().indices().get<by_object_id>();
|
||||
const auto &sto_obj = sto_idx.find(op.object_id);
|
||||
FC_ASSERT(sto_obj == sto_idx.end(), "Sidechain transaction for a given object is already created");
|
||||
|
||||
FC_ASSERT(!op.transaction.empty(), "Sidechain transaction data not set");
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( ( op ) ) }
|
||||
|
||||
object_id_type sidechain_transaction_create_evaluator::do_apply(const sidechain_transaction_create_operation &op)
|
||||
{ try {
|
||||
const auto &new_sidechain_transaction_object = db().create<sidechain_transaction_object>([&](sidechain_transaction_object &sto) {
|
||||
sto.sidechain = op.sidechain;
|
||||
sto.object_id = op.object_id;
|
||||
sto.transaction = op.transaction;
|
||||
std::transform(op.signers.begin(), op.signers.end(), std::inserter(sto.signers, sto.signers.end()), [](const son_id_type son_id) {
|
||||
return std::make_pair(son_id, false);
|
||||
});
|
||||
sto.block = db().head_block_id();
|
||||
sto.valid = true;
|
||||
sto.complete = false;
|
||||
sto.sent = false;
|
||||
});
|
||||
return new_sidechain_transaction_object.id;
|
||||
} FC_CAPTURE_AND_RETHROW( ( op ) ) }
|
||||
|
||||
void_result sidechain_transaction_sign_evaluator::do_evaluate(const sidechain_transaction_sign_operation &op)
|
||||
{ try {
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass
|
||||
|
||||
const auto &sto_idx = db().get_index_type<sidechain_transaction_index>().indices().get<by_id>();
|
||||
const auto &sto_obj = sto_idx.find(op.sidechain_transaction_id);
|
||||
FC_ASSERT(sto_obj != sto_idx.end(), "Sidechain transaction object not found");
|
||||
|
||||
const auto &son_idx = db().get_index_type<son_index>().indices().get<by_account>();
|
||||
const auto &son_obj = son_idx.find(op.payer);
|
||||
FC_ASSERT(son_obj != son_idx.end(), "SON object not found");
|
||||
|
||||
bool expected = false;
|
||||
for (auto signer : sto_obj->signers) {
|
||||
if (signer.first == son_obj->id) {
|
||||
expected = !signer.second;
|
||||
}
|
||||
}
|
||||
FC_ASSERT(expected, "Signer not expected");
|
||||
|
||||
FC_ASSERT(sto_obj->block == op.block, "Sidechain transaction already signed in this block");
|
||||
|
||||
FC_ASSERT(sto_obj->valid, "Transaction not valid");
|
||||
FC_ASSERT(!sto_obj->complete, "Transaction signing completed");
|
||||
FC_ASSERT(!sto_obj->sent, "Transaction already sent");
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( ( op ) ) }
|
||||
|
||||
object_id_type sidechain_transaction_sign_evaluator::do_apply(const sidechain_transaction_sign_operation &op)
|
||||
{ try {
|
||||
const auto &sto_idx = db().get_index_type<sidechain_transaction_index>().indices().get<by_id>();
|
||||
auto sto_obj = sto_idx.find(op.sidechain_transaction_id);
|
||||
|
||||
const auto &son_idx = db().get_index_type<son_index>().indices().get<by_account>();
|
||||
auto son_obj = son_idx.find(op.payer);
|
||||
|
||||
db().modify(*sto_obj, [&](sidechain_transaction_object &sto) {
|
||||
sto.transaction = op.transaction;
|
||||
sto.block = db().head_block_id();
|
||||
sto.complete = op.complete;
|
||||
for (size_t i = 0; i < sto.signers.size(); i++) {
|
||||
if (sto.signers.at(i).first == son_obj->id) {
|
||||
sto.signers.at(i).second = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
db().modify(son_obj->statistics(db()), [&](son_statistics_object& sso) {
|
||||
sso.txs_signed += 1;
|
||||
});
|
||||
|
||||
return op.sidechain_transaction_id;
|
||||
} FC_CAPTURE_AND_RETHROW( ( op ) ) }
|
||||
|
||||
void_result sidechain_transaction_send_evaluator::do_evaluate(const sidechain_transaction_send_operation &op)
|
||||
{ try {
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass
|
||||
|
||||
const auto &sto_idx = db().get_index_type<sidechain_transaction_index>().indices().get<by_id>();
|
||||
const auto &sto_obj = sto_idx.find(op.sidechain_transaction_id);
|
||||
FC_ASSERT(sto_obj != sto_idx.end(), "Sidechain transaction object not found");
|
||||
|
||||
FC_ASSERT(sto_obj->valid, "Transaction not valid");
|
||||
FC_ASSERT(sto_obj->complete, "Transaction signing not complete");
|
||||
FC_ASSERT(!sto_obj->sent, "Transaction already sent");
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( ( op ) ) }
|
||||
|
||||
object_id_type sidechain_transaction_send_evaluator::do_apply(const sidechain_transaction_send_operation &op)
|
||||
{ try {
|
||||
const auto &sto_idx = db().get_index_type<sidechain_transaction_index>().indices().get<by_id>();
|
||||
auto sto_obj = sto_idx.find(op.sidechain_transaction_id);
|
||||
|
||||
db().modify(*sto_obj, [&](sidechain_transaction_object &sto) {
|
||||
sto.block = db().head_block_id();
|
||||
sto.sent = true;
|
||||
});
|
||||
|
||||
return op.sidechain_transaction_id;
|
||||
} FC_CAPTURE_AND_RETHROW( ( op ) ) }
|
||||
|
||||
} // namespace chain
|
||||
} // namespace graphene
|
||||
|
|
@ -30,6 +30,7 @@ object_id_type create_son_evaluator::do_apply(const son_create_operation& op)
|
|||
obj.url = op.url;
|
||||
obj.deposit = op.deposit;
|
||||
obj.signing_key = op.signing_key;
|
||||
obj.sidechain_public_keys = op.sidechain_public_keys;
|
||||
obj.pay_vb = op.pay_vb;
|
||||
obj.statistics = db().create<son_statistics_object>([&](son_statistics_object& s){s.owner = obj.id;}).id;
|
||||
});
|
||||
|
|
@ -55,6 +56,7 @@ object_id_type update_son_evaluator::do_apply(const son_update_operation& op)
|
|||
if(op.new_url.valid()) so.url = *op.new_url;
|
||||
if(op.new_deposit.valid()) so.deposit = *op.new_deposit;
|
||||
if(op.new_signing_key.valid()) so.signing_key = *op.new_signing_key;
|
||||
if(op.new_sidechain_public_keys.valid()) so.sidechain_public_keys = *op.new_sidechain_public_keys;
|
||||
if(op.new_pay_vb.valid()) so.pay_vb = *op.new_pay_vb;
|
||||
});
|
||||
}
|
||||
|
|
@ -64,11 +66,8 @@ object_id_type update_son_evaluator::do_apply(const son_update_operation& op)
|
|||
void_result delete_son_evaluator::do_evaluate(const son_delete_operation& op)
|
||||
{ try {
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON_HARDFORK"); // can be removed after HF date pass
|
||||
// Get the current block witness signatory
|
||||
witness_id_type wit_id = db().get_scheduled_witness(1);
|
||||
const witness_object& current_witness = wit_id(db());
|
||||
// Either owner can remove or witness
|
||||
FC_ASSERT(db().get(op.son_id).son_account == op.owner_account || (db().is_son_dereg_valid(op.son_id) && op.payer == current_witness.witness_account));
|
||||
// Either owner can remove or consensus son account
|
||||
FC_ASSERT(op.payer == db().get(op.son_id).son_account || (db().is_son_dereg_valid(op.son_id) && op.payer == db().get_global_properties().parameters.son_account()));
|
||||
const auto& idx = db().get_index_type<son_index>().indices().get<by_id>();
|
||||
FC_ASSERT( idx.find(op.son_id) != idx.end() );
|
||||
return void_result();
|
||||
|
|
@ -83,6 +82,7 @@ void_result delete_son_evaluator::do_apply(const son_delete_operation& op)
|
|||
linear_vesting_policy new_vesting_policy;
|
||||
new_vesting_policy.begin_timestamp = db().head_block_time();
|
||||
new_vesting_policy.vesting_cliff_seconds = db().get_global_properties().parameters.son_vesting_period();
|
||||
new_vesting_policy.begin_balance = deposit.balance.amount;
|
||||
|
||||
db().modify(son->deposit(db()), [&new_vesting_policy](vesting_balance_object &vbo) {
|
||||
vbo.policy = new_vesting_policy;
|
||||
|
|
@ -93,4 +93,145 @@ void_result delete_son_evaluator::do_apply(const son_delete_operation& op)
|
|||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result son_heartbeat_evaluator::do_evaluate(const son_heartbeat_operation& op)
|
||||
{ try {
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass
|
||||
const auto& idx = db().get_index_type<son_index>().indices().get<by_id>();
|
||||
auto itr = idx.find(op.son_id);
|
||||
FC_ASSERT( itr != idx.end() );
|
||||
FC_ASSERT(itr->son_account == op.owner_account);
|
||||
auto stats = itr->statistics( db() );
|
||||
// Inactive SONs need not send heartbeats
|
||||
FC_ASSERT((itr->status == son_status::active) || (itr->status == son_status::in_maintenance) || (itr->status == son_status::request_maintenance), "Inactive SONs need not send heartbeats");
|
||||
// Account for network delays
|
||||
fc::time_point_sec min_ts = db().head_block_time() - fc::seconds(5 * db().block_interval());
|
||||
// Account for server ntp sync difference
|
||||
fc::time_point_sec max_ts = db().head_block_time() + fc::seconds(5 * db().block_interval());
|
||||
FC_ASSERT(op.ts > stats.last_active_timestamp, "Heartbeat sent without waiting minimum time");
|
||||
FC_ASSERT(op.ts > stats.last_down_timestamp, "Heartbeat sent is invalid can't be <= last down timestamp");
|
||||
FC_ASSERT(op.ts >= min_ts, "Heartbeat ts is behind the min threshold");
|
||||
FC_ASSERT(op.ts <= max_ts, "Heartbeat ts is above the max threshold");
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type son_heartbeat_evaluator::do_apply(const son_heartbeat_operation& op)
|
||||
{ try {
|
||||
const auto& idx = db().get_index_type<son_index>().indices().get<by_id>();
|
||||
auto itr = idx.find(op.son_id);
|
||||
if(itr != idx.end())
|
||||
{
|
||||
const global_property_object& gpo = db().get_global_properties();
|
||||
vector<son_id_type> active_son_ids;
|
||||
active_son_ids.reserve(gpo.active_sons.size());
|
||||
std::transform(gpo.active_sons.begin(), gpo.active_sons.end(),
|
||||
std::inserter(active_son_ids, active_son_ids.end()),
|
||||
[](const son_info& swi) {
|
||||
return swi.son_id;
|
||||
});
|
||||
|
||||
auto it_son = std::find(active_son_ids.begin(), active_son_ids.end(), op.son_id);
|
||||
bool is_son_active = true;
|
||||
|
||||
if(it_son == active_son_ids.end()) {
|
||||
is_son_active = false;
|
||||
}
|
||||
|
||||
if(itr->status == son_status::in_maintenance) {
|
||||
db().modify( itr->statistics( db() ), [&]( son_statistics_object& sso )
|
||||
{
|
||||
sso.current_interval_downtime += op.ts.sec_since_epoch() - sso.last_down_timestamp.sec_since_epoch();
|
||||
sso.last_active_timestamp = op.ts;
|
||||
// TODO: Remove me after sidechain tx signing is finished
|
||||
sso.txs_signed = sso.txs_signed + 1;
|
||||
} );
|
||||
|
||||
db().modify(*itr, [&is_son_active](son_object &so) {
|
||||
if(is_son_active) {
|
||||
so.status = son_status::active;
|
||||
} else {
|
||||
so.status = son_status::inactive;
|
||||
}
|
||||
});
|
||||
} else if ((itr->status == son_status::active) || (itr->status == son_status::request_maintenance)) {
|
||||
db().modify( itr->statistics( db() ), [&]( son_statistics_object& sso )
|
||||
{
|
||||
sso.last_active_timestamp = op.ts;
|
||||
// TODO: Remove me after sidechain tx signing is finished
|
||||
sso.txs_signed = sso.txs_signed + 1;
|
||||
} );
|
||||
}
|
||||
}
|
||||
return op.son_id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result son_report_down_evaluator::do_evaluate(const son_report_down_operation& op)
|
||||
{ try {
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass
|
||||
FC_ASSERT(op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer.");
|
||||
const auto& idx = db().get_index_type<son_index>().indices().get<by_id>();
|
||||
FC_ASSERT( idx.find(op.son_id) != idx.end() );
|
||||
auto itr = idx.find(op.son_id);
|
||||
auto stats = itr->statistics( db() );
|
||||
FC_ASSERT(itr->status == son_status::active || itr->status == son_status::request_maintenance, "Inactive/Deregistered/in_maintenance SONs cannot be reported on as down");
|
||||
FC_ASSERT(op.down_ts >= stats.last_active_timestamp, "down_ts should be greater than last_active_timestamp");
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type son_report_down_evaluator::do_apply(const son_report_down_operation& op)
|
||||
{ try {
|
||||
const auto& idx = db().get_index_type<son_index>().indices().get<by_id>();
|
||||
auto itr = idx.find(op.son_id);
|
||||
if(itr != idx.end())
|
||||
{
|
||||
if ((itr->status == son_status::active) || (itr->status == son_status::request_maintenance)) {
|
||||
db().modify( itr->statistics( db() ), [&]( son_statistics_object& sso )
|
||||
{
|
||||
sso.last_down_timestamp = op.down_ts;
|
||||
});
|
||||
|
||||
db().modify(*itr, [&op](son_object &so) {
|
||||
so.status = son_status::in_maintenance;
|
||||
});
|
||||
}
|
||||
}
|
||||
return op.son_id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result son_maintenance_evaluator::do_evaluate(const son_maintenance_operation& op)
|
||||
{ try {
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass
|
||||
FC_ASSERT(db().get(op.son_id).son_account == op.owner_account);
|
||||
const auto& idx = db().get_index_type<son_index>().indices().get<by_id>();
|
||||
auto itr = idx.find(op.son_id);
|
||||
FC_ASSERT( itr != idx.end() );
|
||||
// Inactive SONs can't go to maintenance, toggle between active and request_maintenance states
|
||||
if(op.request_type == son_maintenance_request_type::request_maintenance) {
|
||||
FC_ASSERT(itr->status == son_status::active, "Inactive SONs can't request for maintenance");
|
||||
} else if(op.request_type == son_maintenance_request_type::cancel_request_maintenance) {
|
||||
FC_ASSERT(itr->status == son_status::request_maintenance, "Only maintenance requested SONs can cancel the request");
|
||||
} else {
|
||||
FC_ASSERT(false, "Invalid maintenance operation");
|
||||
}
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type son_maintenance_evaluator::do_apply(const son_maintenance_operation& op)
|
||||
{ try {
|
||||
const auto& idx = db().get_index_type<son_index>().indices().get<by_id>();
|
||||
auto itr = idx.find(op.son_id);
|
||||
if(itr != idx.end())
|
||||
{
|
||||
if(itr->status == son_status::active && op.request_type == son_maintenance_request_type::request_maintenance) {
|
||||
db().modify(*itr, [](son_object &so) {
|
||||
so.status = son_status::request_maintenance;
|
||||
});
|
||||
} else if(itr->status == son_status::request_maintenance && op.request_type == son_maintenance_request_type::cancel_request_maintenance) {
|
||||
db().modify(*itr, [](son_object &so) {
|
||||
so.status = son_status::active;
|
||||
});
|
||||
}
|
||||
}
|
||||
return op.son_id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
} } // namespace graphene::chain
|
||||
|
|
|
|||
94
libraries/chain/son_wallet_deposit_evaluator.cpp
Normal file
94
libraries/chain/son_wallet_deposit_evaluator.cpp
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
#include <graphene/chain/son_wallet_deposit_evaluator.hpp>
|
||||
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/is_authorized_asset.hpp>
|
||||
#include <graphene/chain/son_object.hpp>
|
||||
#include <graphene/chain/son_wallet_deposit_object.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
void_result create_son_wallet_deposit_evaluator::do_evaluate(const son_wallet_deposit_create_operation& op)
|
||||
{ try{
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
|
||||
FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." );
|
||||
|
||||
const auto &idx = db().get_index_type<son_stats_index>().indices().get<by_owner>();
|
||||
FC_ASSERT(idx.find(op.son_id) != idx.end(), "Statistic object for a given SON ID does not exists");
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type create_son_wallet_deposit_evaluator::do_apply(const son_wallet_deposit_create_operation& op)
|
||||
{ try {
|
||||
const auto& idx = db().get_index_type<son_wallet_deposit_index>().indices().get<by_sidechain_uid>();
|
||||
auto itr = idx.find(op.sidechain_uid);
|
||||
if (itr == idx.end()) {
|
||||
const auto& new_son_wallet_deposit_object = db().create<son_wallet_deposit_object>( [&]( son_wallet_deposit_object& swdo ){
|
||||
swdo.timestamp = op.timestamp;
|
||||
swdo.sidechain = op.sidechain;
|
||||
swdo.sidechain_uid = op.sidechain_uid;
|
||||
swdo.sidechain_transaction_id = op.sidechain_transaction_id;
|
||||
swdo.sidechain_from = op.sidechain_from;
|
||||
swdo.sidechain_to = op.sidechain_to;
|
||||
swdo.sidechain_currency = op.sidechain_currency;
|
||||
swdo.sidechain_amount = op.sidechain_amount;
|
||||
swdo.peerplays_from = op.peerplays_from;
|
||||
swdo.peerplays_to = op.peerplays_to;
|
||||
swdo.peerplays_asset = op.peerplays_asset;
|
||||
|
||||
auto &gpo = db().get_global_properties();
|
||||
for (auto &si : gpo.active_sons) {
|
||||
swdo.expected_reports.insert(si.son_id);
|
||||
|
||||
auto stats_itr = db().get_index_type<son_stats_index>().indices().get<by_owner>().find(si.son_id);
|
||||
db().modify(*stats_itr, [&op, &si](son_statistics_object &sso) {
|
||||
sso.total_sidechain_txs_reported = sso.total_sidechain_txs_reported + 1;
|
||||
if (si.son_id == op.son_id) {
|
||||
sso.sidechain_txs_reported = sso.sidechain_txs_reported + 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
swdo.received_reports.insert(op.son_id);
|
||||
|
||||
swdo.processed = false;
|
||||
});
|
||||
return new_son_wallet_deposit_object.id;
|
||||
} else {
|
||||
db().modify(*itr, [&op](son_wallet_deposit_object &swdo) {
|
||||
swdo.received_reports.insert(op.son_id);
|
||||
});
|
||||
auto stats_itr = db().get_index_type<son_stats_index>().indices().get<by_owner>().find(op.son_id);
|
||||
db().modify(*stats_itr, [&op](son_statistics_object &sso) {
|
||||
sso.sidechain_txs_reported = sso.sidechain_txs_reported + 1;
|
||||
});
|
||||
return (*itr).id;
|
||||
}
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result process_son_wallet_deposit_evaluator::do_evaluate(const son_wallet_deposit_process_operation& op)
|
||||
{ try{
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
|
||||
FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." );
|
||||
|
||||
const auto& idx = db().get_index_type<son_wallet_deposit_index>().indices().get<by_id>();
|
||||
const auto& itr = idx.find(op.son_wallet_deposit_id);
|
||||
FC_ASSERT(itr != idx.end(), "Son wallet deposit not found");
|
||||
FC_ASSERT(!itr->processed, "Son wallet deposit is already processed");
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type process_son_wallet_deposit_evaluator::do_apply(const son_wallet_deposit_process_operation& op)
|
||||
{ try {
|
||||
const auto& idx = db().get_index_type<son_wallet_deposit_index>().indices().get<by_id>();
|
||||
auto itr = idx.find(op.son_wallet_deposit_id);
|
||||
if(itr != idx.end())
|
||||
{
|
||||
db().modify(*itr, [&op](son_wallet_deposit_object &swdo) {
|
||||
swdo.processed = true;
|
||||
});
|
||||
}
|
||||
return op.son_wallet_deposit_id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
} } // namespace graphene::chain
|
||||
81
libraries/chain/son_wallet_evaluator.cpp
Normal file
81
libraries/chain/son_wallet_evaluator.cpp
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
#include <graphene/chain/son_wallet_evaluator.hpp>
|
||||
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/son_wallet_object.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
void_result recreate_son_wallet_evaluator::do_evaluate(const son_wallet_recreate_operation& op)
|
||||
{ try{
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
|
||||
FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." );
|
||||
|
||||
const auto& idx = db().get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
auto itr = idx.rbegin();
|
||||
if(itr != idx.rend())
|
||||
{
|
||||
// Compare current wallet SONs and to-be lists of active sons
|
||||
auto cur_wallet_sons = (*itr).sons;
|
||||
auto new_wallet_sons = op.sons;
|
||||
|
||||
bool son_sets_equal = (cur_wallet_sons.size() == new_wallet_sons.size());
|
||||
if (son_sets_equal) {
|
||||
for( size_t i = 0; i < cur_wallet_sons.size(); i++ ) {
|
||||
son_sets_equal = son_sets_equal && cur_wallet_sons.at(i) == new_wallet_sons.at(i);
|
||||
}
|
||||
}
|
||||
|
||||
FC_ASSERT(son_sets_equal == false, "Wallet recreation not needed, active SONs set is not changed.");
|
||||
}
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type recreate_son_wallet_evaluator::do_apply(const son_wallet_recreate_operation& op)
|
||||
{ try {
|
||||
const auto& idx = db().get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
auto itr = idx.rbegin();
|
||||
if(itr != idx.rend())
|
||||
{
|
||||
db().modify(*itr, [&, op](son_wallet_object &swo) {
|
||||
swo.expires = db().head_block_time();
|
||||
});
|
||||
}
|
||||
|
||||
const auto& new_son_wallet_object = db().create<son_wallet_object>( [&]( son_wallet_object& obj ){
|
||||
obj.valid_from = db().head_block_time();
|
||||
obj.expires = time_point_sec::maximum();
|
||||
obj.sons = op.sons;
|
||||
});
|
||||
return new_son_wallet_object.id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result update_son_wallet_evaluator::do_evaluate(const son_wallet_update_operation& op)
|
||||
{ try{
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
|
||||
FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." );
|
||||
|
||||
const auto& idx = db().get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
FC_ASSERT( idx.find(op.son_wallet_id) != idx.end() );
|
||||
//auto itr = idx.find(op.son_wallet_id);
|
||||
//FC_ASSERT( itr->addresses.find(op.sidechain) == itr->addresses.end() ||
|
||||
// itr->addresses.at(op.sidechain).empty(), "Sidechain wallet address already set");
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type update_son_wallet_evaluator::do_apply(const son_wallet_update_operation& op)
|
||||
{ try {
|
||||
const auto& idx = db().get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
auto itr = idx.find(op.son_wallet_id);
|
||||
if (itr != idx.end())
|
||||
{
|
||||
if (itr->addresses.find(op.sidechain) == itr->addresses.end()) {
|
||||
db().modify(*itr, [&op](son_wallet_object &swo) {
|
||||
swo.addresses[op.sidechain] = op.address;
|
||||
});
|
||||
}
|
||||
}
|
||||
return op.son_wallet_id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
} } // namespace graphene::chain
|
||||
93
libraries/chain/son_wallet_withdraw_evaluator.cpp
Normal file
93
libraries/chain/son_wallet_withdraw_evaluator.cpp
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
#include <graphene/chain/son_wallet_withdraw_evaluator.hpp>
|
||||
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/is_authorized_asset.hpp>
|
||||
#include <graphene/chain/son_object.hpp>
|
||||
#include <graphene/chain/son_wallet_withdraw_object.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
void_result create_son_wallet_withdraw_evaluator::do_evaluate(const son_wallet_withdraw_create_operation& op)
|
||||
{ try{
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
|
||||
FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." );
|
||||
|
||||
const auto &idx = db().get_index_type<son_stats_index>().indices().get<by_owner>();
|
||||
FC_ASSERT(idx.find(op.son_id) != idx.end(), "Statistic object for a given SON ID does not exists");
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type create_son_wallet_withdraw_evaluator::do_apply(const son_wallet_withdraw_create_operation& op)
|
||||
{ try {
|
||||
const auto& idx = db().get_index_type<son_wallet_withdraw_index>().indices().get<by_peerplays_uid>();
|
||||
auto itr = idx.find(op.peerplays_uid);
|
||||
if (itr == idx.end()) {
|
||||
const auto& new_son_wallet_withdraw_object = db().create<son_wallet_withdraw_object>( [&]( son_wallet_withdraw_object& swwo ){
|
||||
swwo.timestamp = op.timestamp;
|
||||
swwo.sidechain = op.sidechain;
|
||||
swwo.peerplays_uid = op.peerplays_uid;
|
||||
swwo.peerplays_transaction_id = op.peerplays_transaction_id;
|
||||
swwo.peerplays_from = op.peerplays_from;
|
||||
swwo.peerplays_asset = op.peerplays_asset;
|
||||
swwo.withdraw_sidechain = op.withdraw_sidechain;
|
||||
swwo.withdraw_address = op.withdraw_address;
|
||||
swwo.withdraw_currency = op.withdraw_currency;
|
||||
swwo.withdraw_amount = op.withdraw_amount;
|
||||
|
||||
auto &gpo = db().get_global_properties();
|
||||
for (auto &si : gpo.active_sons) {
|
||||
swwo.expected_reports.insert(si.son_id);
|
||||
|
||||
auto stats_itr = db().get_index_type<son_stats_index>().indices().get<by_owner>().find(si.son_id);
|
||||
db().modify(*stats_itr, [&op, &si](son_statistics_object &sso) {
|
||||
sso.total_sidechain_txs_reported = sso.total_sidechain_txs_reported + 1;
|
||||
if (si.son_id == op.son_id) {
|
||||
sso.sidechain_txs_reported = sso.sidechain_txs_reported + 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
swwo.received_reports.insert(op.son_id);
|
||||
|
||||
swwo.processed = false;
|
||||
});
|
||||
return new_son_wallet_withdraw_object.id;
|
||||
} else {
|
||||
db().modify(*itr, [&op](son_wallet_withdraw_object &swwo) {
|
||||
swwo.received_reports.insert(op.son_id);
|
||||
});
|
||||
auto stats_itr = db().get_index_type<son_stats_index>().indices().get<by_owner>().find(op.son_id);
|
||||
db().modify(*stats_itr, [&op](son_statistics_object &sso) {
|
||||
sso.sidechain_txs_reported = sso.sidechain_txs_reported + 1;
|
||||
});
|
||||
return (*itr).id;
|
||||
}
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result process_son_wallet_withdraw_evaluator::do_evaluate(const son_wallet_withdraw_process_operation& op)
|
||||
{ try{
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
|
||||
FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." );
|
||||
|
||||
const auto& idx = db().get_index_type<son_wallet_withdraw_index>().indices().get<by_id>();
|
||||
const auto& itr = idx.find(op.son_wallet_withdraw_id);
|
||||
FC_ASSERT(itr != idx.end(), "Son wallet withdraw not found");
|
||||
FC_ASSERT(!itr->processed, "Son wallet withdraw is already processed");
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type process_son_wallet_withdraw_evaluator::do_apply(const son_wallet_withdraw_process_operation& op)
|
||||
{ try {
|
||||
const auto& idx = db().get_index_type<son_wallet_withdraw_index>().indices().get<by_id>();
|
||||
auto itr = idx.find(op.son_wallet_withdraw_id);
|
||||
if(itr != idx.end())
|
||||
{
|
||||
db().modify(*itr, [&op](son_wallet_withdraw_object &swwo) {
|
||||
swwo.processed = true;
|
||||
});
|
||||
}
|
||||
return op.son_wallet_withdraw_id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
} } // namespace graphene::chain
|
||||
|
|
@ -402,15 +402,6 @@ namespace graphene { namespace db {
|
|||
DerivedIndex::remove(obj);
|
||||
}
|
||||
|
||||
virtual const object& insert( object&& obj )override
|
||||
{
|
||||
const auto& res = DerivedIndex::insert(std::move(obj));
|
||||
for( const auto& item : _sindex )
|
||||
item->object_inserted( res );
|
||||
on_add(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
virtual void modify( const object& obj, const std::function<void(object&)>& m )override
|
||||
{
|
||||
save_undo( obj );
|
||||
|
|
|
|||
14
libraries/plugins/peerplays_sidechain/CMakeLists.txt
Normal file → Executable file
14
libraries/plugins/peerplays_sidechain/CMakeLists.txt
Normal file → Executable file
|
|
@ -2,9 +2,21 @@ file(GLOB HEADERS "include/graphene/peerplays_sidechain/*.hpp")
|
|||
|
||||
add_library( peerplays_sidechain
|
||||
peerplays_sidechain_plugin.cpp
|
||||
sidechain_net_manager.cpp
|
||||
sidechain_net_handler.cpp
|
||||
sidechain_net_handler_bitcoin.cpp
|
||||
sidechain_net_handler_peerplays.cpp
|
||||
bitcoin_utils.cpp
|
||||
)
|
||||
|
||||
target_link_libraries( peerplays_sidechain graphene_chain graphene_app )
|
||||
if (SUPPORT_MULTIPLE_SONS)
|
||||
message ("Multiple SONs per software instance are supported")
|
||||
target_compile_definitions(peerplays_sidechain PRIVATE SUPPORT_MULTIPLE_SONS)
|
||||
endif()
|
||||
unset(SUPPORT_MULTIPLE_SONS)
|
||||
unset(SUPPORT_MULTIPLE_SONS CACHE)
|
||||
|
||||
target_link_libraries( peerplays_sidechain graphene_chain graphene_app fc zmq )
|
||||
target_include_directories( peerplays_sidechain
|
||||
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
|
||||
|
||||
|
|
|
|||
680
libraries/plugins/peerplays_sidechain/bitcoin_utils.cpp
Normal file
680
libraries/plugins/peerplays_sidechain/bitcoin_utils.cpp
Normal file
|
|
@ -0,0 +1,680 @@
|
|||
#include <fc/crypto/base58.hpp>
|
||||
#include <fc/crypto/elliptic.hpp>
|
||||
#include <fc/crypto/ripemd160.hpp>
|
||||
#include <fc/crypto/sha256.hpp>
|
||||
#include <fc/io/raw.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin_utils.hpp>
|
||||
#include <secp256k1.h>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
static const unsigned char OP_0 = 0x00;
|
||||
static const unsigned char OP_1 = 0x51;
|
||||
static const unsigned char OP_2 = 0x52;
|
||||
static const unsigned char OP_3 = 0x53;
|
||||
static const unsigned char OP_4 = 0x54;
|
||||
static const unsigned char OP_5 = 0x55;
|
||||
static const unsigned char OP_6 = 0x56;
|
||||
static const unsigned char OP_7 = 0x57;
|
||||
static const unsigned char OP_8 = 0x58;
|
||||
static const unsigned char OP_9 = 0x59;
|
||||
static const unsigned char OP_10 = 0x5a;
|
||||
static const unsigned char OP_11 = 0x5b;
|
||||
static const unsigned char OP_12 = 0x5c;
|
||||
static const unsigned char OP_13 = 0x5d;
|
||||
static const unsigned char OP_14 = 0x5e;
|
||||
static const unsigned char OP_15 = 0x5f;
|
||||
static const unsigned char OP_16 = 0x60;
|
||||
|
||||
static const unsigned char OP_IF = 0x63;
|
||||
static const unsigned char OP_ENDIF = 0x68;
|
||||
static const unsigned char OP_SWAP = 0x7c;
|
||||
static const unsigned char OP_EQUAL = 0x87;
|
||||
static const unsigned char OP_ADD = 0x93;
|
||||
static const unsigned char OP_GREATERTHAN = 0xa0;
|
||||
static const unsigned char OP_HASH160 = 0xa9;
|
||||
static const unsigned char OP_CHECKSIG = 0xac;
|
||||
|
||||
class WriteBytesStream {
|
||||
public:
|
||||
WriteBytesStream(bytes &buffer) :
|
||||
storage_(buffer) {
|
||||
}
|
||||
|
||||
void write(const unsigned char *d, size_t s) {
|
||||
storage_.insert(storage_.end(), d, d + s);
|
||||
}
|
||||
|
||||
bool put(unsigned char c) {
|
||||
storage_.push_back(c);
|
||||
return true;
|
||||
}
|
||||
|
||||
void writedata8(uint8_t obj) {
|
||||
write((unsigned char *)&obj, 1);
|
||||
}
|
||||
|
||||
void writedata16(uint16_t obj) {
|
||||
obj = htole16(obj);
|
||||
write((unsigned char *)&obj, 2);
|
||||
}
|
||||
|
||||
void writedata32(uint32_t obj) {
|
||||
obj = htole32(obj);
|
||||
write((unsigned char *)&obj, 4);
|
||||
}
|
||||
|
||||
void writedata64(uint64_t obj) {
|
||||
obj = htole64(obj);
|
||||
write((unsigned char *)&obj, 8);
|
||||
}
|
||||
|
||||
void write_compact_int(uint64_t val) {
|
||||
if (val < 253) {
|
||||
writedata8(val);
|
||||
} else if (val <= std::numeric_limits<unsigned short>::max()) {
|
||||
writedata8(253);
|
||||
writedata16(val);
|
||||
} else if (val <= std::numeric_limits<unsigned int>::max()) {
|
||||
writedata8(254);
|
||||
writedata32(val);
|
||||
} else {
|
||||
writedata8(255);
|
||||
writedata64(val);
|
||||
}
|
||||
}
|
||||
|
||||
void writedata(const bytes &data) {
|
||||
write_compact_int(data.size());
|
||||
write(&data[0], data.size());
|
||||
}
|
||||
|
||||
private:
|
||||
bytes &storage_;
|
||||
};
|
||||
|
||||
class ReadBytesStream {
|
||||
public:
|
||||
ReadBytesStream(const bytes &buffer, size_t pos = 0) :
|
||||
storage_(buffer),
|
||||
pos_(pos),
|
||||
end_(buffer.size()) {
|
||||
}
|
||||
|
||||
size_t current_pos() const {
|
||||
return pos_;
|
||||
}
|
||||
void set_pos(size_t pos) {
|
||||
if (pos > end_)
|
||||
FC_THROW("Invalid position in BTC tx buffer");
|
||||
pos_ = pos;
|
||||
}
|
||||
|
||||
inline bool read(unsigned char *d, size_t s) {
|
||||
if (end_ - pos_ >= s) {
|
||||
memcpy(d, &storage_[pos_], s);
|
||||
pos_ += s;
|
||||
return true;
|
||||
}
|
||||
FC_THROW("invalid bitcoin tx buffer");
|
||||
}
|
||||
|
||||
inline bool get(unsigned char &c) {
|
||||
if (pos_ < end_) {
|
||||
c = storage_[pos_++];
|
||||
return true;
|
||||
}
|
||||
FC_THROW("invalid bitcoin tx buffer");
|
||||
}
|
||||
|
||||
uint8_t readdata8() {
|
||||
uint8_t obj;
|
||||
read((unsigned char *)&obj, 1);
|
||||
return obj;
|
||||
}
|
||||
uint16_t readdata16() {
|
||||
uint16_t obj;
|
||||
read((unsigned char *)&obj, 2);
|
||||
return le16toh(obj);
|
||||
}
|
||||
uint32_t readdata32() {
|
||||
uint32_t obj;
|
||||
read((unsigned char *)&obj, 4);
|
||||
return le32toh(obj);
|
||||
}
|
||||
uint64_t readdata64() {
|
||||
uint64_t obj;
|
||||
read((unsigned char *)&obj, 8);
|
||||
return le64toh(obj);
|
||||
}
|
||||
|
||||
uint64_t read_compact_int() {
|
||||
uint8_t size = readdata8();
|
||||
uint64_t ret = 0;
|
||||
if (size < 253) {
|
||||
ret = size;
|
||||
} else if (size == 253) {
|
||||
ret = readdata16();
|
||||
if (ret < 253)
|
||||
FC_THROW("non-canonical ReadCompactSize()");
|
||||
} else if (size == 254) {
|
||||
ret = readdata32();
|
||||
if (ret < 0x10000u)
|
||||
FC_THROW("non-canonical ReadCompactSize()");
|
||||
} else {
|
||||
ret = readdata64();
|
||||
if (ret < 0x100000000ULL)
|
||||
FC_THROW("non-canonical ReadCompactSize()");
|
||||
}
|
||||
if (ret > (uint64_t)0x02000000)
|
||||
FC_THROW("ReadCompactSize(): size too large");
|
||||
return ret;
|
||||
}
|
||||
|
||||
void readdata(bytes &data) {
|
||||
size_t s = read_compact_int();
|
||||
data.clear();
|
||||
data.resize(s);
|
||||
read(&data[0], s);
|
||||
}
|
||||
|
||||
private:
|
||||
const bytes &storage_;
|
||||
size_t pos_;
|
||||
size_t end_;
|
||||
};
|
||||
|
||||
void btc_outpoint::to_bytes(bytes &stream) const {
|
||||
WriteBytesStream str(stream);
|
||||
// TODO: write size?
|
||||
str.write((unsigned char *)hash.data(), hash.data_size());
|
||||
str.writedata32(n);
|
||||
}
|
||||
|
||||
size_t btc_outpoint::fill_from_bytes(const bytes &data, size_t pos) {
|
||||
ReadBytesStream str(data, pos);
|
||||
// TODO: read size?
|
||||
str.read((unsigned char *)hash.data(), hash.data_size());
|
||||
n = str.readdata32();
|
||||
return str.current_pos();
|
||||
}
|
||||
|
||||
void btc_in::to_bytes(bytes &stream) const {
|
||||
prevout.to_bytes(stream);
|
||||
WriteBytesStream str(stream);
|
||||
str.writedata(scriptSig);
|
||||
str.writedata32(nSequence);
|
||||
}
|
||||
|
||||
size_t btc_in::fill_from_bytes(const bytes &data, size_t pos) {
|
||||
pos = prevout.fill_from_bytes(data, pos);
|
||||
ReadBytesStream str(data, pos);
|
||||
str.readdata(scriptSig);
|
||||
nSequence = str.readdata32();
|
||||
return str.current_pos();
|
||||
}
|
||||
|
||||
void btc_out::to_bytes(bytes &stream) const {
|
||||
WriteBytesStream str(stream);
|
||||
str.writedata64(nValue);
|
||||
str.writedata(scriptPubKey);
|
||||
}
|
||||
|
||||
size_t btc_out::fill_from_bytes(const bytes &data, size_t pos) {
|
||||
ReadBytesStream str(data, pos);
|
||||
nValue = str.readdata64();
|
||||
str.readdata(scriptPubKey);
|
||||
return str.current_pos();
|
||||
}
|
||||
|
||||
void btc_tx::to_bytes(bytes &stream) const {
|
||||
WriteBytesStream str(stream);
|
||||
str.writedata32(nVersion);
|
||||
if (hasWitness) {
|
||||
// write dummy inputs and flag
|
||||
str.write_compact_int(0);
|
||||
unsigned char flags = 1;
|
||||
str.put(flags);
|
||||
}
|
||||
str.write_compact_int(vin.size());
|
||||
for (const auto &in : vin)
|
||||
in.to_bytes(stream);
|
||||
str.write_compact_int(vout.size());
|
||||
for (const auto &out : vout)
|
||||
out.to_bytes(stream);
|
||||
if (hasWitness) {
|
||||
for (const auto &in : vin) {
|
||||
str.write_compact_int(in.scriptWitness.size());
|
||||
for (const auto &stack_item : in.scriptWitness)
|
||||
str.writedata(stack_item);
|
||||
}
|
||||
}
|
||||
str.writedata32(nLockTime);
|
||||
}
|
||||
|
||||
size_t btc_tx::fill_from_bytes(const bytes &data, size_t pos) {
|
||||
ReadBytesStream ds(data, pos);
|
||||
nVersion = ds.readdata32();
|
||||
unsigned char flags = 0;
|
||||
vin.clear();
|
||||
vout.clear();
|
||||
hasWitness = false;
|
||||
/* Try to read the vin. In case the dummy is there, this will be read as an empty vector. */
|
||||
size_t vin_size = ds.read_compact_int();
|
||||
vin.resize(vin_size);
|
||||
pos = ds.current_pos();
|
||||
for (auto &in : vin)
|
||||
pos = in.fill_from_bytes(data, pos);
|
||||
ds.set_pos(pos);
|
||||
if (vin_size == 0) {
|
||||
/* We read a dummy or an empty vin. */
|
||||
ds.get(flags);
|
||||
if (flags != 0) {
|
||||
size_t vin_size = ds.read_compact_int();
|
||||
vin.resize(vin_size);
|
||||
pos = ds.current_pos();
|
||||
for (auto &in : vin)
|
||||
pos = in.fill_from_bytes(data, pos);
|
||||
ds.set_pos(pos);
|
||||
size_t vout_size = ds.read_compact_int();
|
||||
vout.resize(vout_size);
|
||||
pos = ds.current_pos();
|
||||
for (auto &out : vout)
|
||||
pos = out.fill_from_bytes(data, pos);
|
||||
ds.set_pos(pos);
|
||||
hasWitness = true;
|
||||
}
|
||||
} else {
|
||||
/* We read a non-empty vin. Assume a normal vout follows. */
|
||||
size_t vout_size = ds.read_compact_int();
|
||||
vout.resize(vout_size);
|
||||
pos = ds.current_pos();
|
||||
for (auto &out : vout)
|
||||
pos = out.fill_from_bytes(data, pos);
|
||||
ds.set_pos(pos);
|
||||
}
|
||||
if (hasWitness) {
|
||||
/* The witness flag is present, and we support witnesses. */
|
||||
for (auto &in : vin) {
|
||||
unsigned int size = ds.read_compact_int();
|
||||
in.scriptWitness.resize(size);
|
||||
for (auto &stack_item : in.scriptWitness)
|
||||
ds.readdata(stack_item);
|
||||
}
|
||||
}
|
||||
nLockTime = ds.readdata32();
|
||||
return ds.current_pos();
|
||||
}
|
||||
|
||||
void add_data_to_script(bytes &script, const bytes &data) {
|
||||
WriteBytesStream str(script);
|
||||
str.writedata(data);
|
||||
}
|
||||
|
||||
void add_number_to_script(bytes &script, unsigned char data) {
|
||||
WriteBytesStream str(script);
|
||||
if (data == 0)
|
||||
str.put(OP_0);
|
||||
else if (data == 1)
|
||||
str.put(OP_1);
|
||||
else if (data == 2)
|
||||
str.put(OP_2);
|
||||
else if (data == 3)
|
||||
str.put(OP_3);
|
||||
else if (data == 4)
|
||||
str.put(OP_4);
|
||||
else if (data == 5)
|
||||
str.put(OP_5);
|
||||
else if (data == 6)
|
||||
str.put(OP_6);
|
||||
else if (data == 7)
|
||||
str.put(OP_7);
|
||||
else if (data == 8)
|
||||
str.put(OP_8);
|
||||
else if (data == 9)
|
||||
str.put(OP_9);
|
||||
else if (data == 10)
|
||||
str.put(OP_10);
|
||||
else if (data == 11)
|
||||
str.put(OP_11);
|
||||
else if (data == 12)
|
||||
str.put(OP_12);
|
||||
else if (data == 13)
|
||||
str.put(OP_13);
|
||||
else if (data == 14)
|
||||
str.put(OP_14);
|
||||
else if (data == 15)
|
||||
str.put(OP_15);
|
||||
else if (data == 16)
|
||||
str.put(OP_16);
|
||||
else
|
||||
add_data_to_script(script, {data});
|
||||
}
|
||||
|
||||
bytes generate_redeem_script(std::vector<std::pair<fc::ecc::public_key, int>> key_data) {
|
||||
int total_weight = 0;
|
||||
bytes result;
|
||||
add_number_to_script(result, 0);
|
||||
for (auto &p : key_data) {
|
||||
total_weight += p.second;
|
||||
result.push_back(OP_SWAP);
|
||||
auto raw_data = p.first.serialize();
|
||||
add_data_to_script(result, bytes(raw_data.begin(), raw_data.begin() + raw_data.size()));
|
||||
result.push_back(OP_CHECKSIG);
|
||||
result.push_back(OP_IF);
|
||||
add_number_to_script(result, static_cast<unsigned char>(p.second));
|
||||
result.push_back(OP_ADD);
|
||||
result.push_back(OP_ENDIF);
|
||||
}
|
||||
int threshold_weight = 2 * total_weight / 3;
|
||||
add_number_to_script(result, static_cast<unsigned char>(threshold_weight));
|
||||
result.push_back(OP_GREATERTHAN);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** The Bech32 character set for encoding. */
|
||||
const char *charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
|
||||
|
||||
/** Concatenate two byte arrays. */
|
||||
bytes cat(bytes x, const bytes &y) {
|
||||
x.insert(x.end(), y.begin(), y.end());
|
||||
return x;
|
||||
}
|
||||
|
||||
/** Expand a HRP for use in checksum computation. */
|
||||
bytes expand_hrp(const std::string &hrp) {
|
||||
bytes ret;
|
||||
ret.resize(hrp.size() * 2 + 1);
|
||||
for (size_t i = 0; i < hrp.size(); ++i) {
|
||||
unsigned char c = hrp[i];
|
||||
ret[i] = c >> 5;
|
||||
ret[i + hrp.size() + 1] = c & 0x1f;
|
||||
}
|
||||
ret[hrp.size()] = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Find the polynomial with value coefficients mod the generator as 30-bit. */
|
||||
uint32_t polymod(const bytes &values) {
|
||||
uint32_t chk = 1;
|
||||
for (size_t i = 0; i < values.size(); ++i) {
|
||||
uint8_t top = chk >> 25;
|
||||
chk = (chk & 0x1ffffff) << 5 ^ values[i] ^
|
||||
(-((top >> 0) & 1) & 0x3b6a57b2UL) ^
|
||||
(-((top >> 1) & 1) & 0x26508e6dUL) ^
|
||||
(-((top >> 2) & 1) & 0x1ea119faUL) ^
|
||||
(-((top >> 3) & 1) & 0x3d4233ddUL) ^
|
||||
(-((top >> 4) & 1) & 0x2a1462b3UL);
|
||||
}
|
||||
return chk;
|
||||
}
|
||||
|
||||
/** Create a checksum. */
|
||||
bytes bech32_checksum(const std::string &hrp, const bytes &values) {
|
||||
bytes enc = cat(expand_hrp(hrp), values);
|
||||
enc.resize(enc.size() + 6);
|
||||
uint32_t mod = polymod(enc) ^ 1;
|
||||
bytes ret;
|
||||
ret.resize(6);
|
||||
for (size_t i = 0; i < 6; ++i) {
|
||||
ret[i] = (mod >> (5 * (5 - i))) & 31;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Encode a Bech32 string. */
|
||||
std::string bech32(const std::string &hrp, const bytes &values) {
|
||||
bytes checksum = bech32_checksum(hrp, values);
|
||||
bytes combined = cat(values, checksum);
|
||||
std::string ret = hrp + '1';
|
||||
ret.reserve(ret.size() + combined.size());
|
||||
for (size_t i = 0; i < combined.size(); ++i) {
|
||||
ret += charset[combined[i]];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Convert from one power-of-2 number base to another. */
|
||||
template <int frombits, int tobits, bool pad>
|
||||
bool convertbits(bytes &out, const bytes &in) {
|
||||
int acc = 0;
|
||||
int bits = 0;
|
||||
const int maxv = (1 << tobits) - 1;
|
||||
const int max_acc = (1 << (frombits + tobits - 1)) - 1;
|
||||
for (size_t i = 0; i < in.size(); ++i) {
|
||||
int value = in[i];
|
||||
acc = ((acc << frombits) | value) & max_acc;
|
||||
bits += frombits;
|
||||
while (bits >= tobits) {
|
||||
bits -= tobits;
|
||||
out.push_back((acc >> bits) & maxv);
|
||||
}
|
||||
}
|
||||
if (pad) {
|
||||
if (bits)
|
||||
out.push_back((acc << (tobits - bits)) & maxv);
|
||||
} else if (bits >= frombits || ((acc << (tobits - bits)) & maxv)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Encode a SegWit address. */
|
||||
std::string segwit_addr_encode(const std::string &hrp, uint8_t witver, const bytes &witprog) {
|
||||
bytes enc;
|
||||
enc.push_back(witver);
|
||||
convertbits<8, 5, true>(enc, witprog);
|
||||
std::string ret = bech32(hrp, enc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string p2wsh_address_from_redeem_script(const bytes &script, bitcoin_network network) {
|
||||
// calc script hash
|
||||
fc::sha256 sh = fc::sha256::hash(reinterpret_cast<const char *>(&script[0]), script.size());
|
||||
bytes wp(sh.data(), sh.data() + sh.data_size());
|
||||
switch (network) {
|
||||
case (mainnet):
|
||||
return segwit_addr_encode("bc", 0, wp);
|
||||
case (testnet):
|
||||
case (regtest):
|
||||
return segwit_addr_encode("tb", 0, wp);
|
||||
default:
|
||||
FC_THROW("Unknown bitcoin network type");
|
||||
}
|
||||
FC_THROW("Unknown bitcoin network type");
|
||||
}
|
||||
|
||||
bytes lock_script_for_redeem_script(const bytes &script) {
|
||||
bytes result;
|
||||
result.push_back(OP_0);
|
||||
fc::sha256 h = fc::sha256::hash(reinterpret_cast<const char *>(&script[0]), script.size());
|
||||
bytes shash(h.data(), h.data() + h.data_size());
|
||||
add_data_to_script(result, shash);
|
||||
return result;
|
||||
}
|
||||
|
||||
bytes hash_prevouts(const btc_tx &unsigned_tx) {
|
||||
fc::sha256::encoder hasher;
|
||||
for (const auto &in : unsigned_tx.vin) {
|
||||
bytes data;
|
||||
in.prevout.to_bytes(data);
|
||||
hasher.write(reinterpret_cast<const char *>(&data[0]), data.size());
|
||||
}
|
||||
fc::sha256 res = fc::sha256::hash(hasher.result());
|
||||
return bytes(res.data(), res.data() + res.data_size());
|
||||
}
|
||||
|
||||
bytes hash_sequence(const btc_tx &unsigned_tx) {
|
||||
fc::sha256::encoder hasher;
|
||||
for (const auto &in : unsigned_tx.vin) {
|
||||
hasher.write(reinterpret_cast<const char *>(&in.nSequence), sizeof(in.nSequence));
|
||||
}
|
||||
fc::sha256 res = fc::sha256::hash(hasher.result());
|
||||
return bytes(res.data(), res.data() + res.data_size());
|
||||
}
|
||||
|
||||
bytes hash_outputs(const btc_tx &unsigned_tx) {
|
||||
fc::sha256::encoder hasher;
|
||||
for (const auto &out : unsigned_tx.vout) {
|
||||
bytes data;
|
||||
out.to_bytes(data);
|
||||
hasher.write(reinterpret_cast<const char *>(&data[0]), data.size());
|
||||
}
|
||||
fc::sha256 res = fc::sha256::hash(hasher.result());
|
||||
return bytes(res.data(), res.data() + res.data_size());
|
||||
}
|
||||
|
||||
const secp256k1_context_t *btc_get_context() {
|
||||
static secp256k1_context_t *ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
bytes der_sign(const fc::ecc::private_key &priv_key, const fc::sha256 &digest) {
|
||||
fc::ecc::signature result;
|
||||
int size = result.size();
|
||||
FC_ASSERT(secp256k1_ecdsa_sign(btc_get_context(),
|
||||
(unsigned char *)digest.data(),
|
||||
(unsigned char *)result.begin(),
|
||||
&size,
|
||||
(unsigned char *)priv_key.get_secret().data(),
|
||||
secp256k1_nonce_function_rfc6979,
|
||||
nullptr));
|
||||
return bytes(result.begin(), result.begin() + size);
|
||||
}
|
||||
|
||||
std::vector<bytes> signatures_for_raw_transaction(const bytes &unsigned_tx,
|
||||
std::vector<uint64_t> in_amounts,
|
||||
const bytes &redeem_script,
|
||||
const fc::ecc::private_key &priv_key) {
|
||||
btc_tx tx;
|
||||
tx.fill_from_bytes(unsigned_tx);
|
||||
|
||||
FC_ASSERT(tx.vin.size() == in_amounts.size(), "Incorrect input amounts data");
|
||||
|
||||
std::vector<bytes> results;
|
||||
auto cur_amount = in_amounts.begin();
|
||||
// pre-calc reused values
|
||||
bytes hashPrevouts = hash_prevouts(tx);
|
||||
bytes hashSequence = hash_sequence(tx);
|
||||
bytes hashOutputs = hash_outputs(tx);
|
||||
// calc digest for every input according to BIP143
|
||||
// implement SIGHASH_ALL scheme
|
||||
for (const auto &in : tx.vin) {
|
||||
fc::sha256::encoder hasher;
|
||||
hasher.write(reinterpret_cast<const char *>(&tx.nVersion), sizeof(tx.nVersion));
|
||||
hasher.write(reinterpret_cast<const char *>(&hashPrevouts[0]), hashPrevouts.size());
|
||||
hasher.write(reinterpret_cast<const char *>(&hashSequence[0]), hashSequence.size());
|
||||
bytes data;
|
||||
in.prevout.to_bytes(data);
|
||||
hasher.write(reinterpret_cast<const char *>(&data[0]), data.size());
|
||||
bytes serializedScript;
|
||||
WriteBytesStream stream(serializedScript);
|
||||
stream.writedata(redeem_script);
|
||||
hasher.write(reinterpret_cast<const char *>(&serializedScript[0]), serializedScript.size());
|
||||
uint64_t amount = *cur_amount++;
|
||||
hasher.write(reinterpret_cast<const char *>(&amount), sizeof(amount));
|
||||
hasher.write(reinterpret_cast<const char *>(&in.nSequence), sizeof(in.nSequence));
|
||||
hasher.write(reinterpret_cast<const char *>(&hashOutputs[0]), hashOutputs.size());
|
||||
hasher.write(reinterpret_cast<const char *>(&tx.nLockTime), sizeof(tx.nLockTime));
|
||||
// add sigtype SIGHASH_ALL
|
||||
uint32_t sigtype = 1;
|
||||
hasher.write(reinterpret_cast<const char *>(&sigtype), sizeof(sigtype));
|
||||
|
||||
fc::sha256 digest = fc::sha256::hash(hasher.result());
|
||||
//std::vector<char> res = priv_key.sign(digest);
|
||||
//bytes s_data(res.begin(), res.end());
|
||||
bytes s_data = der_sign(priv_key, digest);
|
||||
s_data.push_back(1);
|
||||
results.push_back(s_data);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
bytes sign_pw_transfer_transaction(const bytes &unsigned_tx, std::vector<uint64_t> in_amounts, const bytes &redeem_script, const std::vector<fc::optional<fc::ecc::private_key>> &priv_keys) {
|
||||
btc_tx tx;
|
||||
tx.fill_from_bytes(unsigned_tx);
|
||||
bytes dummy_data;
|
||||
for (auto key : priv_keys) {
|
||||
if (key) {
|
||||
std::vector<bytes> signatures = signatures_for_raw_transaction(unsigned_tx, in_amounts, redeem_script, *key);
|
||||
FC_ASSERT(signatures.size() == tx.vin.size(), "Invalid signatures number");
|
||||
// push signatures in reverse order because script starts to check the top signature on the stack first
|
||||
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
||||
tx.vin[i].scriptWitness.insert(tx.vin[i].scriptWitness.begin(), signatures[i]);
|
||||
} else {
|
||||
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
||||
tx.vin[i].scriptWitness.push_back(dummy_data);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &in : tx.vin) {
|
||||
in.scriptWitness.push_back(redeem_script);
|
||||
}
|
||||
|
||||
tx.hasWitness = true;
|
||||
bytes ret;
|
||||
tx.to_bytes(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bytes add_dummy_signatures_for_pw_transfer(const bytes &unsigned_tx,
|
||||
const bytes &redeem_script,
|
||||
unsigned int key_count) {
|
||||
btc_tx tx;
|
||||
tx.fill_from_bytes(unsigned_tx);
|
||||
|
||||
bytes dummy_data;
|
||||
for (auto &in : tx.vin) {
|
||||
for (unsigned i = 0; i < key_count; i++)
|
||||
in.scriptWitness.push_back(dummy_data);
|
||||
in.scriptWitness.push_back(redeem_script);
|
||||
}
|
||||
|
||||
tx.hasWitness = true;
|
||||
bytes ret;
|
||||
tx.to_bytes(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bytes partially_sign_pw_transfer_transaction(const bytes &partially_signed_tx,
|
||||
std::vector<uint64_t> in_amounts,
|
||||
const fc::ecc::private_key &priv_key,
|
||||
unsigned int key_idx) {
|
||||
btc_tx tx;
|
||||
tx.fill_from_bytes(partially_signed_tx);
|
||||
FC_ASSERT(tx.vin.size() > 0);
|
||||
bytes redeem_script = tx.vin[0].scriptWitness.back();
|
||||
std::vector<bytes> signatures = signatures_for_raw_transaction(partially_signed_tx, in_amounts, redeem_script, priv_key);
|
||||
FC_ASSERT(signatures.size() == tx.vin.size(), "Invalid signatures number");
|
||||
// push signatures in reverse order because script starts to check the top signature on the stack first
|
||||
unsigned witness_idx = tx.vin[0].scriptWitness.size() - 2 - key_idx;
|
||||
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
||||
tx.vin[i].scriptWitness[witness_idx] = signatures[i];
|
||||
bytes ret;
|
||||
tx.to_bytes(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bytes add_signatures_to_unsigned_tx(const bytes &unsigned_tx, const std::vector<std::vector<bytes>> &signature_set, const bytes &redeem_script) {
|
||||
btc_tx tx;
|
||||
tx.fill_from_bytes(unsigned_tx);
|
||||
bytes dummy_data;
|
||||
for (unsigned int i = 0; i < signature_set.size(); i++) {
|
||||
std::vector<bytes> signatures = signature_set[i];
|
||||
FC_ASSERT(signatures.size() == tx.vin.size(), "Invalid signatures number");
|
||||
// push signatures in reverse order because script starts to check the top signature on the stack first
|
||||
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
||||
tx.vin[i].scriptWitness.insert(tx.vin[i].scriptWitness.begin(), signatures[i]);
|
||||
}
|
||||
|
||||
for (auto &in : tx.vin) {
|
||||
in.scriptWitness.push_back(redeem_script);
|
||||
}
|
||||
|
||||
tx.hasWitness = true;
|
||||
bytes ret;
|
||||
tx.to_bytes(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
#pragma once
|
||||
#include <fc/optional.hpp>
|
||||
#include <graphene/peerplays_sidechain/defs.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
enum bitcoin_network {
|
||||
mainnet,
|
||||
testnet,
|
||||
regtest
|
||||
};
|
||||
|
||||
bytes generate_redeem_script(std::vector<std::pair<fc::ecc::public_key, int>> key_data);
|
||||
std::string p2wsh_address_from_redeem_script(const bytes &script, bitcoin_network network = mainnet);
|
||||
bytes lock_script_for_redeem_script(const bytes &script);
|
||||
|
||||
std::vector<bytes> signatures_for_raw_transaction(const bytes &unsigned_tx,
|
||||
std::vector<uint64_t> in_amounts,
|
||||
const bytes &redeem_script,
|
||||
const fc::ecc::private_key &priv_key);
|
||||
|
||||
/*
|
||||
* unsigned_tx - tx, all inputs of which are spends of the PW P2SH address
|
||||
* returns signed transaction
|
||||
*/
|
||||
bytes sign_pw_transfer_transaction(const bytes &unsigned_tx,
|
||||
std::vector<uint64_t> in_amounts,
|
||||
const bytes &redeem_script,
|
||||
const std::vector<fc::optional<fc::ecc::private_key>> &priv_keys);
|
||||
|
||||
///
|
||||
////// \brief Adds dummy signatures instead of real signatures
|
||||
////// \param unsigned_tx
|
||||
////// \param redeem_script
|
||||
////// \param key_count
|
||||
////// \return can be used as partially signed tx
|
||||
bytes add_dummy_signatures_for_pw_transfer(const bytes &unsigned_tx,
|
||||
const bytes &redeem_script,
|
||||
unsigned int key_count);
|
||||
|
||||
///
|
||||
/// \brief replaces dummy sgnatures in partially signed tx with real tx
|
||||
/// \param partially_signed_tx
|
||||
/// \param in_amounts
|
||||
/// \param priv_key
|
||||
/// \param key_idx
|
||||
/// \return
|
||||
///
|
||||
bytes partially_sign_pw_transfer_transaction(const bytes &partially_signed_tx,
|
||||
std::vector<uint64_t> in_amounts,
|
||||
const fc::ecc::private_key &priv_key,
|
||||
unsigned int key_idx);
|
||||
|
||||
///
|
||||
/// \brief Creates ready to publish bitcoin transaction from unsigned tx and
|
||||
/// full set of the signatures. This is alternative way to create tx
|
||||
/// with partially_sign_pw_transfer_transaction
|
||||
/// \param unsigned_tx
|
||||
/// \param signatures
|
||||
/// \param redeem_script
|
||||
/// \return
|
||||
///
|
||||
bytes add_signatures_to_unsigned_tx(const bytes &unsigned_tx,
|
||||
const std::vector<std::vector<bytes>> &signatures,
|
||||
const bytes &redeem_script);
|
||||
|
||||
struct btc_outpoint {
|
||||
fc::uint256 hash;
|
||||
uint32_t n;
|
||||
|
||||
void to_bytes(bytes &stream) const;
|
||||
size_t fill_from_bytes(const bytes &data, size_t pos = 0);
|
||||
};
|
||||
|
||||
struct btc_in {
|
||||
btc_outpoint prevout;
|
||||
bytes scriptSig;
|
||||
uint32_t nSequence;
|
||||
std::vector<bytes> scriptWitness;
|
||||
|
||||
void to_bytes(bytes &stream) const;
|
||||
size_t fill_from_bytes(const bytes &data, size_t pos = 0);
|
||||
};
|
||||
|
||||
struct btc_out {
|
||||
int64_t nValue;
|
||||
bytes scriptPubKey;
|
||||
|
||||
void to_bytes(bytes &stream) const;
|
||||
size_t fill_from_bytes(const bytes &data, size_t pos = 0);
|
||||
};
|
||||
|
||||
struct btc_tx {
|
||||
std::vector<btc_in> vin;
|
||||
std::vector<btc_out> vout;
|
||||
int32_t nVersion;
|
||||
uint32_t nLockTime;
|
||||
bool hasWitness;
|
||||
|
||||
void to_bytes(bytes &stream) const;
|
||||
size_t fill_from_bytes(const bytes &data, size_t pos = 0);
|
||||
};
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <fc/crypto/sha256.hpp>
|
||||
#include <fc/safe.hpp>
|
||||
#include <fc/time.hpp>
|
||||
|
||||
#include <graphene/chain/protocol/asset.hpp>
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
using bytes = std::vector<unsigned char>;
|
||||
|
||||
struct prev_out {
|
||||
bool operator!=(const prev_out &obj) const {
|
||||
if (this->hash_tx != obj.hash_tx ||
|
||||
this->n_vout != obj.n_vout ||
|
||||
this->amount != obj.amount) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string hash_tx;
|
||||
uint32_t n_vout;
|
||||
uint64_t amount;
|
||||
};
|
||||
|
||||
struct info_for_vin {
|
||||
info_for_vin() = default;
|
||||
|
||||
info_for_vin(const prev_out &_out, const std::string &_address, bytes _script = bytes(), bool _resend = false);
|
||||
|
||||
bool operator!=(const info_for_vin &obj) const;
|
||||
|
||||
struct comparer {
|
||||
bool operator()(const info_for_vin &lhs, const info_for_vin &rhs) const;
|
||||
};
|
||||
|
||||
static uint64_t count_id_info_for_vin;
|
||||
uint64_t id;
|
||||
|
||||
fc::sha256 identifier;
|
||||
|
||||
prev_out out;
|
||||
std::string address;
|
||||
bytes script;
|
||||
|
||||
bool used = false;
|
||||
bool resend = false;
|
||||
};
|
||||
|
||||
struct sidechain_event_data {
|
||||
fc::time_point_sec timestamp;
|
||||
sidechain_type sidechain;
|
||||
std::string sidechain_uid;
|
||||
std::string sidechain_transaction_id;
|
||||
std::string sidechain_from;
|
||||
std::string sidechain_to;
|
||||
std::string sidechain_currency;
|
||||
fc::safe<int64_t> sidechain_amount;
|
||||
chain::account_id_type peerplays_from;
|
||||
chain::account_id_type peerplays_to;
|
||||
chain::asset peerplays_asset;
|
||||
};
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
|
@ -1,34 +1,38 @@
|
|||
#pragma once
|
||||
|
||||
#include <graphene/app/plugin.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/account_object.hpp>
|
||||
|
||||
#include <fc/thread/future.hpp>
|
||||
#include <graphene/chain/son_object.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
using namespace chain;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
class peerplays_sidechain_plugin_impl;
|
||||
namespace detail {
|
||||
class peerplays_sidechain_plugin_impl;
|
||||
}
|
||||
|
||||
class peerplays_sidechain_plugin : public graphene::app::plugin
|
||||
{
|
||||
public:
|
||||
peerplays_sidechain_plugin();
|
||||
virtual ~peerplays_sidechain_plugin();
|
||||
class peerplays_sidechain_plugin : public graphene::app::plugin {
|
||||
public:
|
||||
peerplays_sidechain_plugin();
|
||||
virtual ~peerplays_sidechain_plugin();
|
||||
|
||||
std::string plugin_name()const override;
|
||||
virtual void plugin_set_program_options(
|
||||
boost::program_options::options_description& cli,
|
||||
boost::program_options::options_description& cfg) override;
|
||||
virtual void plugin_initialize(const boost::program_options::variables_map& options) override;
|
||||
virtual void plugin_startup() override;
|
||||
std::string plugin_name() const override;
|
||||
virtual void plugin_set_program_options(
|
||||
boost::program_options::options_description &cli,
|
||||
boost::program_options::options_description &cfg) override;
|
||||
virtual void plugin_initialize(const boost::program_options::variables_map &options) override;
|
||||
virtual void plugin_startup() override;
|
||||
|
||||
std::unique_ptr<detail::peerplays_sidechain_plugin_impl> my;
|
||||
std::unique_ptr<detail::peerplays_sidechain_plugin_impl> my;
|
||||
|
||||
std::set<chain::son_id_type> &get_sons();
|
||||
const son_id_type get_current_son_id();
|
||||
const son_object get_current_son_object();
|
||||
const son_object get_son_object(son_id_type son_id);
|
||||
bool is_active_son(son_id_type son_id);
|
||||
fc::ecc::private_key get_private_key(son_id_type son_id);
|
||||
fc::ecc::private_key get_private_key(chain::public_key_type public_key);
|
||||
};
|
||||
|
||||
} } //graphene::peerplays_sidechain_plugin
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
#include <fc/signals.hpp>
|
||||
#include <graphene/chain/global_property_object.hpp>
|
||||
#include <graphene/chain/sidechain_address_object.hpp>
|
||||
#include <graphene/chain/sidechain_transaction_object.hpp>
|
||||
#include <graphene/chain/son_wallet_deposit_object.hpp>
|
||||
#include <graphene/chain/son_wallet_withdraw_object.hpp>
|
||||
#include <graphene/peerplays_sidechain/defs.hpp>
|
||||
#include <graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
class sidechain_net_handler {
|
||||
public:
|
||||
sidechain_net_handler(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options);
|
||||
virtual ~sidechain_net_handler();
|
||||
|
||||
sidechain_type get_sidechain();
|
||||
std::vector<std::string> get_sidechain_deposit_addresses();
|
||||
std::vector<std::string> get_sidechain_withdraw_addresses();
|
||||
std::string get_private_key(std::string public_key);
|
||||
|
||||
void sidechain_event_data_received(const sidechain_event_data &sed);
|
||||
void process_deposits();
|
||||
void process_withdrawals();
|
||||
void process_sidechain_transactions();
|
||||
void send_sidechain_transactions();
|
||||
|
||||
virtual void recreate_primary_wallet() = 0;
|
||||
virtual bool process_deposit(const son_wallet_deposit_object &swdo) = 0;
|
||||
virtual bool process_withdrawal(const son_wallet_withdraw_object &swwo) = 0;
|
||||
virtual std::string process_sidechain_transaction(const sidechain_transaction_object &sto, bool &complete) = 0;
|
||||
virtual bool send_sidechain_transaction(const sidechain_transaction_object &sto) = 0;
|
||||
|
||||
protected:
|
||||
peerplays_sidechain_plugin &plugin;
|
||||
graphene::chain::database &database;
|
||||
sidechain_type sidechain;
|
||||
|
||||
std::map<std::string, std::string> private_keys;
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
#pragma once
|
||||
|
||||
#include <graphene/peerplays_sidechain/sidechain_net_handler.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <zmq.hpp>
|
||||
|
||||
#include <fc/network/http/connection.hpp>
|
||||
#include <fc/signals.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
class btc_txout {
|
||||
public:
|
||||
std::string txid_;
|
||||
unsigned int out_num_;
|
||||
double amount_;
|
||||
};
|
||||
|
||||
class bitcoin_rpc_client {
|
||||
public:
|
||||
bitcoin_rpc_client(std::string _ip, uint32_t _rpc, std::string _user, std::string _password, std::string _wallet, std::string _wallet_password);
|
||||
|
||||
std::string addmultisigaddress(const uint32_t nrequired, const std::vector<std::string> public_keys);
|
||||
std::string createpsbt(const std::vector<btc_txout> &ins, const fc::flat_map<std::string, double> outs);
|
||||
std::string createrawtransaction(const std::vector<btc_txout> &ins, const fc::flat_map<std::string, double> outs);
|
||||
std::string createwallet(const std::string &wallet_name);
|
||||
std::string decodepsbt(std::string const &tx_psbt);
|
||||
std::string decoderawtransaction(std::string const &tx_hex);
|
||||
std::string encryptwallet(const std::string &passphrase);
|
||||
uint64_t estimatesmartfee();
|
||||
std::string finalizepsbt(std::string const &tx_psbt);
|
||||
std::string getaddressinfo(const std::string &address);
|
||||
std::string getblock(const std::string &block_hash, int32_t verbosity = 2);
|
||||
void importaddress(const std::string &address_or_script);
|
||||
std::vector<btc_txout> listunspent();
|
||||
std::vector<btc_txout> listunspent_by_address_and_amount(const std::string &address, double transfer_amount);
|
||||
std::string loadwallet(const std::string &filename);
|
||||
bool sendrawtransaction(const std::string &tx_hex);
|
||||
std::string signrawtransactionwithwallet(const std::string &tx_hash);
|
||||
std::string unloadwallet(const std::string &filename);
|
||||
std::string walletlock();
|
||||
std::string walletprocesspsbt(std::string const &tx_psbt);
|
||||
bool walletpassphrase(const std::string &passphrase, uint32_t timeout = 60);
|
||||
|
||||
private:
|
||||
fc::http::reply send_post_request(std::string body, bool show_log = false);
|
||||
|
||||
std::string ip;
|
||||
uint32_t rpc_port;
|
||||
std::string user;
|
||||
std::string password;
|
||||
std::string wallet;
|
||||
std::string wallet_password;
|
||||
|
||||
fc::http::header authorization;
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
|
||||
class zmq_listener {
|
||||
public:
|
||||
zmq_listener(std::string _ip, uint32_t _zmq);
|
||||
|
||||
fc::signal<void(const std::string &)> event_received;
|
||||
|
||||
private:
|
||||
void handle_zmq();
|
||||
std::vector<zmq::message_t> receive_multipart();
|
||||
|
||||
std::string ip;
|
||||
uint32_t zmq_port;
|
||||
|
||||
zmq::context_t ctx;
|
||||
zmq::socket_t socket;
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
|
||||
class sidechain_net_handler_bitcoin : public sidechain_net_handler {
|
||||
public:
|
||||
sidechain_net_handler_bitcoin(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options);
|
||||
virtual ~sidechain_net_handler_bitcoin();
|
||||
|
||||
void recreate_primary_wallet();
|
||||
bool process_deposit(const son_wallet_deposit_object &swdo);
|
||||
bool process_withdrawal(const son_wallet_withdraw_object &swwo);
|
||||
std::string process_sidechain_transaction(const sidechain_transaction_object &sto, bool &complete);
|
||||
bool send_sidechain_transaction(const sidechain_transaction_object &sto);
|
||||
|
||||
private:
|
||||
std::string ip;
|
||||
uint32_t zmq_port;
|
||||
uint32_t rpc_port;
|
||||
std::string rpc_user;
|
||||
std::string rpc_password;
|
||||
std::string wallet;
|
||||
std::string wallet_password;
|
||||
|
||||
std::unique_ptr<bitcoin_rpc_client> bitcoin_client;
|
||||
std::unique_ptr<zmq_listener> listener;
|
||||
|
||||
fc::future<void> on_changed_objects_task;
|
||||
|
||||
std::string create_transaction(const std::vector<btc_txout> &inputs, const fc::flat_map<std::string, double> outputs);
|
||||
std::string sign_transaction(const std::string &tx, bool &complete);
|
||||
bool send_transaction(const std::string &tx);
|
||||
|
||||
std::string create_transaction_raw(const std::vector<btc_txout> &inputs, const fc::flat_map<std::string, double> outputs);
|
||||
std::string create_transaction_psbt(const std::vector<btc_txout> &inputs, const fc::flat_map<std::string, double> outputs);
|
||||
std::string create_transaction_standalone(const std::vector<btc_txout> &inputs, const fc::flat_map<std::string, double> outputs);
|
||||
|
||||
std::string sign_transaction_raw(const std::string &tx, bool &complete);
|
||||
std::string sign_transaction_psbt(const std::string &tx, bool &complete);
|
||||
std::string sign_transaction_standalone(const std::string &tx, bool &complete);
|
||||
|
||||
void handle_event(const std::string &event_data);
|
||||
std::vector<info_for_vin> extract_info_from_block(const std::string &_block);
|
||||
void on_changed_objects(const vector<object_id_type> &ids, const flat_set<account_id_type> &accounts);
|
||||
void on_changed_objects_cb(const vector<object_id_type> &ids, const flat_set<account_id_type> &accounts);
|
||||
};
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
#include <graphene/peerplays_sidechain/sidechain_net_handler.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <fc/signals.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
class sidechain_net_handler_peerplays : public sidechain_net_handler {
|
||||
public:
|
||||
sidechain_net_handler_peerplays(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options);
|
||||
virtual ~sidechain_net_handler_peerplays();
|
||||
|
||||
void recreate_primary_wallet();
|
||||
bool process_deposit(const son_wallet_deposit_object &swdo);
|
||||
bool process_withdrawal(const son_wallet_withdraw_object &swwo);
|
||||
std::string process_sidechain_transaction(const sidechain_transaction_object &sto, bool &complete);
|
||||
bool send_sidechain_transaction(const sidechain_transaction_object &sto);
|
||||
|
||||
private:
|
||||
void on_applied_block(const signed_block &b);
|
||||
};
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
#include <graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp>
|
||||
#include <graphene/peerplays_sidechain/sidechain_net_handler.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
class sidechain_net_manager {
|
||||
public:
|
||||
sidechain_net_manager(peerplays_sidechain_plugin &_plugin);
|
||||
virtual ~sidechain_net_manager();
|
||||
|
||||
bool create_handler(sidechain_type sidechain, const boost::program_options::variables_map &options);
|
||||
void recreate_primary_wallet();
|
||||
void process_deposits();
|
||||
void process_withdrawals();
|
||||
void process_sidechain_transactions();
|
||||
void send_sidechain_transactions();
|
||||
|
||||
private:
|
||||
peerplays_sidechain_plugin &plugin;
|
||||
graphene::chain::database &database;
|
||||
std::vector<std::unique_ptr<sidechain_net_handler>> net_handlers;
|
||||
};
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
|
@ -1,74 +1,658 @@
|
|||
#include <graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp>
|
||||
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/range/algorithm_ext/insert.hpp>
|
||||
|
||||
#include <fc/log/logger.hpp>
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
|
||||
#include <graphene/chain/proposal_object.hpp>
|
||||
#include <graphene/chain/protocol/transfer.hpp>
|
||||
#include <graphene/chain/sidechain_address_object.hpp>
|
||||
#include <graphene/chain/son_wallet_object.hpp>
|
||||
#include <graphene/chain/son_wallet_withdraw_object.hpp>
|
||||
#include <graphene/peerplays_sidechain/sidechain_net_manager.hpp>
|
||||
#include <graphene/utilities/key_conversion.hpp>
|
||||
|
||||
namespace bpo = boost::program_options;
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
namespace detail
|
||||
{
|
||||
namespace detail {
|
||||
|
||||
class peerplays_sidechain_plugin_impl {
|
||||
public:
|
||||
peerplays_sidechain_plugin_impl(peerplays_sidechain_plugin &_plugin);
|
||||
virtual ~peerplays_sidechain_plugin_impl();
|
||||
|
||||
class peerplays_sidechain_plugin_impl
|
||||
{
|
||||
public:
|
||||
peerplays_sidechain_plugin_impl(peerplays_sidechain_plugin& _plugin)
|
||||
: _self( _plugin )
|
||||
{ }
|
||||
virtual ~peerplays_sidechain_plugin_impl();
|
||||
void plugin_set_program_options(
|
||||
boost::program_options::options_description &cli,
|
||||
boost::program_options::options_description &cfg);
|
||||
void plugin_initialize(const boost::program_options::variables_map &opt);
|
||||
void plugin_startup();
|
||||
|
||||
peerplays_sidechain_plugin& _self;
|
||||
std::set<chain::son_id_type> &get_sons();
|
||||
const son_id_type get_current_son_id();
|
||||
const son_object get_current_son_object();
|
||||
const son_object get_son_object(son_id_type son_id);
|
||||
bool is_active_son(son_id_type son_id);
|
||||
fc::ecc::private_key get_private_key(son_id_type son_id);
|
||||
fc::ecc::private_key get_private_key(chain::public_key_type public_key);
|
||||
|
||||
uint32_t parameter;
|
||||
uint32_t optional_parameter;
|
||||
void schedule_heartbeat_loop();
|
||||
void heartbeat_loop();
|
||||
void schedule_son_processing();
|
||||
void son_processing();
|
||||
void approve_proposals();
|
||||
void create_son_down_proposals();
|
||||
void create_son_deregister_proposals();
|
||||
void recreate_primary_wallet();
|
||||
void process_deposits();
|
||||
void process_withdrawals();
|
||||
void process_sidechain_transactions();
|
||||
void send_sidechain_transactions();
|
||||
|
||||
private:
|
||||
peerplays_sidechain_plugin &plugin;
|
||||
|
||||
boost::program_options::variables_map options;
|
||||
|
||||
bool config_ready_son;
|
||||
bool config_ready_bitcoin;
|
||||
bool config_ready_peerplays;
|
||||
|
||||
son_id_type current_son_id;
|
||||
|
||||
std::unique_ptr<peerplays_sidechain::sidechain_net_manager> net_manager;
|
||||
std::set<chain::son_id_type> sons;
|
||||
std::map<chain::public_key_type, fc::ecc::private_key> private_keys;
|
||||
fc::future<void> _heartbeat_task;
|
||||
fc::future<void> _son_processing_task;
|
||||
|
||||
bool first_block_skipped;
|
||||
void on_applied_block(const signed_block &b);
|
||||
};
|
||||
|
||||
peerplays_sidechain_plugin_impl::~peerplays_sidechain_plugin_impl()
|
||||
{
|
||||
return;
|
||||
peerplays_sidechain_plugin_impl::peerplays_sidechain_plugin_impl(peerplays_sidechain_plugin &_plugin) :
|
||||
plugin(_plugin),
|
||||
config_ready_son(false),
|
||||
config_ready_bitcoin(false),
|
||||
config_ready_peerplays(false),
|
||||
current_son_id(son_id_type(std::numeric_limits<uint32_t>().max())),
|
||||
net_manager(nullptr),
|
||||
first_block_skipped(false) {
|
||||
}
|
||||
|
||||
} // end namespace detail
|
||||
peerplays_sidechain_plugin_impl::~peerplays_sidechain_plugin_impl() {
|
||||
try {
|
||||
if (_heartbeat_task.valid())
|
||||
_heartbeat_task.cancel_and_wait(__FUNCTION__);
|
||||
} catch (fc::canceled_exception &) {
|
||||
//Expected exception. Move along.
|
||||
} catch (fc::exception &e) {
|
||||
edump((e.to_detail_string()));
|
||||
}
|
||||
|
||||
try {
|
||||
if (_son_processing_task.valid())
|
||||
_son_processing_task.cancel_and_wait(__FUNCTION__);
|
||||
} catch (fc::canceled_exception &) {
|
||||
//Expected exception. Move along.
|
||||
} catch (fc::exception &e) {
|
||||
edump((e.to_detail_string()));
|
||||
}
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::plugin_set_program_options(
|
||||
boost::program_options::options_description &cli,
|
||||
boost::program_options::options_description &cfg) {
|
||||
auto default_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(std::string("nathan")));
|
||||
string son_id_example = fc::json::to_string(chain::son_id_type(5));
|
||||
string son_id_example2 = fc::json::to_string(chain::son_id_type(6));
|
||||
|
||||
cli.add_options()("son-id", bpo::value<vector<string>>(), ("ID of SON controlled by this node (e.g. " + son_id_example + ", quotes are required)").c_str());
|
||||
cli.add_options()("son-ids", bpo::value<string>(), ("IDs of multiple SONs controlled by this node (e.g. [" + son_id_example + ", " + son_id_example2 + "], quotes are required)").c_str());
|
||||
cli.add_options()("peerplays-private-key", bpo::value<vector<string>>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR(std::make_pair(chain::public_key_type(default_priv_key.get_public_key()), graphene::utilities::key_to_wif(default_priv_key))),
|
||||
"Tuple of [PublicKey, WIF private key] (may specify multiple times)");
|
||||
cli.add_options()("bitcoin-node-ip", bpo::value<string>()->default_value("127.0.0.1"), "IP address of Bitcoin node");
|
||||
cli.add_options()("bitcoin-node-zmq-port", bpo::value<uint32_t>()->default_value(11111), "ZMQ port of Bitcoin node");
|
||||
cli.add_options()("bitcoin-node-rpc-port", bpo::value<uint32_t>()->default_value(8332), "RPC port of Bitcoin node");
|
||||
cli.add_options()("bitcoin-node-rpc-user", bpo::value<string>()->default_value("1"), "Bitcoin RPC user");
|
||||
cli.add_options()("bitcoin-node-rpc-password", bpo::value<string>()->default_value("1"), "Bitcoin RPC password");
|
||||
cli.add_options()("bitcoin-wallet", bpo::value<string>(), "Bitcoin wallet");
|
||||
cli.add_options()("bitcoin-wallet-password", bpo::value<string>(), "Bitcoin wallet password");
|
||||
cli.add_options()("bitcoin-private-key", bpo::value<vector<string>>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR(std::make_pair("02d0f137e717fb3aab7aff99904001d49a0a636c5e1342f8927a4ba2eaee8e9772", "cVN31uC9sTEr392DLVUEjrtMgLA8Yb3fpYmTRj7bomTm6nn2ANPr")),
|
||||
"Tuple of [Bitcoin public key, Bitcoin private key] (may specify multiple times)");
|
||||
cfg.add(cli);
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_options::variables_map &opt) {
|
||||
options = opt;
|
||||
config_ready_son = (options.count("son-id") || options.count("son-ids")) && options.count("peerplays-private-key");
|
||||
if (config_ready_son) {
|
||||
LOAD_VALUE_SET(options, "son-id", sons, chain::son_id_type)
|
||||
if (options.count("son-ids"))
|
||||
boost::insert(sons, fc::json::from_string(options.at("son-ids").as<string>()).as<vector<chain::son_id_type>>(5));
|
||||
config_ready_son = config_ready_son && !sons.empty();
|
||||
|
||||
#ifndef SUPPORT_MULTIPLE_SONS
|
||||
FC_ASSERT(sons.size() == 1, "Multiple SONs not supported");
|
||||
#endif
|
||||
|
||||
if (options.count("peerplays-private-key")) {
|
||||
const std::vector<std::string> key_id_to_wif_pair_strings = options["peerplays-private-key"].as<std::vector<std::string>>();
|
||||
for (const std::string &key_id_to_wif_pair_string : key_id_to_wif_pair_strings) {
|
||||
auto key_id_to_wif_pair = graphene::app::dejsonify<std::pair<chain::public_key_type, std::string>>(key_id_to_wif_pair_string, 5);
|
||||
ilog("Public Key: ${public}", ("public", key_id_to_wif_pair.first));
|
||||
fc::optional<fc::ecc::private_key> private_key = graphene::utilities::wif_to_key(key_id_to_wif_pair.second);
|
||||
if (!private_key) {
|
||||
// the key isn't in WIF format; see if they are still passing the old native private key format. This is
|
||||
// just here to ease the transition, can be removed soon
|
||||
try {
|
||||
private_key = fc::variant(key_id_to_wif_pair.second, 2).as<fc::ecc::private_key>(1);
|
||||
} catch (const fc::exception &) {
|
||||
FC_THROW("Invalid WIF-format private key ${key_string}", ("key_string", key_id_to_wif_pair.second));
|
||||
}
|
||||
}
|
||||
private_keys[key_id_to_wif_pair.first] = *private_key;
|
||||
}
|
||||
config_ready_son = config_ready_son && !private_keys.empty();
|
||||
}
|
||||
}
|
||||
if (!config_ready_son) {
|
||||
wlog("Haven't set up SON parameters");
|
||||
throw;
|
||||
}
|
||||
|
||||
config_ready_bitcoin = options.count("bitcoin-node-ip") &&
|
||||
options.count("bitcoin-node-zmq-port") && options.count("bitcoin-node-rpc-port") &&
|
||||
options.count("bitcoin-node-rpc-user") && options.count("bitcoin-node-rpc-password") &&
|
||||
/*options.count( "bitcoin-wallet" ) && options.count( "bitcoin-wallet-password" ) &&*/
|
||||
options.count("bitcoin-private-key");
|
||||
if (!config_ready_bitcoin) {
|
||||
wlog("Haven't set up Bitcoin sidechain parameters");
|
||||
}
|
||||
|
||||
//config_ready_ethereum = options.count( "ethereum-node-ip" ) &&
|
||||
// options.count( "ethereum-address" ) && options.count( "ethereum-public-key" ) && options.count( "ethereum-private-key" );
|
||||
//if (!config_ready_ethereum) {
|
||||
// wlog("Haven't set up Ethereum sidechain parameters");
|
||||
//}
|
||||
|
||||
config_ready_peerplays = true;
|
||||
if (!config_ready_peerplays) {
|
||||
wlog("Haven't set up Peerplays sidechain parameters");
|
||||
}
|
||||
|
||||
if (!(config_ready_bitcoin /*&& config_ready_ethereum*/ && config_ready_peerplays)) {
|
||||
wlog("Haven't set up any sidechain parameters");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::plugin_startup() {
|
||||
|
||||
if (config_ready_son) {
|
||||
ilog("Starting ${n} SON instances", ("n", sons.size()));
|
||||
|
||||
schedule_heartbeat_loop();
|
||||
} else {
|
||||
elog("No sons configured! Please add SON IDs and private keys to configuration.");
|
||||
}
|
||||
|
||||
net_manager = std::unique_ptr<sidechain_net_manager>(new sidechain_net_manager(plugin));
|
||||
|
||||
if (config_ready_bitcoin) {
|
||||
net_manager->create_handler(sidechain_type::bitcoin, options);
|
||||
ilog("Bitcoin sidechain handler running");
|
||||
}
|
||||
|
||||
//if (config_ready_ethereum) {
|
||||
// net_manager->create_handler(sidechain_type::ethereum, options);
|
||||
// ilog("Ethereum sidechain handler running");
|
||||
//}
|
||||
|
||||
if (config_ready_peerplays) {
|
||||
net_manager->create_handler(sidechain_type::peerplays, options);
|
||||
ilog("Peerplays sidechain handler running");
|
||||
}
|
||||
|
||||
plugin.database().applied_block.connect([&](const signed_block &b) {
|
||||
on_applied_block(b);
|
||||
});
|
||||
}
|
||||
|
||||
std::set<chain::son_id_type> &peerplays_sidechain_plugin_impl::get_sons() {
|
||||
return sons;
|
||||
}
|
||||
|
||||
const son_id_type peerplays_sidechain_plugin_impl::get_current_son_id() {
|
||||
return current_son_id;
|
||||
}
|
||||
|
||||
const son_object peerplays_sidechain_plugin_impl::get_current_son_object() {
|
||||
return get_son_object(current_son_id);
|
||||
}
|
||||
|
||||
const son_object peerplays_sidechain_plugin_impl::get_son_object(son_id_type son_id) {
|
||||
const auto &idx = plugin.database().get_index_type<chain::son_index>().indices().get<by_id>();
|
||||
auto son_obj = idx.find(son_id);
|
||||
if (son_obj == idx.end())
|
||||
return {};
|
||||
return *son_obj;
|
||||
}
|
||||
|
||||
bool peerplays_sidechain_plugin_impl::is_active_son(son_id_type son_id) {
|
||||
const auto &idx = plugin.database().get_index_type<chain::son_index>().indices().get<by_id>();
|
||||
auto son_obj = idx.find(son_id);
|
||||
if (son_obj == idx.end())
|
||||
return false;
|
||||
|
||||
const chain::global_property_object &gpo = plugin.database().get_global_properties();
|
||||
vector<son_id_type> active_son_ids;
|
||||
active_son_ids.reserve(gpo.active_sons.size());
|
||||
std::transform(gpo.active_sons.begin(), gpo.active_sons.end(),
|
||||
std::inserter(active_son_ids, active_son_ids.end()),
|
||||
[](const son_info &swi) {
|
||||
return swi.son_id;
|
||||
});
|
||||
|
||||
auto it = std::find(active_son_ids.begin(), active_son_ids.end(), son_id);
|
||||
|
||||
return (it != active_son_ids.end());
|
||||
}
|
||||
|
||||
fc::ecc::private_key peerplays_sidechain_plugin_impl::get_private_key(son_id_type son_id) {
|
||||
return get_private_key(get_son_object(son_id).signing_key);
|
||||
}
|
||||
|
||||
fc::ecc::private_key peerplays_sidechain_plugin_impl::get_private_key(chain::public_key_type public_key) {
|
||||
auto private_key_itr = private_keys.find(public_key);
|
||||
if (private_key_itr != private_keys.end()) {
|
||||
return private_key_itr->second;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::schedule_heartbeat_loop() {
|
||||
fc::time_point now = fc::time_point::now();
|
||||
int64_t time_to_next_heartbeat = plugin.database().get_global_properties().parameters.son_heartbeat_frequency();
|
||||
|
||||
fc::time_point next_wakeup(now + fc::seconds(time_to_next_heartbeat));
|
||||
|
||||
_heartbeat_task = fc::schedule([this] {
|
||||
heartbeat_loop();
|
||||
},
|
||||
next_wakeup, "SON Heartbeat Production");
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::heartbeat_loop() {
|
||||
schedule_heartbeat_loop();
|
||||
chain::database &d = plugin.database();
|
||||
|
||||
for (son_id_type son_id : sons) {
|
||||
if (is_active_son(son_id) || get_son_object(son_id).status == chain::son_status::in_maintenance) {
|
||||
|
||||
ilog("Sending heartbeat for SON ${son}", ("son", son_id));
|
||||
chain::son_heartbeat_operation op;
|
||||
op.owner_account = get_son_object(son_id).son_account;
|
||||
op.son_id = son_id;
|
||||
op.ts = fc::time_point::now() + fc::seconds(0);
|
||||
chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(son_id), op);
|
||||
fc::future<bool> fut = fc::async([&]() {
|
||||
try {
|
||||
d.push_transaction(trx, database::validation_steps::skip_block_size_check);
|
||||
if (plugin.app().p2p_node())
|
||||
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
|
||||
return true;
|
||||
} catch (fc::exception e) {
|
||||
elog("Sending heartbeat failed with exception ${e}", ("e", e.what()));
|
||||
return false;
|
||||
}
|
||||
});
|
||||
fut.wait(fc::seconds(10));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::schedule_son_processing() {
|
||||
fc::time_point now = fc::time_point::now();
|
||||
int64_t time_to_next_son_processing = 500000;
|
||||
|
||||
fc::time_point next_wakeup(now + fc::microseconds(time_to_next_son_processing));
|
||||
|
||||
_son_processing_task = fc::schedule([this] {
|
||||
son_processing();
|
||||
},
|
||||
next_wakeup, "SON Processing");
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::son_processing() {
|
||||
if (plugin.database().get_global_properties().active_sons.size() <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
fc::time_point now_fine = fc::time_point::now();
|
||||
fc::time_point_sec now = now_fine + fc::microseconds(500000);
|
||||
if (plugin.database().get_slot_time(1) < now) {
|
||||
return; // Not synced
|
||||
}
|
||||
|
||||
chain::son_id_type scheduled_son_id = plugin.database().get_scheduled_son(1);
|
||||
ilog("Scheduled SON: ${scheduled_son_id} Now: ${now} ",
|
||||
("scheduled_son_id", scheduled_son_id)("now", now));
|
||||
|
||||
for (son_id_type son_id : plugin.get_sons()) {
|
||||
|
||||
if (plugin.is_active_son(son_id)) {
|
||||
|
||||
current_son_id = son_id;
|
||||
|
||||
// Tasks that are executed by all active SONs, no matter if scheduled
|
||||
// E.g. sending approvals and signing (only signing that can be done in parallel)
|
||||
approve_proposals();
|
||||
|
||||
// Tasks that are executed by scheduled and active SON
|
||||
if (current_son_id == scheduled_son_id) {
|
||||
|
||||
create_son_down_proposals();
|
||||
|
||||
create_son_deregister_proposals();
|
||||
|
||||
recreate_primary_wallet();
|
||||
|
||||
process_deposits();
|
||||
|
||||
process_withdrawals();
|
||||
|
||||
process_sidechain_transactions();
|
||||
|
||||
send_sidechain_transactions();
|
||||
}
|
||||
} else {
|
||||
// Tasks that are executed by previously active SONs
|
||||
// E.g. sending approvals and signing that SON was required to do while it was active
|
||||
//approve_leftover_proposals(); ???
|
||||
//process_leftover_sidechain_transactions(); ???
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::approve_proposals() {
|
||||
|
||||
auto approve_proposal = [&](const chain::son_id_type &son_id, const chain::proposal_id_type &proposal_id) {
|
||||
ilog("Sending approval for ${p} from ${s}", ("p", proposal_id)("s", son_id));
|
||||
chain::proposal_update_operation puo;
|
||||
puo.fee_paying_account = get_son_object(son_id).son_account;
|
||||
puo.proposal = proposal_id;
|
||||
puo.active_approvals_to_add = {get_son_object(son_id).son_account};
|
||||
chain::signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(son_id), puo);
|
||||
fc::future<bool> fut = fc::async([&]() {
|
||||
try {
|
||||
plugin.database().push_transaction(trx, database::validation_steps::skip_block_size_check);
|
||||
if (plugin.app().p2p_node())
|
||||
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
|
||||
return true;
|
||||
} catch (fc::exception e) {
|
||||
elog("Sending approval failed with exception ${e}", ("e", e.what()));
|
||||
return false;
|
||||
}
|
||||
});
|
||||
fut.wait(fc::seconds(10));
|
||||
};
|
||||
|
||||
const auto &idx = plugin.database().get_index_type<proposal_index>().indices().get<by_id>();
|
||||
vector<proposal_id_type> proposals;
|
||||
for (const auto &proposal : idx) {
|
||||
proposals.push_back(proposal.id);
|
||||
}
|
||||
|
||||
for (const auto proposal_id : proposals) {
|
||||
|
||||
const object *obj = plugin.database().find_object(proposal_id);
|
||||
const chain::proposal_object *proposal_ptr = dynamic_cast<const chain::proposal_object *>(obj);
|
||||
if (proposal_ptr == nullptr) {
|
||||
continue;
|
||||
}
|
||||
const proposal_object proposal = *proposal_ptr;
|
||||
|
||||
if (proposal.available_active_approvals.find(get_current_son_object().son_account) != proposal.available_active_approvals.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (proposal.proposed_transaction.operations.size() == 1) {
|
||||
int32_t op_idx_0 = proposal.proposed_transaction.operations[0].which();
|
||||
|
||||
if (op_idx_0 == chain::operation::tag<chain::son_report_down_operation>::value) {
|
||||
approve_proposal(get_current_son_id(), proposal.id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (op_idx_0 == chain::operation::tag<chain::son_delete_operation>::value) {
|
||||
approve_proposal(get_current_son_id(), proposal.id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (op_idx_0 == chain::operation::tag<chain::son_wallet_update_operation>::value) {
|
||||
approve_proposal(get_current_son_id(), proposal.id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (op_idx_0 == chain::operation::tag<chain::son_wallet_deposit_create_operation>::value) {
|
||||
approve_proposal(get_current_son_id(), proposal.id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (op_idx_0 == chain::operation::tag<chain::son_wallet_withdraw_create_operation>::value) {
|
||||
approve_proposal(get_current_son_id(), proposal.id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (op_idx_0 == chain::operation::tag<chain::son_wallet_withdraw_process_operation>::value) {
|
||||
approve_proposal(get_current_son_id(), proposal.id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (op_idx_0 == chain::operation::tag<chain::sidechain_transaction_create_operation>::value) {
|
||||
approve_proposal(get_current_son_id(), proposal.id);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (proposal.proposed_transaction.operations.size() == 2) {
|
||||
int32_t op_idx_0 = proposal.proposed_transaction.operations[0].which();
|
||||
int32_t op_idx_1 = proposal.proposed_transaction.operations[1].which();
|
||||
|
||||
if ((op_idx_0 == chain::operation::tag<chain::son_wallet_deposit_process_operation>::value) &&
|
||||
(op_idx_1 == chain::operation::tag<chain::transfer_operation>::value)) {
|
||||
approve_proposal(get_current_son_id(), proposal.id);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
ilog("==================================================");
|
||||
ilog("Proposal not approved ${proposal}", ("proposal", proposal));
|
||||
ilog("==================================================");
|
||||
}
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::create_son_down_proposals() {
|
||||
auto create_son_down_proposal = [&](chain::son_id_type son_id, fc::time_point_sec last_active_ts) {
|
||||
chain::database &d = plugin.database();
|
||||
const chain::global_property_object &gpo = d.get_global_properties();
|
||||
|
||||
chain::son_report_down_operation son_down_op;
|
||||
son_down_op.payer = d.get_global_properties().parameters.son_account();
|
||||
son_down_op.son_id = son_id;
|
||||
son_down_op.down_ts = last_active_ts;
|
||||
|
||||
proposal_create_operation proposal_op;
|
||||
proposal_op.fee_paying_account = get_current_son_object().son_account;
|
||||
proposal_op.proposed_ops.emplace_back(op_wrapper(son_down_op));
|
||||
uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3;
|
||||
proposal_op.expiration_time = time_point_sec(d.head_block_time().sec_since_epoch() + lifetime);
|
||||
return proposal_op;
|
||||
};
|
||||
|
||||
chain::database &d = plugin.database();
|
||||
const chain::global_property_object &gpo = d.get_global_properties();
|
||||
const chain::dynamic_global_property_object &dgpo = d.get_dynamic_global_properties();
|
||||
const auto &idx = d.get_index_type<chain::son_index>().indices().get<by_id>();
|
||||
std::set<son_id_type> sons_being_reported_down = d.get_sons_being_reported_down();
|
||||
chain::son_id_type my_son_id = get_current_son_id();
|
||||
for (auto son_inf : gpo.active_sons) {
|
||||
if (my_son_id == son_inf.son_id || (sons_being_reported_down.find(son_inf.son_id) != sons_being_reported_down.end())) {
|
||||
continue;
|
||||
}
|
||||
auto son_obj = idx.find(son_inf.son_id);
|
||||
auto stats = son_obj->statistics(d);
|
||||
fc::time_point_sec last_maintenance_time = dgpo.next_maintenance_time - gpo.parameters.maintenance_interval;
|
||||
fc::time_point_sec last_active_ts = ((stats.last_active_timestamp > last_maintenance_time) ? stats.last_active_timestamp : last_maintenance_time);
|
||||
int64_t down_threshold = gpo.parameters.son_down_time();
|
||||
if (((son_obj->status == chain::son_status::active) || (son_obj->status == chain::son_status::request_maintenance)) &&
|
||||
((fc::time_point::now() - last_active_ts) > fc::seconds(down_threshold))) {
|
||||
ilog("Sending son down proposal for ${t} from ${s}", ("t", std::string(object_id_type(son_obj->id)))("s", std::string(object_id_type(my_son_id))));
|
||||
chain::proposal_create_operation op = create_son_down_proposal(son_inf.son_id, last_active_ts);
|
||||
chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(get_son_object(my_son_id).signing_key), op);
|
||||
fc::future<bool> fut = fc::async([&]() {
|
||||
try {
|
||||
d.push_transaction(trx, database::validation_steps::skip_block_size_check);
|
||||
if (plugin.app().p2p_node())
|
||||
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
|
||||
return true;
|
||||
} catch (fc::exception e) {
|
||||
elog("Sending son down proposal failed with exception ${e}", ("e", e.what()));
|
||||
return false;
|
||||
}
|
||||
});
|
||||
fut.wait(fc::seconds(10));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::create_son_deregister_proposals() {
|
||||
chain::database &d = plugin.database();
|
||||
std::set<son_id_type> sons_to_be_dereg = d.get_sons_to_be_deregistered();
|
||||
chain::son_id_type my_son_id = get_current_son_id();
|
||||
|
||||
if (sons_to_be_dereg.size() > 0) {
|
||||
// We shouldn't raise proposals for the SONs for which a de-reg
|
||||
// proposal is already raised.
|
||||
std::set<son_id_type> sons_being_dereg = d.get_sons_being_deregistered();
|
||||
for (auto &son : sons_to_be_dereg) {
|
||||
// New SON to be deregistered
|
||||
if (sons_being_dereg.find(son) == sons_being_dereg.end() && my_son_id != son) {
|
||||
// Creating the de-reg proposal
|
||||
auto op = d.create_son_deregister_proposal(son, get_son_object(my_son_id).son_account);
|
||||
if (op.valid()) {
|
||||
// Signing and pushing into the txs to be included in the block
|
||||
ilog("Sending son deregister proposal for ${p} from ${s}", ("p", son)("s", my_son_id));
|
||||
chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(get_son_object(my_son_id).signing_key), *op);
|
||||
fc::future<bool> fut = fc::async([&]() {
|
||||
try {
|
||||
d.push_transaction(trx, database::validation_steps::skip_block_size_check);
|
||||
if (plugin.app().p2p_node())
|
||||
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
|
||||
return true;
|
||||
} catch (fc::exception e) {
|
||||
elog("Sending son deregister proposal failed with exception ${e}", ("e", e.what()));
|
||||
return false;
|
||||
}
|
||||
});
|
||||
fut.wait(fc::seconds(10));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::recreate_primary_wallet() {
|
||||
net_manager->recreate_primary_wallet();
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::process_deposits() {
|
||||
net_manager->process_deposits();
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::process_withdrawals() {
|
||||
net_manager->process_withdrawals();
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::process_sidechain_transactions() {
|
||||
net_manager->process_sidechain_transactions();
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::send_sidechain_transactions() {
|
||||
net_manager->send_sidechain_transactions();
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::on_applied_block(const signed_block &b) {
|
||||
if (first_block_skipped) {
|
||||
schedule_son_processing();
|
||||
} else {
|
||||
first_block_skipped = true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
peerplays_sidechain_plugin::peerplays_sidechain_plugin() :
|
||||
my( new detail::peerplays_sidechain_plugin_impl(*this) )
|
||||
{
|
||||
my(new detail::peerplays_sidechain_plugin_impl(*this)) {
|
||||
}
|
||||
|
||||
peerplays_sidechain_plugin::~peerplays_sidechain_plugin()
|
||||
{
|
||||
peerplays_sidechain_plugin::~peerplays_sidechain_plugin() {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string peerplays_sidechain_plugin::plugin_name()const
|
||||
{
|
||||
std::string peerplays_sidechain_plugin::plugin_name() const {
|
||||
return "peerplays_sidechain";
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin::plugin_set_program_options(
|
||||
boost::program_options::options_description& cli,
|
||||
boost::program_options::options_description& cfg
|
||||
)
|
||||
{
|
||||
cli.add_options()
|
||||
("parameter", boost::program_options::value<uint32_t>(), "Parameter")
|
||||
("optional-parameter", boost::program_options::value<uint32_t>(), "Optional parameter")
|
||||
;
|
||||
cfg.add(cli);
|
||||
boost::program_options::options_description &cli,
|
||||
boost::program_options::options_description &cfg) {
|
||||
my->plugin_set_program_options(cli, cfg);
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin::plugin_initialize(const boost::program_options::variables_map& options)
|
||||
{
|
||||
ilog("peerplays sidechain plugin: plugin_initialize()");
|
||||
|
||||
if (options.count("parameter")) {
|
||||
my->parameter = options["optional-parameter"].as<uint32_t>();
|
||||
}
|
||||
if (options.count("optional-parameter")) {
|
||||
my->optional_parameter = options["optional-parameter"].as<uint32_t>();
|
||||
}
|
||||
void peerplays_sidechain_plugin::plugin_initialize(const boost::program_options::variables_map &options) {
|
||||
ilog("peerplays sidechain plugin: plugin_initialize() begin");
|
||||
my->plugin_initialize(options);
|
||||
ilog("peerplays sidechain plugin: plugin_initialize() end");
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin::plugin_startup()
|
||||
{
|
||||
ilog("peerplays sidechain plugin: plugin_startup()");
|
||||
void peerplays_sidechain_plugin::plugin_startup() {
|
||||
ilog("peerplays sidechain plugin: plugin_startup() begin");
|
||||
my->plugin_startup();
|
||||
ilog("peerplays sidechain plugin: plugin_startup() end");
|
||||
}
|
||||
|
||||
} }
|
||||
std::set<chain::son_id_type> &peerplays_sidechain_plugin::get_sons() {
|
||||
return my->get_sons();
|
||||
}
|
||||
|
||||
const son_id_type peerplays_sidechain_plugin::get_current_son_id() {
|
||||
return my->get_current_son_id();
|
||||
}
|
||||
|
||||
const son_object peerplays_sidechain_plugin::get_current_son_object() {
|
||||
return my->get_current_son_object();
|
||||
}
|
||||
|
||||
const son_object peerplays_sidechain_plugin::get_son_object(son_id_type son_id) {
|
||||
return my->get_son_object(son_id);
|
||||
}
|
||||
|
||||
bool peerplays_sidechain_plugin::is_active_son(son_id_type son_id) {
|
||||
return my->is_active_son(son_id);
|
||||
}
|
||||
|
||||
fc::ecc::private_key peerplays_sidechain_plugin::get_private_key(son_id_type son_id) {
|
||||
return my->get_private_key(son_id);
|
||||
}
|
||||
|
||||
fc::ecc::private_key peerplays_sidechain_plugin::get_private_key(chain::public_key_type public_key) {
|
||||
return my->get_private_key(public_key);
|
||||
}
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
|
|
|||
327
libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp
Normal file
327
libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp
Normal file
|
|
@ -0,0 +1,327 @@
|
|||
#include <graphene/peerplays_sidechain/sidechain_net_handler.hpp>
|
||||
|
||||
#include <fc/log/logger.hpp>
|
||||
#include <fc/smart_ref_fwd.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
sidechain_net_handler::sidechain_net_handler(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) :
|
||||
plugin(_plugin),
|
||||
database(_plugin.database()) {
|
||||
}
|
||||
|
||||
sidechain_net_handler::~sidechain_net_handler() {
|
||||
}
|
||||
|
||||
sidechain_type sidechain_net_handler::get_sidechain() {
|
||||
return sidechain;
|
||||
}
|
||||
|
||||
std::vector<std::string> sidechain_net_handler::get_sidechain_deposit_addresses() {
|
||||
std::vector<std::string> result;
|
||||
|
||||
const auto &sidechain_addresses_idx = database.get_index_type<sidechain_address_index>();
|
||||
const auto &sidechain_addresses_by_sidechain_idx = sidechain_addresses_idx.indices().get<by_sidechain>();
|
||||
const auto &sidechain_addresses_by_sidechain_range = sidechain_addresses_by_sidechain_idx.equal_range(sidechain);
|
||||
std::for_each(sidechain_addresses_by_sidechain_range.first, sidechain_addresses_by_sidechain_range.second,
|
||||
[&result](const sidechain_address_object &sao) {
|
||||
result.push_back(sao.deposit_address);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::string> sidechain_net_handler::get_sidechain_withdraw_addresses() {
|
||||
std::vector<std::string> result;
|
||||
|
||||
const auto &sidechain_addresses_idx = database.get_index_type<sidechain_address_index>();
|
||||
const auto &sidechain_addresses_by_sidechain_idx = sidechain_addresses_idx.indices().get<by_sidechain>();
|
||||
const auto &sidechain_addresses_by_sidechain_range = sidechain_addresses_by_sidechain_idx.equal_range(sidechain);
|
||||
std::for_each(sidechain_addresses_by_sidechain_range.first, sidechain_addresses_by_sidechain_range.second,
|
||||
[&result](const sidechain_address_object &sao) {
|
||||
result.push_back(sao.withdraw_address);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string sidechain_net_handler::get_private_key(std::string public_key) {
|
||||
auto private_key_itr = private_keys.find(public_key);
|
||||
if (private_key_itr != private_keys.end()) {
|
||||
return private_key_itr->second;
|
||||
}
|
||||
return std::string();
|
||||
}
|
||||
|
||||
void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_data &sed) {
|
||||
ilog("sidechain_event_data:");
|
||||
ilog(" timestamp: ${timestamp}", ("timestamp", sed.timestamp));
|
||||
ilog(" sidechain: ${sidechain}", ("sidechain", sed.sidechain));
|
||||
ilog(" sidechain_uid: ${uid}", ("uid", sed.sidechain_uid));
|
||||
ilog(" sidechain_transaction_id: ${transaction_id}", ("transaction_id", sed.sidechain_transaction_id));
|
||||
ilog(" sidechain_from: ${from}", ("from", sed.sidechain_from));
|
||||
ilog(" sidechain_to: ${to}", ("to", sed.sidechain_to));
|
||||
ilog(" sidechain_currency: ${currency}", ("currency", sed.sidechain_currency));
|
||||
ilog(" sidechain_amount: ${amount}", ("amount", sed.sidechain_amount));
|
||||
ilog(" peerplays_from: ${peerplays_from}", ("peerplays_from", sed.peerplays_from));
|
||||
ilog(" peerplays_to: ${peerplays_to}", ("peerplays_to", sed.peerplays_to));
|
||||
ilog(" peerplays_asset: ${peerplays_asset}", ("peerplays_asset", sed.peerplays_asset));
|
||||
|
||||
const chain::global_property_object &gpo = database.get_global_properties();
|
||||
|
||||
// Deposit request
|
||||
if ((sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain_currency.compare("1.3.0") != 0)) {
|
||||
son_wallet_deposit_create_operation op;
|
||||
op.payer = gpo.parameters.son_account();
|
||||
//op.son_id = ; // to be filled for each son
|
||||
op.timestamp = sed.timestamp;
|
||||
op.sidechain = sed.sidechain;
|
||||
op.sidechain_uid = sed.sidechain_uid;
|
||||
op.sidechain_transaction_id = sed.sidechain_transaction_id;
|
||||
op.sidechain_from = sed.sidechain_from;
|
||||
op.sidechain_to = sed.sidechain_to;
|
||||
op.sidechain_currency = sed.sidechain_currency;
|
||||
op.sidechain_amount = sed.sidechain_amount;
|
||||
op.peerplays_from = sed.peerplays_from;
|
||||
op.peerplays_to = sed.peerplays_to;
|
||||
op.peerplays_asset = sed.peerplays_asset;
|
||||
|
||||
for (son_id_type son_id : plugin.get_sons()) {
|
||||
if (plugin.is_active_son(son_id)) {
|
||||
|
||||
op.son_id = son_id;
|
||||
|
||||
proposal_create_operation proposal_op;
|
||||
proposal_op.fee_paying_account = plugin.get_son_object(son_id).son_account;
|
||||
proposal_op.proposed_ops.emplace_back(op);
|
||||
uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3;
|
||||
proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime);
|
||||
|
||||
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(son_id), proposal_op);
|
||||
try {
|
||||
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
|
||||
if (plugin.app().p2p_node())
|
||||
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
|
||||
} catch (fc::exception e) {
|
||||
elog("Sending proposal for son wallet deposit create operation by ${son} failed with exception ${e}", ("son", son_id)("e", e.what()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Withdrawal request
|
||||
if ((sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain_currency.compare("1.3.0") == 0)) {
|
||||
// BTC Payout only (for now)
|
||||
const auto &sidechain_addresses_idx = database.get_index_type<sidechain_address_index>().indices().get<by_account_and_sidechain>();
|
||||
const auto &addr_itr = sidechain_addresses_idx.find(std::make_tuple(sed.peerplays_from, sidechain_type::bitcoin));
|
||||
if (addr_itr == sidechain_addresses_idx.end())
|
||||
return;
|
||||
|
||||
son_wallet_withdraw_create_operation op;
|
||||
op.payer = gpo.parameters.son_account();
|
||||
//op.son_id = ; // to be filled for each son
|
||||
op.timestamp = sed.timestamp;
|
||||
op.sidechain = sed.sidechain;
|
||||
op.peerplays_uid = sed.sidechain_uid;
|
||||
op.peerplays_transaction_id = sed.sidechain_transaction_id;
|
||||
op.peerplays_from = sed.peerplays_from;
|
||||
op.peerplays_asset = sed.peerplays_asset;
|
||||
op.withdraw_sidechain = sidechain_type::bitcoin; // BTC payout only (for now)
|
||||
op.withdraw_address = addr_itr->withdraw_address; // BTC payout only (for now)
|
||||
op.withdraw_currency = "BTC"; // BTC payout only (for now)
|
||||
op.withdraw_amount = sed.peerplays_asset.amount * 1000; // BTC payout only (for now)
|
||||
|
||||
for (son_id_type son_id : plugin.get_sons()) {
|
||||
if (plugin.is_active_son(son_id)) {
|
||||
|
||||
op.son_id = son_id;
|
||||
|
||||
proposal_create_operation proposal_op;
|
||||
proposal_op.fee_paying_account = plugin.get_son_object(son_id).son_account;
|
||||
proposal_op.proposed_ops.emplace_back(op);
|
||||
uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3;
|
||||
proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime);
|
||||
|
||||
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(son_id), proposal_op);
|
||||
try {
|
||||
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
|
||||
if (plugin.app().p2p_node())
|
||||
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
|
||||
} catch (fc::exception e) {
|
||||
elog("Sending proposal for son wallet withdraw create operation by ${son} failed with exception ${e}", ("son", son_id)("e", e.what()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
FC_ASSERT(false, "Invalid sidechain event");
|
||||
}
|
||||
|
||||
void sidechain_net_handler::process_deposits() {
|
||||
const auto &idx = database.get_index_type<son_wallet_deposit_index>().indices().get<by_sidechain_and_processed>();
|
||||
const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, false));
|
||||
|
||||
std::for_each(idx_range.first, idx_range.second, [&](const son_wallet_deposit_object &swdo) {
|
||||
ilog("Deposit to process: ${swdo}", ("swdo", swdo));
|
||||
|
||||
bool process_deposit_result = process_deposit(swdo);
|
||||
|
||||
if (!process_deposit_result) {
|
||||
wlog("Deposit not processed: ${swdo}", ("swdo", swdo));
|
||||
return;
|
||||
}
|
||||
|
||||
const chain::global_property_object &gpo = database.get_global_properties();
|
||||
|
||||
son_wallet_deposit_process_operation swdp_op;
|
||||
swdp_op.payer = gpo.parameters.son_account();
|
||||
swdp_op.son_wallet_deposit_id = swdo.id;
|
||||
|
||||
transfer_operation t_op;
|
||||
t_op.fee = asset(2000000);
|
||||
t_op.from = swdo.peerplays_to; // gpo.parameters.son_account()
|
||||
t_op.to = swdo.peerplays_from;
|
||||
t_op.amount = swdo.peerplays_asset;
|
||||
|
||||
proposal_create_operation proposal_op;
|
||||
proposal_op.fee_paying_account = plugin.get_current_son_object().son_account;
|
||||
proposal_op.proposed_ops.emplace_back(swdp_op);
|
||||
proposal_op.proposed_ops.emplace_back(t_op);
|
||||
uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3;
|
||||
proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime);
|
||||
|
||||
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op);
|
||||
trx.validate();
|
||||
try {
|
||||
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
|
||||
if (plugin.app().p2p_node())
|
||||
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
|
||||
} catch (fc::exception e) {
|
||||
elog("Sending proposal for deposit sidechain transaction create operation failed with exception ${e}", ("e", e.what()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void sidechain_net_handler::process_withdrawals() {
|
||||
const auto &idx = database.get_index_type<son_wallet_withdraw_index>().indices().get<by_withdraw_sidechain_and_processed>();
|
||||
const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, false));
|
||||
|
||||
std::for_each(idx_range.first, idx_range.second, [&](const son_wallet_withdraw_object &swwo) {
|
||||
ilog("Withdraw to process: ${swwo}", ("swwo", swwo));
|
||||
|
||||
bool process_withdrawal_result = process_withdrawal(swwo);
|
||||
|
||||
if (!process_withdrawal_result) {
|
||||
wlog("Withdraw not processed: ${swwo}", ("swwo", swwo));
|
||||
return;
|
||||
}
|
||||
|
||||
const chain::global_property_object &gpo = database.get_global_properties();
|
||||
|
||||
son_wallet_withdraw_process_operation swwp_op;
|
||||
swwp_op.payer = gpo.parameters.son_account();
|
||||
swwp_op.son_wallet_withdraw_id = swwo.id;
|
||||
|
||||
proposal_create_operation proposal_op;
|
||||
proposal_op.fee_paying_account = plugin.get_current_son_object().son_account;
|
||||
proposal_op.proposed_ops.emplace_back(swwp_op);
|
||||
uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3;
|
||||
proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime);
|
||||
|
||||
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op);
|
||||
trx.validate();
|
||||
try {
|
||||
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
|
||||
if (plugin.app().p2p_node())
|
||||
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
|
||||
} catch (fc::exception e) {
|
||||
elog("Sending proposal for withdraw sidechain transaction create operation failed with exception ${e}", ("e", e.what()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void sidechain_net_handler::process_sidechain_transactions() {
|
||||
const auto &idx = database.get_index_type<sidechain_transaction_index>().indices().get<by_sidechain_and_complete>();
|
||||
const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, false));
|
||||
|
||||
std::for_each(idx_range.first, idx_range.second, [&](const sidechain_transaction_object &sto) {
|
||||
ilog("Sidechain transaction to process: ${sto}", ("sto", sto));
|
||||
|
||||
bool complete = false;
|
||||
std::string processed_sidechain_tx = process_sidechain_transaction(sto, complete);
|
||||
|
||||
if (processed_sidechain_tx.empty()) {
|
||||
wlog("Sidechain transaction not processed: ${sto}", ("sto", sto));
|
||||
return;
|
||||
}
|
||||
|
||||
sidechain_transaction_sign_operation sts_op;
|
||||
sts_op.payer = plugin.get_current_son_object().son_account;
|
||||
sts_op.sidechain_transaction_id = sto.id;
|
||||
sts_op.transaction = processed_sidechain_tx;
|
||||
sts_op.block = sto.block;
|
||||
sts_op.complete = complete;
|
||||
|
||||
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), sts_op);
|
||||
trx.validate();
|
||||
try {
|
||||
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
|
||||
if (plugin.app().p2p_node())
|
||||
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
|
||||
} catch (fc::exception e) {
|
||||
elog("Sending proposal for sidechain transaction sign operation failed with exception ${e}", ("e", e.what()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void sidechain_net_handler::send_sidechain_transactions() {
|
||||
const auto &idx = database.get_index_type<sidechain_transaction_index>().indices().get<by_sidechain_and_complete_and_sent>();
|
||||
const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, true, false));
|
||||
|
||||
std::for_each(idx_range.first, idx_range.second, [&](const sidechain_transaction_object &sto) {
|
||||
ilog("Sidechain transaction to send: ${sto}", ("sto", sto));
|
||||
|
||||
bool sent = send_sidechain_transaction(sto);
|
||||
|
||||
if (!sent) {
|
||||
wlog("Sidechain transaction not sent: ${sto}", ("sto", sto));
|
||||
return;
|
||||
}
|
||||
|
||||
sidechain_transaction_send_operation sts_op;
|
||||
sts_op.payer = plugin.get_current_son_object().son_account;
|
||||
sts_op.sidechain_transaction_id = sto.id;
|
||||
|
||||
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), sts_op);
|
||||
trx.validate();
|
||||
try {
|
||||
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
|
||||
if (plugin.app().p2p_node())
|
||||
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
|
||||
} catch (fc::exception e) {
|
||||
elog("Sending proposal for sidechain transaction send operation failed with exception ${e}", ("e", e.what()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void sidechain_net_handler::recreate_primary_wallet() {
|
||||
FC_ASSERT(false, "recreate_primary_wallet not implemented");
|
||||
}
|
||||
|
||||
bool sidechain_net_handler::process_deposit(const son_wallet_deposit_object &swdo) {
|
||||
FC_ASSERT(false, "process_deposit not implemented");
|
||||
}
|
||||
|
||||
bool sidechain_net_handler::process_withdrawal(const son_wallet_withdraw_object &swwo) {
|
||||
FC_ASSERT(false, "process_withdrawal not implemented");
|
||||
}
|
||||
|
||||
std::string sidechain_net_handler::process_sidechain_transaction(const sidechain_transaction_object &sto, bool &complete) {
|
||||
FC_ASSERT(false, "process_sidechain_transaction not implemented");
|
||||
}
|
||||
|
||||
bool sidechain_net_handler::send_sidechain_transaction(const sidechain_transaction_object &sto) {
|
||||
FC_ASSERT(false, "send_sidechain_transaction not implemented");
|
||||
}
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,88 @@
|
|||
#include <graphene/peerplays_sidechain/sidechain_net_handler_peerplays.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <thread>
|
||||
|
||||
#include <boost/algorithm/hex.hpp>
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
|
||||
#include <fc/crypto/base64.hpp>
|
||||
#include <fc/log/logger.hpp>
|
||||
#include <fc/network/ip.hpp>
|
||||
|
||||
#include <graphene/chain/account_object.hpp>
|
||||
#include <graphene/chain/protocol/son_wallet.hpp>
|
||||
#include <graphene/chain/son_info.hpp>
|
||||
#include <graphene/chain/son_wallet_object.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
sidechain_net_handler_peerplays::sidechain_net_handler_peerplays(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) :
|
||||
sidechain_net_handler(_plugin, options) {
|
||||
sidechain = sidechain_type::peerplays;
|
||||
database.applied_block.connect([&](const signed_block &b) {
|
||||
on_applied_block(b);
|
||||
});
|
||||
}
|
||||
|
||||
sidechain_net_handler_peerplays::~sidechain_net_handler_peerplays() {
|
||||
}
|
||||
|
||||
void sidechain_net_handler_peerplays::recreate_primary_wallet() {
|
||||
return;
|
||||
}
|
||||
|
||||
bool sidechain_net_handler_peerplays::process_deposit(const son_wallet_deposit_object &swdo) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sidechain_net_handler_peerplays::process_withdrawal(const son_wallet_withdraw_object &swwo) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string sidechain_net_handler_peerplays::process_sidechain_transaction(const sidechain_transaction_object &sto, bool &complete) {
|
||||
complete = true;
|
||||
return sto.transaction;
|
||||
}
|
||||
|
||||
bool sidechain_net_handler_peerplays::send_sidechain_transaction(const sidechain_transaction_object &sto) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void sidechain_net_handler_peerplays::on_applied_block(const signed_block &b) {
|
||||
for (const auto &trx : b.transactions) {
|
||||
size_t operation_index = -1;
|
||||
for (auto op : trx.operations) {
|
||||
operation_index = operation_index + 1;
|
||||
if (op.which() == operation::tag<transfer_operation>::value) {
|
||||
transfer_operation transfer_op = op.get<transfer_operation>();
|
||||
if (transfer_op.to != plugin.database().get_global_properties().parameters.son_account()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "peerplays"
|
||||
<< "-" << trx.id().str() << "-" << operation_index;
|
||||
std::string sidechain_uid = ss.str();
|
||||
|
||||
sidechain_event_data sed;
|
||||
sed.timestamp = database.head_block_time();
|
||||
sed.sidechain = sidechain_type::peerplays;
|
||||
sed.sidechain_uid = sidechain_uid;
|
||||
sed.sidechain_transaction_id = trx.id().str();
|
||||
sed.sidechain_from = fc::to_string(transfer_op.from.space_id) + "." + fc::to_string(transfer_op.from.type_id) + "." + fc::to_string((uint64_t)transfer_op.from.instance);
|
||||
sed.sidechain_to = fc::to_string(transfer_op.to.space_id) + "." + fc::to_string(transfer_op.to.type_id) + "." + fc::to_string((uint64_t)transfer_op.to.instance);
|
||||
sed.sidechain_currency = fc::to_string(transfer_op.amount.asset_id.space_id) + "." + fc::to_string(transfer_op.amount.asset_id.type_id) + "." + fc::to_string((uint64_t)transfer_op.amount.asset_id.instance); //transfer_op.amount.asset_id(database).symbol;
|
||||
sed.sidechain_amount = transfer_op.amount.amount;
|
||||
sed.peerplays_from = transfer_op.from;
|
||||
sed.peerplays_to = transfer_op.to;
|
||||
// We should calculate exchange rate between CORE/TEST and other Peerplays asset
|
||||
sed.peerplays_asset = asset(transfer_op.amount.amount / transfer_op.amount.asset_id(database).options.core_exchange_rate.quote.amount);
|
||||
sidechain_event_data_received(sed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
#include <graphene/peerplays_sidechain/sidechain_net_manager.hpp>
|
||||
|
||||
#include <fc/log/logger.hpp>
|
||||
#include <graphene/chain/son_wallet_object.hpp>
|
||||
#include <graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp>
|
||||
#include <graphene/peerplays_sidechain/sidechain_net_handler_peerplays.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
sidechain_net_manager::sidechain_net_manager(peerplays_sidechain_plugin &_plugin) :
|
||||
plugin(_plugin),
|
||||
database(_plugin.database()) {
|
||||
}
|
||||
|
||||
sidechain_net_manager::~sidechain_net_manager() {
|
||||
}
|
||||
|
||||
bool sidechain_net_manager::create_handler(sidechain_type sidechain, const boost::program_options::variables_map &options) {
|
||||
|
||||
bool ret_val = false;
|
||||
|
||||
switch (sidechain) {
|
||||
case sidechain_type::bitcoin: {
|
||||
std::unique_ptr<sidechain_net_handler> h = std::unique_ptr<sidechain_net_handler>(new sidechain_net_handler_bitcoin(plugin, options));
|
||||
net_handlers.push_back(std::move(h));
|
||||
ret_val = true;
|
||||
break;
|
||||
}
|
||||
case sidechain_type::peerplays: {
|
||||
std::unique_ptr<sidechain_net_handler> h = std::unique_ptr<sidechain_net_handler>(new sidechain_net_handler_peerplays(plugin, options));
|
||||
net_handlers.push_back(std::move(h));
|
||||
ret_val = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
void sidechain_net_manager::recreate_primary_wallet() {
|
||||
for (size_t i = 0; i < net_handlers.size(); i++) {
|
||||
net_handlers.at(i)->recreate_primary_wallet();
|
||||
}
|
||||
}
|
||||
|
||||
void sidechain_net_manager::process_deposits() {
|
||||
for (size_t i = 0; i < net_handlers.size(); i++) {
|
||||
net_handlers.at(i)->process_deposits();
|
||||
}
|
||||
}
|
||||
|
||||
void sidechain_net_manager::process_withdrawals() {
|
||||
for (size_t i = 0; i < net_handlers.size(); i++) {
|
||||
net_handlers.at(i)->process_withdrawals();
|
||||
}
|
||||
}
|
||||
|
||||
void sidechain_net_manager::process_sidechain_transactions() {
|
||||
for (size_t i = 0; i < net_handlers.size(); i++) {
|
||||
net_handlers.at(i)->process_sidechain_transactions();
|
||||
}
|
||||
}
|
||||
|
||||
void sidechain_net_manager::send_sidechain_transactions() {
|
||||
for (size_t i = 0; i < net_handlers.size(); i++) {
|
||||
net_handlers.at(i)->send_sidechain_transactions();
|
||||
}
|
||||
}
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
|
@ -23,7 +23,7 @@ else()
|
|||
endif()
|
||||
|
||||
add_library( graphene_wallet wallet.cpp ${CMAKE_CURRENT_BINARY_DIR}/api_documentation.cpp ${HEADERS} )
|
||||
target_link_libraries( graphene_wallet PRIVATE graphene_app graphene_net graphene_chain graphene_utilities fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} )
|
||||
target_link_libraries( graphene_wallet PRIVATE graphene_app graphene_net graphene_chain graphene_utilities fc peerplays_sidechain ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} )
|
||||
target_include_directories( graphene_db PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
|
||||
|
||||
if(MSVC)
|
||||
|
|
|
|||
|
|
@ -1311,6 +1311,7 @@ class wallet_api
|
|||
* display this when showing a list of SONs. May be blank.
|
||||
* @param deposit_id vesting balance id for SON deposit
|
||||
* @param pay_vb_id vesting balance id for SON pay_vb
|
||||
* @param sidechain_public_keys The new set of sidechain public keys.
|
||||
* @param broadcast true to broadcast the transaction on the network
|
||||
* @returns the signed transaction registering a SON
|
||||
*/
|
||||
|
|
@ -1318,6 +1319,7 @@ class wallet_api
|
|||
string url,
|
||||
vesting_balance_id_type deposit_id,
|
||||
vesting_balance_id_type pay_vb_id,
|
||||
flat_map<peerplays_sidechain::sidechain_type, string> sidechain_public_keys,
|
||||
bool broadcast = false);
|
||||
|
||||
/**
|
||||
|
|
@ -1326,11 +1328,13 @@ class wallet_api
|
|||
* @param witness The name of the SON's owner account. Also accepts the ID of the owner account or the ID of the SON.
|
||||
* @param url Same as for create_son. The empty string makes it remain the same.
|
||||
* @param block_signing_key The new block signing public key. The empty string makes it remain the same.
|
||||
* @param sidechain_public_keys The new set of sidechain public keys. The empty string makes it remain the same.
|
||||
* @param broadcast true if you wish to broadcast the transaction.
|
||||
*/
|
||||
signed_transaction update_son(string owner_account,
|
||||
string url,
|
||||
string block_signing_key,
|
||||
flat_map<peerplays_sidechain::sidechain_type, string> sidechain_public_keys,
|
||||
bool broadcast = false);
|
||||
|
||||
|
||||
|
|
@ -1339,14 +1343,30 @@ class wallet_api
|
|||
* An account can have at most one witness object.
|
||||
*
|
||||
* @param owner_account the name or id of the account which is creating the witness
|
||||
* @param url a URL to include in the witness record in the blockchain. Clients may
|
||||
* display this when showing a list of witnesses. May be blank.
|
||||
* @param broadcast true to broadcast the transaction on the network
|
||||
* @returns the signed transaction registering a witness
|
||||
*/
|
||||
signed_transaction delete_son(string owner_account,
|
||||
bool broadcast = false);
|
||||
|
||||
/** Modify status of the SON owned by the given account to maintenance.
|
||||
*
|
||||
* @param owner_account the name or id of the account which is owning the SON
|
||||
* @param broadcast true to broadcast the transaction on the network
|
||||
* @returns the signed transaction
|
||||
*/
|
||||
signed_transaction request_son_maintenance(string owner_account,
|
||||
bool broadcast = false);
|
||||
|
||||
/** Modify status of the SON owned by the given account back to active.
|
||||
*
|
||||
* @param owner_account the name or id of the account which is owning the SON
|
||||
* @param broadcast true to broadcast the transaction on the network
|
||||
* @returns the signed transaction
|
||||
*/
|
||||
signed_transaction cancel_request_son_maintenance(string owner_account,
|
||||
bool broadcast = false);
|
||||
|
||||
/** Lists all SONs in the blockchain.
|
||||
* This returns a list of all account names that own SON, and the associated SON id,
|
||||
* sorted by name. This lists SONs whether they are currently voted in or not.
|
||||
|
|
@ -1362,6 +1382,106 @@ class wallet_api
|
|||
*/
|
||||
map<string, son_id_type> list_sons(const string& lowerbound, uint32_t limit);
|
||||
|
||||
/** Lists active at the moment SONs.
|
||||
* This returns a list of all account names that own active SON, and the associated SON id,
|
||||
* sorted by name.
|
||||
* @returns a list of active SONs mapping SON names to SON ids
|
||||
*/
|
||||
map<string, son_id_type> list_active_sons();
|
||||
|
||||
/**
|
||||
* @brief Get active SON wallet
|
||||
* @return Active SON wallet object
|
||||
*/
|
||||
optional<son_wallet_object> get_active_son_wallet();
|
||||
|
||||
/**
|
||||
* @brief Get SON wallet that was active for a given time point
|
||||
* @param time_point Time point
|
||||
* @return SON wallet object, for the wallet that was active for a given time point
|
||||
*/
|
||||
optional<son_wallet_object> get_son_wallet_by_time_point(time_point_sec time_point);
|
||||
|
||||
/**
|
||||
* @brief Get full list of SON wallets
|
||||
* @param limit Maximum number of results to return
|
||||
* @return A list of SON wallet objects
|
||||
*/
|
||||
vector<optional<son_wallet_object>> get_son_wallets(uint32_t limit);
|
||||
|
||||
/** Adds sidechain address owned by the given account for a given sidechain.
|
||||
*
|
||||
* An account can have at most one sidechain address for one sidechain.
|
||||
*
|
||||
* @param account the name or id of the account who owns the address
|
||||
* @param sidechain a sidechain to whom address belongs
|
||||
* @param deposit_address sidechain address for deposits
|
||||
* @param withdraw_address sidechain address for withdrawals
|
||||
* @param broadcast true to broadcast the transaction on the network
|
||||
* @returns the signed transaction adding sidechain address
|
||||
*/
|
||||
signed_transaction add_sidechain_address(string account,
|
||||
peerplays_sidechain::sidechain_type sidechain,
|
||||
string deposit_address,
|
||||
string withdraw_address,
|
||||
bool broadcast = false);
|
||||
|
||||
/** Updates existing sidechain address owned by the given account for a given sidechain.
|
||||
*
|
||||
* Only address, private key and public key might be updated.
|
||||
*
|
||||
* @param account the name or id of the account who owns the address
|
||||
* @param sidechain a sidechain to whom address belongs
|
||||
* @param deposit_address sidechain address for deposits
|
||||
* @param withdraw_address sidechain address for withdrawals
|
||||
* @param broadcast true to broadcast the transaction on the network
|
||||
* @returns the signed transaction updating sidechain address
|
||||
*/
|
||||
signed_transaction update_sidechain_address(string account,
|
||||
peerplays_sidechain::sidechain_type sidechain,
|
||||
string deposit_address,
|
||||
string withdraw_address,
|
||||
bool broadcast = false);
|
||||
|
||||
/** Deletes existing sidechain address owned by the given account for a given sidechain.
|
||||
*
|
||||
* @param account the name or id of the account who owns the address
|
||||
* @param sidechain a sidechain to whom address belongs
|
||||
* @param broadcast true to broadcast the transaction on the network
|
||||
* @returns the signed transaction updating sidechain address
|
||||
*/
|
||||
signed_transaction delete_sidechain_address(string account,
|
||||
peerplays_sidechain::sidechain_type sidechain,
|
||||
bool broadcast = false);
|
||||
|
||||
/** Retrieves all sidechain addresses owned by given account.
|
||||
*
|
||||
* @param account the name or id of the account who owns the address
|
||||
* @returns the list of all sidechain addresses owned by given account.
|
||||
*/
|
||||
vector<optional<sidechain_address_object>> get_sidechain_addresses_by_account(string account);
|
||||
|
||||
/** Retrieves all sidechain addresses registered for a given sidechain.
|
||||
*
|
||||
* @param sidechain the name of the sidechain
|
||||
* @returns the list of all sidechain addresses registered for a given sidechain.
|
||||
*/
|
||||
vector<optional<sidechain_address_object>> get_sidechain_addresses_by_sidechain(peerplays_sidechain::sidechain_type sidechain);
|
||||
|
||||
/** Retrieves sidechain address owned by given account for a given sidechain.
|
||||
*
|
||||
* @param account the name or id of the account who owns the address
|
||||
* @param sidechain the name of the sidechain
|
||||
* @returns the sidechain address owned by given account for a given sidechain.
|
||||
*/
|
||||
fc::optional<sidechain_address_object> get_sidechain_address_by_account_and_sidechain(string account, peerplays_sidechain::sidechain_type sidechain);
|
||||
|
||||
/** Retrieves the total number of sidechain addresses registered in the system.
|
||||
*
|
||||
* @returns the total number of sidechain addresses registered in the system.
|
||||
*/
|
||||
uint64_t get_sidechain_addresses_count();
|
||||
|
||||
/** Creates a witness object owned by the given account.
|
||||
*
|
||||
* An account can have at most one witness object.
|
||||
|
|
@ -1428,15 +1548,17 @@ class wallet_api
|
|||
|
||||
/** Creates a vesting deposit owned by the given account.
|
||||
*
|
||||
* @param owner_account the name or id of the account
|
||||
* @param amount the amount to deposit
|
||||
* @param owner_account vesting balance owner and creator (the name or id)
|
||||
* @param amount amount to vest
|
||||
* @param asset_symbol the symbol of the asset to vest
|
||||
* @param vesting_type "normal", "gpos" or "son"
|
||||
* @param broadcast true to broadcast the transaction on the network
|
||||
* @returns the signed transaction registering a vesting object
|
||||
*/
|
||||
signed_transaction create_vesting(string owner_account,
|
||||
signed_transaction create_vesting_balance(string owner_account,
|
||||
string amount,
|
||||
string vesting_type,
|
||||
string asset_symbol,
|
||||
vesting_balance_type vesting_type,
|
||||
bool broadcast = false);
|
||||
|
||||
/**
|
||||
|
|
@ -2113,11 +2235,24 @@ FC_API( graphene::wallet::wallet_api,
|
|||
(update_son)
|
||||
(delete_son)
|
||||
(list_sons)
|
||||
(list_active_sons)
|
||||
(request_son_maintenance)
|
||||
(cancel_request_son_maintenance)
|
||||
(get_active_son_wallet)
|
||||
(get_son_wallet_by_time_point)
|
||||
(get_son_wallets)
|
||||
(add_sidechain_address)
|
||||
(update_sidechain_address)
|
||||
(delete_sidechain_address)
|
||||
(get_sidechain_addresses_by_account)
|
||||
(get_sidechain_addresses_by_sidechain)
|
||||
(get_sidechain_address_by_account_and_sidechain)
|
||||
(get_sidechain_addresses_count)
|
||||
(create_witness)
|
||||
(update_witness)
|
||||
(create_worker)
|
||||
(update_worker_votes)
|
||||
(create_vesting)
|
||||
(create_vesting_balance)
|
||||
(get_vesting_balances)
|
||||
(withdraw_vesting)
|
||||
(vote_for_committee_member)
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@
|
|||
#include <graphene/chain/rock_paper_scissors.hpp>
|
||||
|
||||
#include <graphene/bookie/bookie_api.hpp>
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
|
||||
#include <graphene/chain/protocol/fee_schedule.hpp>
|
||||
#include <graphene/utilities/git_revision.hpp>
|
||||
|
|
@ -822,6 +823,7 @@ public:
|
|||
// account, false otherwise (but it is stored either way)
|
||||
bool import_key(string account_name_or_id, string wif_key)
|
||||
{
|
||||
fc::scoped_lock<fc::mutex> lock(_resync_mutex);
|
||||
fc::optional<fc::ecc::private_key> optional_private_key = wif_to_key(wif_key);
|
||||
if (!optional_private_key)
|
||||
FC_THROW("Invalid private key");
|
||||
|
|
@ -1323,6 +1325,7 @@ public:
|
|||
bool broadcast = false,
|
||||
bool save_wallet = true)
|
||||
{ try {
|
||||
fc::scoped_lock<fc::mutex> lock(_resync_mutex);
|
||||
int active_key_index = find_first_unused_derived_key_index(owner_privkey);
|
||||
fc::ecc::private_key active_privkey = derive_private_key( key_to_wif(owner_privkey), active_key_index);
|
||||
|
||||
|
|
@ -1827,8 +1830,10 @@ public:
|
|||
string url,
|
||||
vesting_balance_id_type deposit_id,
|
||||
vesting_balance_id_type pay_vb_id,
|
||||
flat_map<peerplays_sidechain::sidechain_type, string> sidechain_public_keys,
|
||||
bool broadcast /* = false */)
|
||||
{ try {
|
||||
fc::scoped_lock<fc::mutex> lock(_resync_mutex);
|
||||
account_object son_account = get_account(owner_account);
|
||||
auto son_public_key = son_account.active.get_keys()[0];
|
||||
|
||||
|
|
@ -1838,6 +1843,7 @@ public:
|
|||
son_create_op.url = url;
|
||||
son_create_op.deposit = deposit_id;
|
||||
son_create_op.pay_vb = pay_vb_id;
|
||||
son_create_op.sidechain_public_keys = sidechain_public_keys;
|
||||
|
||||
if (_remote_db->get_son_by_account(son_create_op.owner_account))
|
||||
FC_THROW("Account ${owner_account} is already a SON", ("owner_account", owner_account));
|
||||
|
|
@ -1853,6 +1859,7 @@ public:
|
|||
signed_transaction update_son(string owner_account,
|
||||
string url,
|
||||
string block_signing_key,
|
||||
flat_map<peerplays_sidechain::sidechain_type, string> sidechain_public_keys,
|
||||
bool broadcast /* = false */)
|
||||
{ try {
|
||||
son_object son = get_son(owner_account);
|
||||
|
|
@ -1865,6 +1872,9 @@ public:
|
|||
if( block_signing_key != "" ) {
|
||||
son_update_op.new_signing_key = public_key_type( block_signing_key );
|
||||
}
|
||||
if( !sidechain_public_keys.empty() ) {
|
||||
son_update_op.new_sidechain_public_keys = sidechain_public_keys;
|
||||
}
|
||||
|
||||
signed_transaction tx;
|
||||
tx.operations.push_back( son_update_op );
|
||||
|
|
@ -1892,10 +1902,165 @@ public:
|
|||
return sign_transaction( tx, broadcast );
|
||||
} FC_CAPTURE_AND_RETHROW( (owner_account)(broadcast) ) }
|
||||
|
||||
signed_transaction request_son_maintenance(string owner_account,
|
||||
bool broadcast)
|
||||
{ try {
|
||||
son_object son = get_son(owner_account);
|
||||
|
||||
son_maintenance_operation op;
|
||||
op.owner_account = son.son_account;
|
||||
op.son_id = son.id;
|
||||
op.request_type = son_maintenance_request_type::request_maintenance;
|
||||
|
||||
signed_transaction tx;
|
||||
tx.operations.push_back( op );
|
||||
set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees );
|
||||
tx.validate();
|
||||
|
||||
return sign_transaction( tx, broadcast );
|
||||
} FC_CAPTURE_AND_RETHROW( (owner_account) ) }
|
||||
|
||||
signed_transaction cancel_request_son_maintenance(string owner_account,
|
||||
bool broadcast)
|
||||
{ try {
|
||||
son_object son = get_son(owner_account);
|
||||
|
||||
son_maintenance_operation op;
|
||||
op.owner_account = son.son_account;
|
||||
op.son_id = son.id;
|
||||
op.request_type = son_maintenance_request_type::cancel_request_maintenance;
|
||||
|
||||
signed_transaction tx;
|
||||
tx.operations.push_back( op );
|
||||
set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees );
|
||||
tx.validate();
|
||||
|
||||
return sign_transaction( tx, broadcast );
|
||||
} FC_CAPTURE_AND_RETHROW( (owner_account) ) }
|
||||
|
||||
map<string, son_id_type> list_active_sons()
|
||||
{ try {
|
||||
global_property_object gpo = get_global_properties();
|
||||
vector<son_id_type> son_ids;
|
||||
son_ids.reserve(gpo.active_sons.size());
|
||||
std::transform(gpo.active_sons.begin(), gpo.active_sons.end(),
|
||||
std::inserter(son_ids, son_ids.end()),
|
||||
[](const son_info& swi) {
|
||||
return swi.son_id;
|
||||
});
|
||||
std::vector<fc::optional<son_object>> son_objects = _remote_db->get_sons(son_ids);
|
||||
vector<account_id_type> owners;
|
||||
for(auto obj: son_objects)
|
||||
{
|
||||
if (obj)
|
||||
owners.push_back(obj->son_account);
|
||||
}
|
||||
vector<fc::optional<account_object>> accs = _remote_db->get_accounts(owners);
|
||||
std::remove_if(son_objects.begin(), son_objects.end(),
|
||||
[](const fc::optional<son_object>& obj) -> bool { return obj.valid(); });
|
||||
map<string, son_id_type> result;
|
||||
std::transform(accs.begin(), accs.end(), son_objects.begin(),
|
||||
std::inserter(result, result.end()),
|
||||
[](fc::optional<account_object>& acct, fc::optional<son_object> son) {
|
||||
FC_ASSERT(acct, "Invalid active SONs list in global properties.");
|
||||
if (son.valid())
|
||||
return std::make_pair<string, son_id_type>(string(acct->name), std::move(son->id));
|
||||
return std::make_pair<string, son_id_type>(string(acct->name), std::move(son_id_type()));
|
||||
});
|
||||
return result;
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
optional<son_wallet_object> get_active_son_wallet()
|
||||
{ try {
|
||||
return _remote_db->get_active_son_wallet();
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
optional<son_wallet_object> get_son_wallet_by_time_point(time_point_sec time_point)
|
||||
{ try {
|
||||
return _remote_db->get_son_wallet_by_time_point(time_point);
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
vector<optional<son_wallet_object>> get_son_wallets(uint32_t limit)
|
||||
{ try {
|
||||
return _remote_db->get_son_wallets(limit);
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
signed_transaction add_sidechain_address(string account,
|
||||
peerplays_sidechain::sidechain_type sidechain,
|
||||
string deposit_address,
|
||||
string withdraw_address,
|
||||
bool broadcast /* = false */)
|
||||
{ try {
|
||||
account_id_type sidechain_address_account_id = get_account_id(account);
|
||||
|
||||
sidechain_address_add_operation op;
|
||||
op.sidechain_address_account = sidechain_address_account_id;
|
||||
op.sidechain = sidechain;
|
||||
op.deposit_address = deposit_address;
|
||||
op.withdraw_address = withdraw_address;
|
||||
|
||||
signed_transaction tx;
|
||||
tx.operations.push_back( op );
|
||||
set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees);
|
||||
tx.validate();
|
||||
|
||||
return sign_transaction( tx, broadcast );
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
signed_transaction update_sidechain_address(string account,
|
||||
peerplays_sidechain::sidechain_type sidechain,
|
||||
string deposit_address,
|
||||
string withdraw_address,
|
||||
bool broadcast /* = false */)
|
||||
{ try {
|
||||
account_id_type sidechain_address_account_id = get_account_id(account);
|
||||
fc::optional<sidechain_address_object> sao = _remote_db->get_sidechain_address_by_account_and_sidechain(sidechain_address_account_id, sidechain);
|
||||
if (!sao)
|
||||
FC_THROW("No sidechain address for account ${account} and sidechain ${sidechain}", ("account", sidechain_address_account_id)("sidechain", sidechain));
|
||||
|
||||
sidechain_address_update_operation op;
|
||||
op.sidechain_address_id = sao->id;
|
||||
op.sidechain_address_account = sidechain_address_account_id;
|
||||
op.sidechain = sidechain;
|
||||
op.deposit_address = deposit_address;
|
||||
op.withdraw_address = withdraw_address;
|
||||
|
||||
signed_transaction tx;
|
||||
tx.operations.push_back( op );
|
||||
set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees);
|
||||
tx.validate();
|
||||
|
||||
return sign_transaction( tx, broadcast );
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
signed_transaction delete_sidechain_address(string account,
|
||||
peerplays_sidechain::sidechain_type sidechain,
|
||||
bool broadcast /* = false */)
|
||||
{ try {
|
||||
account_id_type sidechain_address_account_id = get_account_id(account);
|
||||
fc::optional<sidechain_address_object> sao = _remote_db->get_sidechain_address_by_account_and_sidechain(sidechain_address_account_id, sidechain);
|
||||
if (!sao)
|
||||
FC_THROW("No sidechain address for account ${account} and sidechain ${sidechain}", ("account", sidechain_address_account_id)("sidechain", sidechain));
|
||||
|
||||
sidechain_address_delete_operation op;
|
||||
op.sidechain_address_id = sao->id;
|
||||
op.sidechain_address_account = sidechain_address_account_id;
|
||||
op.sidechain = sidechain;
|
||||
|
||||
signed_transaction tx;
|
||||
tx.operations.push_back( op );
|
||||
set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees);
|
||||
tx.validate();
|
||||
|
||||
return sign_transaction( tx, broadcast );
|
||||
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
signed_transaction create_witness(string owner_account,
|
||||
string url,
|
||||
bool broadcast /* = false */)
|
||||
{ try {
|
||||
fc::scoped_lock<fc::mutex> lock(_resync_mutex);
|
||||
account_object witness_account = get_account(owner_account);
|
||||
fc::ecc::private_key active_private_key = get_private_key_for_account(witness_account);
|
||||
int witness_key_index = find_first_unused_derived_key_index(active_private_key);
|
||||
|
|
@ -2067,27 +2232,21 @@ public:
|
|||
return sign_transaction( tx, broadcast );
|
||||
}
|
||||
|
||||
signed_transaction create_vesting(string owner_account,
|
||||
signed_transaction create_vesting_balance(string owner_account,
|
||||
string amount,
|
||||
string vesting_type,
|
||||
string asset_symbol,
|
||||
vesting_balance_type vesting_type,
|
||||
bool broadcast /* = false */)
|
||||
{ try {
|
||||
account_object son_account = get_account(owner_account);
|
||||
fc::optional<asset_object> asset_obj = get_asset(asset_symbol);
|
||||
FC_ASSERT(asset_obj, "Invalid asset symbol {asst}", ("asst", asset_symbol));
|
||||
|
||||
vesting_balance_create_operation op;
|
||||
op.creator = son_account.get_id();
|
||||
op.owner = son_account.get_id();
|
||||
op.amount = asset_object().amount_from_string(amount);
|
||||
if (vesting_type == "normal")
|
||||
op.balance_type = vesting_balance_type::normal;
|
||||
else if (vesting_type == "gpos")
|
||||
op.balance_type = vesting_balance_type::gpos;
|
||||
else if (vesting_type == "son")
|
||||
op.balance_type = vesting_balance_type::son;
|
||||
else
|
||||
{
|
||||
FC_ASSERT( false, "unknown vesting type value ${vt}", ("vt", vesting_type) );
|
||||
}
|
||||
op.amount = asset_obj->amount_from_string(amount);
|
||||
op.balance_type = vesting_type;
|
||||
if (op.balance_type == vesting_balance_type::son)
|
||||
op.policy = dormant_vesting_policy_initializer {};
|
||||
|
||||
|
|
@ -2232,26 +2391,27 @@ public:
|
|||
uint16_t desired_number_of_sons,
|
||||
bool broadcast /* = false */)
|
||||
{ try {
|
||||
FC_ASSERT(sons_to_approve.size() || sons_to_reject.size(), "Both accepted and rejected lists can't be empty simultaneously");
|
||||
account_object voting_account_object = get_account(voting_account);
|
||||
for (const std::string& son : sons_to_approve)
|
||||
{
|
||||
account_id_type son_owner_account_id = get_account_id(son);
|
||||
fc::optional<son_object> son_obj = _remote_db->get_son_by_account(son_owner_account_id);
|
||||
if (!son_obj)
|
||||
FC_THROW("Account ${son} is not registered as a witness", ("son", son));
|
||||
FC_THROW("Account ${son} is not registered as a SON", ("son", son));
|
||||
auto insert_result = voting_account_object.options.votes.insert(son_obj->vote_id);
|
||||
if (!insert_result.second)
|
||||
FC_THROW("Account ${account} was already voting for son ${son}", ("account", voting_account)("son", son));
|
||||
FC_THROW("Account ${account} was already voting for SON ${son}", ("account", voting_account)("son", son));
|
||||
}
|
||||
for (const std::string& son : sons_to_reject)
|
||||
{
|
||||
account_id_type son_owner_account_id = get_account_id(son);
|
||||
fc::optional<son_object> son_obj = _remote_db->get_son_by_account(son_owner_account_id);
|
||||
if (!son_obj)
|
||||
FC_THROW("Account ${son} is not registered as a son", ("son", son));
|
||||
FC_THROW("Account ${son} is not registered as a SON", ("son", son));
|
||||
unsigned votes_removed = voting_account_object.options.votes.erase(son_obj->vote_id);
|
||||
if (!votes_removed)
|
||||
FC_THROW("Account ${account} is already not voting for son ${son}", ("account", voting_account)("son", son));
|
||||
FC_THROW("Account ${account} is already not voting for SON ${son}", ("account", voting_account)("son", son));
|
||||
}
|
||||
voting_account_object.options.num_son = desired_number_of_sons;
|
||||
|
||||
|
|
@ -4231,29 +4391,32 @@ committee_member_object wallet_api::get_committee_member(string owner_account)
|
|||
return my->get_committee_member(owner_account);
|
||||
}
|
||||
|
||||
signed_transaction wallet_api::create_vesting(string owner_account,
|
||||
signed_transaction wallet_api::create_vesting_balance(string owner_account,
|
||||
string amount,
|
||||
string vesting_type,
|
||||
string asset_symbol,
|
||||
vesting_balance_type vesting_type,
|
||||
bool broadcast /* = false */)
|
||||
{
|
||||
return my->create_vesting(owner_account, amount, vesting_type, broadcast);
|
||||
return my->create_vesting_balance(owner_account, amount, asset_symbol, vesting_type, broadcast);
|
||||
}
|
||||
|
||||
signed_transaction wallet_api::create_son(string owner_account,
|
||||
string url,
|
||||
vesting_balance_id_type deposit_id,
|
||||
vesting_balance_id_type pay_vb_id,
|
||||
flat_map<peerplays_sidechain::sidechain_type, string> sidechain_public_keys,
|
||||
bool broadcast /* = false */)
|
||||
{
|
||||
return my->create_son(owner_account, url, deposit_id, pay_vb_id, broadcast);
|
||||
return my->create_son(owner_account, url, deposit_id, pay_vb_id, sidechain_public_keys, broadcast);
|
||||
}
|
||||
|
||||
signed_transaction wallet_api::update_son(string owner_account,
|
||||
string url,
|
||||
string block_signing_key,
|
||||
flat_map<peerplays_sidechain::sidechain_type, string> sidechain_public_keys,
|
||||
bool broadcast /* = false */)
|
||||
{
|
||||
return my->update_son(owner_account, url, block_signing_key, broadcast);
|
||||
return my->update_son(owner_account, url, block_signing_key, sidechain_public_keys, broadcast);
|
||||
}
|
||||
|
||||
signed_transaction wallet_api::delete_son(string owner_account,
|
||||
|
|
@ -4262,11 +4425,88 @@ signed_transaction wallet_api::delete_son(string owner_account,
|
|||
return my->delete_son(owner_account, broadcast);
|
||||
}
|
||||
|
||||
signed_transaction wallet_api::request_son_maintenance(string owner_account, bool broadcast)
|
||||
{
|
||||
return my->request_son_maintenance(owner_account, broadcast);
|
||||
}
|
||||
|
||||
signed_transaction wallet_api::cancel_request_son_maintenance(string owner_account, bool broadcast)
|
||||
{
|
||||
return my->cancel_request_son_maintenance(owner_account, broadcast);
|
||||
}
|
||||
|
||||
map<string, son_id_type> wallet_api::list_sons(const string& lowerbound, uint32_t limit)
|
||||
{
|
||||
return my->_remote_db->lookup_son_accounts(lowerbound, limit);
|
||||
}
|
||||
|
||||
map<string, son_id_type> wallet_api::list_active_sons()
|
||||
{
|
||||
return my->list_active_sons();
|
||||
}
|
||||
|
||||
optional<son_wallet_object> wallet_api::get_active_son_wallet()
|
||||
{
|
||||
return my->get_active_son_wallet();
|
||||
}
|
||||
|
||||
optional<son_wallet_object> wallet_api::get_son_wallet_by_time_point(time_point_sec time_point)
|
||||
{
|
||||
return my->get_son_wallet_by_time_point(time_point);
|
||||
}
|
||||
|
||||
vector<optional<son_wallet_object>> wallet_api::get_son_wallets(uint32_t limit)
|
||||
{
|
||||
return my->get_son_wallets(limit);
|
||||
}
|
||||
|
||||
signed_transaction wallet_api::add_sidechain_address(string account,
|
||||
peerplays_sidechain::sidechain_type sidechain,
|
||||
string deposit_address,
|
||||
string withdraw_address,
|
||||
bool broadcast /* = false */)
|
||||
{
|
||||
return my->add_sidechain_address(account, sidechain, deposit_address, withdraw_address, broadcast);
|
||||
}
|
||||
|
||||
signed_transaction wallet_api::update_sidechain_address(string account,
|
||||
peerplays_sidechain::sidechain_type sidechain,
|
||||
string deposit_address,
|
||||
string withdraw_address,
|
||||
bool broadcast /* = false */)
|
||||
{
|
||||
return my->update_sidechain_address(account, sidechain, deposit_address, withdraw_address, broadcast);
|
||||
}
|
||||
|
||||
signed_transaction wallet_api::delete_sidechain_address(string account,
|
||||
peerplays_sidechain::sidechain_type sidechain,
|
||||
bool broadcast /* = false */)
|
||||
{
|
||||
return my->delete_sidechain_address(account, sidechain, broadcast);
|
||||
}
|
||||
|
||||
vector<optional<sidechain_address_object>> wallet_api::get_sidechain_addresses_by_account(string account)
|
||||
{
|
||||
account_id_type account_id = get_account_id(account);
|
||||
return my->_remote_db->get_sidechain_addresses_by_account(account_id);
|
||||
}
|
||||
|
||||
vector<optional<sidechain_address_object>> wallet_api::get_sidechain_addresses_by_sidechain(peerplays_sidechain::sidechain_type sidechain)
|
||||
{
|
||||
return my->_remote_db->get_sidechain_addresses_by_sidechain(sidechain);
|
||||
}
|
||||
|
||||
fc::optional<sidechain_address_object> wallet_api::get_sidechain_address_by_account_and_sidechain(string account, peerplays_sidechain::sidechain_type sidechain)
|
||||
{
|
||||
account_id_type account_id = get_account_id(account);
|
||||
return my->_remote_db->get_sidechain_address_by_account_and_sidechain(account_id, sidechain);
|
||||
}
|
||||
|
||||
uint64_t wallet_api::get_sidechain_addresses_count()
|
||||
{
|
||||
return my->_remote_db->get_sidechain_addresses_count();
|
||||
}
|
||||
|
||||
signed_transaction wallet_api::create_witness(string owner_account,
|
||||
string url,
|
||||
bool broadcast /* = false */)
|
||||
|
|
|
|||
|
|
@ -44,6 +44,11 @@
|
|||
#include <graphene/chain/match_object.hpp>
|
||||
#include <graphene/chain/game_object.hpp>
|
||||
#include <graphene/chain/son_object.hpp>
|
||||
#include <graphene/chain/son_wallet_object.hpp>
|
||||
#include <graphene/chain/son_wallet_deposit_object.hpp>
|
||||
#include <graphene/chain/son_wallet_withdraw_object.hpp>
|
||||
#include <graphene/chain/sidechain_address_object.hpp>
|
||||
#include <graphene/chain/sidechain_transaction_object.hpp>
|
||||
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
#include <iostream>
|
||||
|
|
|
|||
|
|
@ -352,7 +352,15 @@
|
|||
"maximum_tournament_start_time_in_future": 2419200,
|
||||
"maximum_tournament_start_delay": 604800,
|
||||
"maximum_tournament_number_of_wins": 100,
|
||||
"extensions": {}
|
||||
"extensions": {
|
||||
"son_vesting_amount": 5000000,
|
||||
"son_vesting_period": 172800,
|
||||
"son_pay_daily_max": 20000000,
|
||||
"son_pay_time": 86400,
|
||||
"son_deregister_time": 43200,
|
||||
"son_heartbeat_frequency": 180,
|
||||
"son_down_time": 360
|
||||
}
|
||||
},
|
||||
"initial_bts_accounts": [],
|
||||
"initial_accounts": [{
|
||||
|
|
@ -493,4 +501,4 @@
|
|||
"num_special_accounts": 0,
|
||||
"num_special_assets": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ endif()
|
|||
|
||||
file(GLOB UNIT_TESTS "tests/*.cpp")
|
||||
add_executable( chain_test ${UNIT_TESTS} ${COMMON_SOURCES} )
|
||||
target_link_libraries( chain_test graphene_chain graphene_app graphene_account_history graphene_bookie graphene_egenesis_none fc graphene_wallet ${PLATFORM_SPECIFIC_LIBS} )
|
||||
target_link_libraries( chain_test graphene_chain graphene_app graphene_account_history graphene_bookie peerplays_sidechain graphene_egenesis_none fc graphene_wallet ${PLATFORM_SPECIFIC_LIBS} )
|
||||
if(MSVC)
|
||||
set_source_files_properties( tests/serialization_tests.cpp PROPERTIES COMPILE_FLAGS "/bigobj" )
|
||||
endif(MSVC)
|
||||
|
|
@ -23,7 +23,7 @@ target_link_libraries( chain_bench graphene_chain graphene_app graphene_account_
|
|||
|
||||
file(GLOB APP_SOURCES "app/*.cpp")
|
||||
add_executable( app_test ${APP_SOURCES} )
|
||||
target_link_libraries( app_test graphene_app graphene_account_history graphene_bookie graphene_net graphene_chain graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} )
|
||||
target_link_libraries( app_test graphene_app graphene_account_history graphene_witness graphene_bookie graphene_net graphene_chain graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} )
|
||||
|
||||
file(GLOB INTENSE_SOURCES "intense/*.cpp")
|
||||
add_executable( intense_test ${INTENSE_SOURCES} ${COMMON_SOURCES} )
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@
|
|||
#include <graphene/utilities/tempdir.hpp>
|
||||
|
||||
#include <graphene/account_history/account_history_plugin.hpp>
|
||||
#include <graphene/witness/witness.hpp>
|
||||
#include <graphene/market_history/market_history_plugin.hpp>
|
||||
|
||||
#include <fc/thread/thread.hpp>
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
|
|
@ -57,6 +59,8 @@ BOOST_AUTO_TEST_CASE( two_node_network )
|
|||
|
||||
graphene::app::application app1;
|
||||
app1.register_plugin<graphene::account_history::account_history_plugin>();
|
||||
app1.register_plugin<graphene::witness_plugin::witness_plugin>();
|
||||
app1.register_plugin<graphene::market_history::market_history_plugin>();
|
||||
boost::program_options::variables_map cfg;
|
||||
cfg.emplace("p2p-endpoint", boost::program_options::variable_value(string("127.0.0.1:0"), false));
|
||||
app1.initialize(app_dir.path(), cfg);
|
||||
|
|
@ -72,6 +76,8 @@ BOOST_AUTO_TEST_CASE( two_node_network )
|
|||
|
||||
graphene::app::application app2;
|
||||
app2.register_plugin<account_history::account_history_plugin>();
|
||||
app2.register_plugin<graphene::witness_plugin::witness_plugin>();
|
||||
app2.register_plugin<graphene::market_history::market_history_plugin>();
|
||||
cfg2.erase("p2p-endpoint");
|
||||
cfg2.emplace("p2p-endpoint", boost::program_options::variable_value(string("127.0.0.1:0"), false));
|
||||
cfg2.emplace("seed-node", boost::program_options::variable_value(vector<string>{endpoint1}, false));
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ public:
|
|||
}
|
||||
|
||||
void create_son(const std::string& account_name, const std::string& son_url,
|
||||
flat_map<graphene::peerplays_sidechain::sidechain_type, string>& sidechain_public_keys,
|
||||
bool generate_maintenance = true)
|
||||
{
|
||||
graphene::wallet::brain_key_info bki;
|
||||
|
|
@ -78,11 +79,12 @@ public:
|
|||
BOOST_CHECK(fixture_.generate_block());
|
||||
|
||||
// create deposit vesting
|
||||
fixture_.con.wallet_api_ptr->create_vesting(account_name, "50000000", "son", true);
|
||||
fixture_.con.wallet_api_ptr->create_vesting_balance(account_name,
|
||||
"50", "1.3.0", vesting_balance_type::son, true);
|
||||
BOOST_CHECK(fixture_.generate_block());
|
||||
|
||||
// create pay_vb vesting
|
||||
fixture_.con.wallet_api_ptr->create_vesting(account_name, "1000000", "normal", true);
|
||||
fixture_.con.wallet_api_ptr->create_vesting_balance(account_name, "1", "1.3.0", vesting_balance_type::normal, true);
|
||||
BOOST_CHECK(fixture_.generate_block());
|
||||
|
||||
// check deposits are here
|
||||
|
|
@ -91,6 +93,7 @@ public:
|
|||
|
||||
create_tx = fixture_.con.wallet_api_ptr->create_son(account_name, son_url,
|
||||
deposits[0].id, deposits[1].id,
|
||||
sidechain_public_keys,
|
||||
true);
|
||||
|
||||
if (generate_maintenance)
|
||||
|
|
@ -109,9 +112,17 @@ BOOST_AUTO_TEST_CASE( create_sons )
|
|||
BOOST_TEST_MESSAGE("SON cli wallet tests begin");
|
||||
try
|
||||
{
|
||||
flat_map<graphene::peerplays_sidechain::sidechain_type, string> sidechain_public_keys;
|
||||
|
||||
son_test_helper sth(*this);
|
||||
sth.create_son("son1account", "http://son1");
|
||||
sth.create_son("son2account", "http://son2");
|
||||
|
||||
sidechain_public_keys.clear();
|
||||
sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin_address 1";
|
||||
sth.create_son("son1account", "http://son1", sidechain_public_keys);
|
||||
|
||||
sidechain_public_keys.clear();
|
||||
sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin_address 2";
|
||||
sth.create_son("son2account", "http://son2", sidechain_public_keys);
|
||||
|
||||
auto son1_obj = con.wallet_api_ptr->get_son("son1account");
|
||||
BOOST_CHECK(son1_obj.son_account == con.wallet_api_ptr->get_account_id("son1account"));
|
||||
|
|
@ -135,8 +146,13 @@ BOOST_AUTO_TEST_CASE( cli_update_son )
|
|||
{
|
||||
BOOST_TEST_MESSAGE("Cli get_son and update_son Test");
|
||||
|
||||
flat_map<graphene::peerplays_sidechain::sidechain_type, string> sidechain_public_keys;
|
||||
|
||||
sidechain_public_keys.clear();
|
||||
sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin_address 1";
|
||||
|
||||
son_test_helper sth(*this);
|
||||
sth.create_son("sonmember", "http://sonmember");
|
||||
sth.create_son("sonmember", "http://sonmember", sidechain_public_keys);
|
||||
|
||||
auto sonmember_acct = con.wallet_api_ptr->get_account("sonmember");
|
||||
|
||||
|
|
@ -146,12 +162,16 @@ BOOST_AUTO_TEST_CASE( cli_update_son )
|
|||
BOOST_CHECK(son_data.son_account == sonmember_acct.get_id());
|
||||
|
||||
// update SON
|
||||
con.wallet_api_ptr->update_son("sonmember", "http://sonmember_updated", "", true);
|
||||
sidechain_public_keys.clear();
|
||||
sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin_address 2";
|
||||
|
||||
con.wallet_api_ptr->update_son("sonmember", "http://sonmember_updated", "", sidechain_public_keys, true);
|
||||
son_data = con.wallet_api_ptr->get_son("sonmember");
|
||||
BOOST_CHECK(son_data.url == "http://sonmember_updated");
|
||||
|
||||
// update SON signing key
|
||||
con.wallet_api_ptr->update_son("sonmember", "http://sonmember_updated2", "TEST6Yaq5ZNTTkMM2kBBzV5jktr8ETsniCC3bnVD7eFmegRrLXfGGG", true);
|
||||
sidechain_public_keys.clear();
|
||||
con.wallet_api_ptr->update_son("sonmember", "http://sonmember_updated2", "TEST6Yaq5ZNTTkMM2kBBzV5jktr8ETsniCC3bnVD7eFmegRrLXfGGG", sidechain_public_keys, true);
|
||||
son_data = con.wallet_api_ptr->get_son("sonmember");
|
||||
BOOST_CHECK(son_data.url == "http://sonmember_updated2");
|
||||
BOOST_CHECK(std::string(son_data.signing_key) == "TEST6Yaq5ZNTTkMM2kBBzV5jktr8ETsniCC3bnVD7eFmegRrLXfGGG");
|
||||
|
|
@ -167,9 +187,17 @@ BOOST_AUTO_TEST_CASE( son_voting )
|
|||
BOOST_TEST_MESSAGE("SON Vote cli wallet tests begin");
|
||||
try
|
||||
{
|
||||
flat_map<graphene::peerplays_sidechain::sidechain_type, string> sidechain_public_keys;
|
||||
|
||||
son_test_helper sth(*this);
|
||||
sth.create_son("son1account", "http://son1");
|
||||
sth.create_son("son2account", "http://son2");
|
||||
|
||||
sidechain_public_keys.clear();
|
||||
sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin_address 1";
|
||||
sth.create_son("son1account", "http://son1", sidechain_public_keys);
|
||||
|
||||
sidechain_public_keys.clear();
|
||||
sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin_address 2";
|
||||
sth.create_son("son2account", "http://son2", sidechain_public_keys);
|
||||
|
||||
BOOST_TEST_MESSAGE("Voting for SONs");
|
||||
|
||||
|
|
@ -238,9 +266,17 @@ BOOST_AUTO_TEST_CASE( delete_son )
|
|||
BOOST_TEST_MESSAGE("SON delete cli wallet tests begin");
|
||||
try
|
||||
{
|
||||
son_test_helper sth(*this);
|
||||
sth.create_son("son1account", "http://son1");
|
||||
sth.create_son("son2account", "http://son2");
|
||||
flat_map<graphene::peerplays_sidechain::sidechain_type, string> sidechain_public_keys;
|
||||
|
||||
son_test_helper sth(*this);
|
||||
|
||||
sidechain_public_keys.clear();
|
||||
sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin_address 1";
|
||||
sth.create_son("son1account", "http://son1", sidechain_public_keys);
|
||||
|
||||
sidechain_public_keys.clear();
|
||||
sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin_address 2";
|
||||
sth.create_son("son2account", "http://son2", sidechain_public_keys);
|
||||
|
||||
BOOST_TEST_MESSAGE("Deleting SONs");
|
||||
signed_transaction delete_tx;
|
||||
|
|
@ -278,11 +314,17 @@ BOOST_FIXTURE_TEST_CASE( select_top_fifteen_sons, cli_fixture )
|
|||
gpo = con.wallet_api_ptr->get_global_properties();
|
||||
unsigned int son_number = gpo.parameters.maximum_son_count;
|
||||
|
||||
flat_map<graphene::peerplays_sidechain::sidechain_type, string> sidechain_public_keys;
|
||||
|
||||
// create son accounts
|
||||
for(unsigned int i = 0; i < son_number + 1; i++)
|
||||
{
|
||||
sidechain_public_keys.clear();
|
||||
sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin_address " + fc::to_pretty_string(i);
|
||||
sth.create_son("sonaccount" + fc::to_pretty_string(i),
|
||||
"http://son" + fc::to_pretty_string(i), false);
|
||||
"http://son" + fc::to_pretty_string(i),
|
||||
sidechain_public_keys,
|
||||
false);
|
||||
}
|
||||
BOOST_CHECK(generate_maintenance_block());
|
||||
|
||||
|
|
@ -343,9 +385,17 @@ BOOST_AUTO_TEST_CASE( list_son )
|
|||
BOOST_TEST_MESSAGE("List SONs cli wallet tests begin");
|
||||
try
|
||||
{
|
||||
flat_map<graphene::peerplays_sidechain::sidechain_type, string> sidechain_public_keys;
|
||||
|
||||
son_test_helper sth(*this);
|
||||
sth.create_son("son1account", "http://son1");
|
||||
sth.create_son("son2account", "http://son2");
|
||||
|
||||
sidechain_public_keys.clear();
|
||||
sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin_address 1";
|
||||
sth.create_son("son1account", "http://son1", sidechain_public_keys);
|
||||
|
||||
sidechain_public_keys.clear();
|
||||
sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin_address 2";
|
||||
sth.create_son("son2account", "http://son2", sidechain_public_keys);
|
||||
|
||||
auto res = con.wallet_api_ptr->list_sons("", 100);
|
||||
BOOST_REQUIRE(res.size() == 2);
|
||||
|
|
@ -365,9 +415,17 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test )
|
|||
BOOST_TEST_MESSAGE("SON update_son_votes cli wallet tests begin");
|
||||
try
|
||||
{
|
||||
son_test_helper sth(*this);
|
||||
sth.create_son("son1account", "http://son1");
|
||||
sth.create_son("son2account", "http://son2");
|
||||
flat_map<graphene::peerplays_sidechain::sidechain_type, string> sidechain_public_keys;
|
||||
|
||||
son_test_helper sth(*this);
|
||||
|
||||
sidechain_public_keys.clear();
|
||||
sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin_address 1";
|
||||
sth.create_son("son1account", "http://son1", sidechain_public_keys);
|
||||
|
||||
sidechain_public_keys.clear();
|
||||
sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin_address 2";
|
||||
sth.create_son("son2account", "http://son2", sidechain_public_keys);
|
||||
|
||||
BOOST_TEST_MESSAGE("Vote for 2 accounts with update_son_votes");
|
||||
|
||||
|
|
@ -392,7 +450,7 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test )
|
|||
accepted.push_back("son1account");
|
||||
accepted.push_back("son2account");
|
||||
update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted,
|
||||
rejected, 15, true);
|
||||
rejected, 2, true);
|
||||
BOOST_CHECK(generate_block());
|
||||
BOOST_CHECK(generate_maintenance_block());
|
||||
|
||||
|
|
@ -412,7 +470,7 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test )
|
|||
rejected.clear();
|
||||
rejected.push_back("son1account");
|
||||
update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted,
|
||||
rejected, 15, true);
|
||||
rejected, 1, true);
|
||||
BOOST_CHECK(generate_maintenance_block());
|
||||
|
||||
// Verify the votes
|
||||
|
|
@ -431,7 +489,7 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test )
|
|||
rejected.clear();
|
||||
rejected.push_back("son1accnt");
|
||||
BOOST_CHECK_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted,
|
||||
rejected, 15, true), fc::exception);
|
||||
rejected, 1, true), fc::exception);
|
||||
BOOST_CHECK(generate_block());
|
||||
|
||||
// Verify the votes
|
||||
|
|
@ -449,7 +507,7 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test )
|
|||
rejected.clear();
|
||||
rejected.push_back("son2account");
|
||||
update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted,
|
||||
rejected, 15, true);
|
||||
rejected, 0, true);
|
||||
BOOST_CHECK(generate_maintenance_block());
|
||||
|
||||
// Verify the votes
|
||||
|
|
@ -468,7 +526,24 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test )
|
|||
rejected.push_back("son1accnt");
|
||||
accepted.push_back("son1accnt");
|
||||
BOOST_REQUIRE_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted,
|
||||
rejected, 15, true), fc::exception);
|
||||
rejected, 1, true), fc::exception);
|
||||
BOOST_CHECK(generate_maintenance_block());
|
||||
|
||||
// Verify the votes
|
||||
son1_obj = con.wallet_api_ptr->get_son("son1account");
|
||||
son1_end_votes = son1_obj.total_votes;
|
||||
BOOST_CHECK(son1_end_votes == son1_start_votes);
|
||||
son1_start_votes = son1_end_votes;
|
||||
son2_obj = con.wallet_api_ptr->get_son("son2account");
|
||||
son2_end_votes = son2_obj.total_votes;
|
||||
BOOST_CHECK(son2_end_votes == son2_start_votes);
|
||||
son2_start_votes = son2_end_votes;
|
||||
|
||||
// Try to accept and reject empty lists
|
||||
accepted.clear();
|
||||
rejected.clear();
|
||||
BOOST_REQUIRE_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted,
|
||||
rejected, 1, true), fc::exception);
|
||||
BOOST_CHECK(generate_maintenance_block());
|
||||
|
||||
// Verify the votes
|
||||
|
|
@ -497,9 +572,17 @@ BOOST_AUTO_TEST_CASE( related_functions )
|
|||
global_property_object gpo = con.wallet_api_ptr->get_global_properties();
|
||||
BOOST_CHECK(gpo.active_sons.size() == 0);
|
||||
|
||||
flat_map<graphene::peerplays_sidechain::sidechain_type, string> sidechain_public_keys;
|
||||
|
||||
son_test_helper sth(*this);
|
||||
sth.create_son("son1account", "http://son1");
|
||||
sth.create_son("son2account", "http://son2");
|
||||
|
||||
sidechain_public_keys.clear();
|
||||
sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin_address 1";
|
||||
sth.create_son("son1account", "http://son1", sidechain_public_keys);
|
||||
|
||||
sidechain_public_keys.clear();
|
||||
sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin_address 2";
|
||||
sth.create_son("son2account", "http://son2", sidechain_public_keys);
|
||||
|
||||
gpo = con.wallet_api_ptr->get_global_properties();
|
||||
BOOST_CHECK(gpo.active_sons.size() == 2);
|
||||
|
|
@ -512,6 +595,148 @@ BOOST_AUTO_TEST_CASE( related_functions )
|
|||
BOOST_TEST_MESSAGE("SON-related functions cli wallet tests end");
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE( cli_list_active_sons, cli_fixture )
|
||||
{
|
||||
BOOST_TEST_MESSAGE("SON cli wallet tests for list_active_sons begin");
|
||||
try
|
||||
{
|
||||
son_test_helper sth(*this);
|
||||
|
||||
signed_transaction vote_tx;
|
||||
global_property_object gpo;
|
||||
|
||||
gpo = con.wallet_api_ptr->get_global_properties();
|
||||
unsigned int son_number = gpo.parameters.maximum_son_count;
|
||||
|
||||
flat_map<graphene::peerplays_sidechain::sidechain_type, string> sidechain_public_keys;
|
||||
|
||||
// create son accounts
|
||||
for(unsigned int i = 0; i < son_number + 1; i++)
|
||||
{
|
||||
sidechain_public_keys.clear();
|
||||
sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin_address " + fc::to_pretty_string(i);
|
||||
sth.create_son("sonaccount" + fc::to_pretty_string(i),
|
||||
"http://son" + fc::to_pretty_string(i),
|
||||
sidechain_public_keys,
|
||||
false);
|
||||
}
|
||||
BOOST_CHECK(generate_maintenance_block());
|
||||
|
||||
BOOST_TEST_MESSAGE("Voting for SONs");
|
||||
for(unsigned int i = 1; i < son_number + 1; i++)
|
||||
{
|
||||
std::string name = "sonaccount" + fc::to_pretty_string(i);
|
||||
vote_tx = con.wallet_api_ptr->vote_for_son(name, name, true, true);
|
||||
}
|
||||
BOOST_CHECK(generate_maintenance_block());
|
||||
|
||||
for(unsigned int i = 1; i < son_number; i++)
|
||||
{
|
||||
std::string name1 = "sonaccount" + fc::to_pretty_string(i);
|
||||
std::string name2 = "sonaccount" + fc::to_pretty_string(i + 1);
|
||||
vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, true, true);
|
||||
}
|
||||
BOOST_CHECK(generate_maintenance_block());
|
||||
gpo = con.wallet_api_ptr->get_global_properties();
|
||||
BOOST_TEST_MESSAGE("gpo: " << gpo.active_sons.size());
|
||||
|
||||
BOOST_CHECK(gpo.active_sons.size() == son_number);
|
||||
|
||||
map<string, son_id_type> active_sons = con.wallet_api_ptr->list_active_sons();
|
||||
BOOST_CHECK(active_sons.size() == son_number);
|
||||
for(unsigned int i = 1; i < son_number + 1; i++)
|
||||
{
|
||||
std::string name = "sonaccount" + fc::to_pretty_string(i);
|
||||
BOOST_CHECK(active_sons.find(name) != active_sons.end());
|
||||
}
|
||||
|
||||
// check list_active_son after SON deletion
|
||||
con.wallet_api_ptr->delete_son("sonaccount1", true);
|
||||
BOOST_CHECK_NO_THROW(con.wallet_api_ptr->list_active_sons());
|
||||
} catch( fc::exception& e ) {
|
||||
BOOST_TEST_MESSAGE("SON cli wallet tests exception");
|
||||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
}
|
||||
BOOST_TEST_MESSAGE("SON cli wallet tests for list_active_sons end");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( maintenance_test )
|
||||
{
|
||||
BOOST_TEST_MESSAGE("SON maintenance cli wallet tests begin");
|
||||
try
|
||||
{
|
||||
son_test_helper sth(*this);
|
||||
|
||||
std::string name("sonaccount1");
|
||||
|
||||
global_property_object gpo;
|
||||
gpo = con.wallet_api_ptr->get_global_properties();
|
||||
unsigned int son_number = gpo.parameters.maximum_son_count;
|
||||
|
||||
flat_map<graphene::peerplays_sidechain::sidechain_type, string> sidechain_public_keys;
|
||||
|
||||
// create son accounts
|
||||
for(unsigned int i = 0; i < son_number + 1; i++)
|
||||
{
|
||||
sidechain_public_keys.clear();
|
||||
sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin_address " + fc::to_pretty_string(i);
|
||||
sth.create_son("sonaccount" + fc::to_pretty_string(i),
|
||||
"http://son" + fc::to_pretty_string(i),
|
||||
sidechain_public_keys,
|
||||
false);
|
||||
}
|
||||
BOOST_CHECK(generate_maintenance_block());
|
||||
|
||||
BOOST_TEST_MESSAGE("Voting for SONs");
|
||||
for(unsigned int i = 1; i < son_number + 1; i++)
|
||||
{
|
||||
con.wallet_api_ptr->vote_for_son("sonaccount" + fc::to_pretty_string(i), name, true, true);
|
||||
}
|
||||
BOOST_CHECK(generate_maintenance_block());
|
||||
|
||||
son_object son_obj = con.wallet_api_ptr->get_son(name);
|
||||
BOOST_CHECK(son_obj.status == son_status::active);
|
||||
|
||||
// put SON in maintenance mode
|
||||
con.wallet_api_ptr->request_son_maintenance(name, true);
|
||||
BOOST_CHECK(generate_block());
|
||||
|
||||
// check SON is in request_maintenance
|
||||
son_obj = con.wallet_api_ptr->get_son(name);
|
||||
BOOST_CHECK(son_obj.status == son_status::request_maintenance);
|
||||
|
||||
// restore SON activity
|
||||
con.wallet_api_ptr->cancel_request_son_maintenance(name, true);
|
||||
BOOST_CHECK(generate_block());
|
||||
|
||||
// check SON is active
|
||||
son_obj = con.wallet_api_ptr->get_son(name);
|
||||
BOOST_CHECK(son_obj.status == son_status::active);
|
||||
|
||||
// put SON in maintenance mode
|
||||
con.wallet_api_ptr->request_son_maintenance(name, true);
|
||||
BOOST_CHECK(generate_block());
|
||||
|
||||
// check SON is in request_maintenance
|
||||
son_obj = con.wallet_api_ptr->get_son(name);
|
||||
BOOST_CHECK(son_obj.status == son_status::request_maintenance);
|
||||
|
||||
// process maintenance
|
||||
BOOST_CHECK(generate_maintenance_block());
|
||||
|
||||
// check SON is in maintenance
|
||||
son_obj = con.wallet_api_ptr->get_son(name);
|
||||
BOOST_CHECK(son_obj.status == son_status::in_maintenance);
|
||||
|
||||
|
||||
} catch( fc::exception& e ) {
|
||||
BOOST_TEST_MESSAGE("SON cli wallet tests exception");
|
||||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
}
|
||||
BOOST_TEST_MESSAGE("SON maintenance cli wallet tests end");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@
|
|||
#include <fc/crypto/digest.hpp>
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
|
@ -265,11 +266,11 @@ void database_fixture::verify_asset_supplies( const database& db )
|
|||
total_balances[betting_market_group.asset_id] += o.fees_collected;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint64_t sweeps_vestings = 0;
|
||||
for( const sweeps_vesting_balance_object& svbo: db.get_index_type< sweeps_vesting_balance_index >().indices() )
|
||||
sweeps_vestings += svbo.balance;
|
||||
|
||||
|
||||
total_balances[db.get_global_properties().parameters.sweeps_distribution_asset()] += sweeps_vestings / SWEEPS_VESTING_BALANCE_MULTIPLIER;
|
||||
total_balances[asset_id_type()] += db.get_dynamic_global_properties().witness_budget;
|
||||
total_balances[asset_id_type()] += db.get_dynamic_global_properties().son_budget;
|
||||
|
|
@ -413,6 +414,23 @@ void database_fixture::generate_blocks(fc::time_point_sec timestamp, bool miss_i
|
|||
generate_block(skip);
|
||||
}
|
||||
|
||||
bool database_fixture::generate_maintenance_block() {
|
||||
try {
|
||||
fc::ecc::private_key committee_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan")));
|
||||
uint32_t skip = ~database::skip_fork_db;
|
||||
auto maint_time = db.get_dynamic_global_properties().next_maintenance_time;
|
||||
auto slots_to_miss = db.get_slot_at_time(maint_time);
|
||||
db.generate_block(db.get_slot_time(slots_to_miss),
|
||||
db.get_scheduled_witness(slots_to_miss),
|
||||
committee_key,
|
||||
skip);
|
||||
return true;
|
||||
} catch (std::exception& e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
account_create_operation database_fixture::make_account(
|
||||
const std::string& name /* = "nathan" */,
|
||||
public_key_type key /* = key_id_type() */
|
||||
|
|
@ -731,7 +749,7 @@ const witness_object& database_fixture::create_witness( const account_object& ow
|
|||
witness_create_operation op;
|
||||
op.witness_account = owner.id;
|
||||
op.block_signing_key = signing_private_key.get_public_key();
|
||||
|
||||
|
||||
secret_hash_type::encoder enc;
|
||||
fc::raw::pack(enc, signing_private_key);
|
||||
fc::raw::pack(enc, secret_hash_type());
|
||||
|
|
@ -1116,12 +1134,12 @@ int64_t database_fixture::get_balance( const account_object& account, const asse
|
|||
}
|
||||
|
||||
int64_t database_fixture::get_dividend_pending_payout_balance(asset_id_type dividend_holder_asset_type,
|
||||
account_id_type dividend_holder_account_id,
|
||||
asset_id_type dividend_payout_asset_type) const
|
||||
account_id_type dividend_holder_account_id,
|
||||
asset_id_type dividend_payout_asset_type) const
|
||||
{
|
||||
const pending_dividend_payout_balance_for_holder_object_index& pending_payout_balance_index =
|
||||
const pending_dividend_payout_balance_for_holder_object_index& pending_payout_balance_index =
|
||||
db.get_index_type<pending_dividend_payout_balance_for_holder_object_index>();
|
||||
auto pending_payout_iter =
|
||||
auto pending_payout_iter =
|
||||
pending_payout_balance_index.indices().get<by_dividend_payout_account>().find(boost::make_tuple(dividend_holder_asset_type, dividend_payout_asset_type, dividend_holder_account_id));
|
||||
if (pending_payout_iter == pending_payout_balance_index.indices().get<by_dividend_payout_account>().end())
|
||||
return 0;
|
||||
|
|
@ -1342,7 +1360,7 @@ void database_fixture::delete_sport(sport_id_type sport_id)
|
|||
sport_delete_op.sport_id = sport_id;
|
||||
process_operation_by_witnesses(sport_delete_op);
|
||||
} FC_CAPTURE_AND_RETHROW( (sport_id) ) }
|
||||
|
||||
|
||||
const event_group_object& database_fixture::create_event_group(internationalized_string_type name, sport_id_type sport_id)
|
||||
{ try {
|
||||
event_group_create_operation event_group_create_op;
|
||||
|
|
@ -1372,7 +1390,7 @@ void database_fixture::delete_event_group(event_group_id_type event_group_id)
|
|||
process_operation_by_witnesses(event_group_delete_op);
|
||||
} FC_CAPTURE_AND_RETHROW( (event_group_id) )
|
||||
}
|
||||
|
||||
|
||||
void database_fixture::try_update_event_group(event_group_id_type event_group_id,
|
||||
fc::optional<object_id_type> sport_id,
|
||||
fc::optional<internationalized_string_type> name,
|
||||
|
|
@ -1412,7 +1430,7 @@ void database_fixture::update_event_impl(event_id_type event_id,
|
|||
fc::optional<object_id_type> event_group_id,
|
||||
fc::optional<internationalized_string_type> name,
|
||||
fc::optional<internationalized_string_type> season,
|
||||
fc::optional<event_status> status,
|
||||
fc::optional<event_status> status,
|
||||
bool force)
|
||||
{ try {
|
||||
event_update_operation event_update_op;
|
||||
|
|
@ -1449,9 +1467,9 @@ void database_fixture::update_betting_market_rules(betting_market_rules_id_type
|
|||
process_operation_by_witnesses(betting_market_rules_update_op);
|
||||
} FC_CAPTURE_AND_RETHROW( (name)(description) ) }
|
||||
|
||||
const betting_market_group_object& database_fixture::create_betting_market_group(internationalized_string_type description,
|
||||
event_id_type event_id,
|
||||
betting_market_rules_id_type rules_id,
|
||||
const betting_market_group_object& database_fixture::create_betting_market_group(internationalized_string_type description,
|
||||
event_id_type event_id,
|
||||
betting_market_rules_id_type rules_id,
|
||||
asset_id_type asset_id,
|
||||
bool never_in_play,
|
||||
uint32_t delay_before_settling)
|
||||
|
|
@ -1521,7 +1539,7 @@ void database_fixture::update_betting_market(betting_market_id_type betting_mark
|
|||
bet_place_op.amount_to_bet = amount_to_bet;
|
||||
bet_place_op.backer_multiplier = backer_multiplier;
|
||||
bet_place_op.back_or_lay = back_or_lay;
|
||||
|
||||
|
||||
trx.operations.push_back(bet_place_op);
|
||||
trx.validate();
|
||||
processed_transaction ptx = db.push_transaction(trx, ~0);
|
||||
|
|
|
|||
|
|
@ -199,6 +199,12 @@ struct database_fixture {
|
|||
*/
|
||||
void generate_blocks(fc::time_point_sec timestamp, bool miss_intermediate_blocks = true, uint32_t skip = ~0);
|
||||
|
||||
///////////
|
||||
/// @brief Skip intermediate blocks, and generate a maintenance block
|
||||
/// @returns true on success
|
||||
///////////
|
||||
bool generate_maintenance_block();
|
||||
|
||||
account_create_operation make_account(
|
||||
const std::string& name = "nathan",
|
||||
public_key_type = public_key_type()
|
||||
|
|
@ -295,7 +301,7 @@ struct database_fixture {
|
|||
int64_t get_balance( account_id_type account, asset_id_type a )const;
|
||||
int64_t get_balance( const account_object& account, const asset_object& a )const;
|
||||
int64_t get_dividend_pending_payout_balance(asset_id_type dividend_holder_asset_type,
|
||||
account_id_type dividend_holder_account_id,
|
||||
account_id_type dividend_holder_account_id,
|
||||
asset_id_type dividend_payout_asset_type) const;
|
||||
vector< operation_history_object > get_operation_history( account_id_type account_id )const;
|
||||
void process_operation_by_witnesses(operation op);
|
||||
|
|
@ -321,7 +327,7 @@ struct database_fixture {
|
|||
fc::optional<internationalized_string_type> season,
|
||||
fc::optional<event_status> status,
|
||||
bool force);
|
||||
BOOST_PARAMETER_MEMBER_FUNCTION((void), update_event, keywords::tag,
|
||||
BOOST_PARAMETER_MEMBER_FUNCTION((void), update_event, keywords::tag,
|
||||
(required (event_id, (event_id_type)))
|
||||
(optional (event_group_id, (fc::optional<object_id_type>), fc::optional<object_id_type>())
|
||||
(name, (fc::optional<internationalized_string_type>), fc::optional<internationalized_string_type>())
|
||||
|
|
@ -336,9 +342,9 @@ struct database_fixture {
|
|||
void update_betting_market_rules(betting_market_rules_id_type rules_id,
|
||||
fc::optional<internationalized_string_type> name,
|
||||
fc::optional<internationalized_string_type> description);
|
||||
const betting_market_group_object& create_betting_market_group(internationalized_string_type description,
|
||||
event_id_type event_id,
|
||||
betting_market_rules_id_type rules_id,
|
||||
const betting_market_group_object& create_betting_market_group(internationalized_string_type description,
|
||||
event_id_type event_id,
|
||||
betting_market_rules_id_type rules_id,
|
||||
asset_id_type asset_id,
|
||||
bool never_in_play,
|
||||
uint32_t delay_before_settling);
|
||||
|
|
@ -347,7 +353,7 @@ struct database_fixture {
|
|||
fc::optional<object_id_type> rules_id,
|
||||
fc::optional<betting_market_group_status> status,
|
||||
bool force);
|
||||
BOOST_PARAMETER_MEMBER_FUNCTION((void), update_betting_market_group, keywords::tag,
|
||||
BOOST_PARAMETER_MEMBER_FUNCTION((void), update_betting_market_group, keywords::tag,
|
||||
(required (betting_market_group_id, (betting_market_group_id_type)))
|
||||
(optional (description, (fc::optional<internationalized_string_type>), fc::optional<internationalized_string_type>())
|
||||
(rules_id, (fc::optional<object_id_type>), fc::optional<object_id_type>())
|
||||
|
|
|
|||
418
tests/peerplays_sidechain/bitcoin_utils_test.cpp
Normal file
418
tests/peerplays_sidechain/bitcoin_utils_test.cpp
Normal file
|
|
@ -0,0 +1,418 @@
|
|||
#include <boost/test/unit_test.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin_utils.hpp>
|
||||
#include <fc/crypto/hex.hpp>
|
||||
#include <fc/crypto/elliptic.hpp>
|
||||
#include <fc/crypto/base58.hpp>
|
||||
#include <secp256k1.h>
|
||||
|
||||
using namespace graphene::peerplays_sidechain;
|
||||
|
||||
BOOST_AUTO_TEST_CASE(tx_serialization)
|
||||
{
|
||||
// use real mainnet transaction
|
||||
// txid: 6189e3febb5a21cee8b725aa1ef04ffce7e609448446d3a8d6f483c634ef5315
|
||||
// json: {"txid":"6189e3febb5a21cee8b725aa1ef04ffce7e609448446d3a8d6f483c634ef5315","hash":"6189e3febb5a21cee8b725aa1ef04ffce7e609448446d3a8d6f483c634ef5315","version":1,"size":224,"vsize":224,"weight":896,"locktime":0,"vin":[{"txid":"55d079ca797fee81416b71b373abedd8722e33c9f73177be0166b5d5fdac478b","vout":0,"scriptSig":{"asm":"3045022100d82e57d4d11d3b811d07f2fa4ded2fb8a3b7bb1d3e9f293433de5c0d1093c3bd02206704ccd2ff437e2f7716b5e9f2502a9cbb41f1245a18b2b10296980f1ae38253[ALL] 02be9919a5ba373b1af58ad757db19e7c836116bb8138e0c6d99599e4db96568f4","hex":"483045022100d82e57d4d11d3b811d07f2fa4ded2fb8a3b7bb1d3e9f293433de5c0d1093c3bd02206704ccd2ff437e2f7716b5e9f2502a9cbb41f1245a18b2b10296980f1ae38253012102be9919a5ba373b1af58ad757db19e7c836116bb8138e0c6d99599e4db96568f4"},"sequence":4294967295}],"vout":[{"value":1.26491535,"n":0,"scriptPubKey":{"asm":"OP_DUP OP_HASH160 95783804d28e528fbc4b48c7700471e6845804eb OP_EQUALVERIFY OP_CHECKSIG","hex":"76a91495783804d28e528fbc4b48c7700471e6845804eb88ac","reqSigs":1,"type":"pubkeyhash","addresses":["1EdKhXv7zjGowPzgDQ4z1wa2ukVrXRXXkP"]}},{"value":0.0002,"n":1,"scriptPubKey":{"asm":"OP_HASH160 fb0670971091da8248b5c900c6515727a20e8662 OP_EQUAL","hex":"a914fb0670971091da8248b5c900c6515727a20e866287","reqSigs":1,"type":"scripthash","addresses":["3QaKF8zobqcqY8aS6nxCD5ZYdiRfL3RCmU"]}}]}
|
||||
// hex: "01000000018b47acfdd5b56601be7731f7c9332e72d8edab73b3716b4181ee7f79ca79d055000000006b483045022100d82e57d4d11d3b811d07f2fa4ded2fb8a3b7bb1d3e9f293433de5c0d1093c3bd02206704ccd2ff437e2f7716b5e9f2502a9cbb41f1245a18b2b10296980f1ae38253012102be9919a5ba373b1af58ad757db19e7c836116bb8138e0c6d99599e4db96568f4ffffffff028f1b8a07000000001976a91495783804d28e528fbc4b48c7700471e6845804eb88ac204e00000000000017a914fb0670971091da8248b5c900c6515727a20e86628700000000"
|
||||
fc::string strtx("01000000018b47acfdd5b56601be7731f7c9332e72d8edab73b3716b4181ee7f79ca79d055000000006b483045022100d82e57d4d11d3b811d07f2fa4ded2fb8a3b7bb1d3e9f293433de5c0d1093c3bd02206704ccd2ff437e2f7716b5e9f2502a9cbb41f1245a18b2b10296980f1ae38253012102be9919a5ba373b1af58ad757db19e7c836116bb8138e0c6d99599e4db96568f4ffffffff028f1b8a07000000001976a91495783804d28e528fbc4b48c7700471e6845804eb88ac204e00000000000017a914fb0670971091da8248b5c900c6515727a20e86628700000000");
|
||||
bytes bintx;
|
||||
bintx.resize(strtx.length() / 2);
|
||||
fc::from_hex(strtx, reinterpret_cast<char*>(&bintx[0]), bintx.size());
|
||||
btc_tx tx;
|
||||
BOOST_CHECK_NO_THROW(tx.fill_from_bytes(bintx));
|
||||
BOOST_CHECK(tx.nVersion == 1);
|
||||
BOOST_CHECK(tx.nLockTime == 0);
|
||||
BOOST_CHECK(tx.vin.size() == 1);
|
||||
BOOST_CHECK(tx.vout.size() == 2);
|
||||
bytes buff;
|
||||
tx.to_bytes(buff);
|
||||
BOOST_CHECK(bintx == buff);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(pw_transfer)
|
||||
{
|
||||
// key set for the old Primary Wallet
|
||||
std::vector<fc::ecc::private_key> priv_old;
|
||||
for(unsigned i = 0; i < 15; ++i)
|
||||
{
|
||||
const char* seed = reinterpret_cast<const char*>(&i);
|
||||
fc::sha256 h = fc::sha256::hash(seed, sizeof(i));
|
||||
priv_old.push_back(fc::ecc::private_key::generate_from_seed(h));
|
||||
}
|
||||
// print old keys
|
||||
for(auto key: priv_old)
|
||||
{
|
||||
fc::sha256 secret = key.get_secret();
|
||||
bytes data({239});
|
||||
data.insert(data.end(), secret.data(), secret.data() + secret.data_size());
|
||||
fc::sha256 cs = fc::sha256::hash(fc::sha256::hash((char*)&data[0], data.size()));
|
||||
data.insert(data.end(), cs.data(), cs.data() + 4);
|
||||
}
|
||||
std::vector<fc::ecc::public_key> pub_old;
|
||||
for(auto& key: priv_old)
|
||||
pub_old.push_back(key.get_public_key());
|
||||
// old key weights
|
||||
std::vector<std::pair<fc::ecc::public_key, int> > weights_old;
|
||||
for(unsigned i = 0; i < 15; ++i)
|
||||
weights_old.push_back(std::make_pair(pub_old[i], i + 1));
|
||||
// redeem script for old PW
|
||||
bytes redeem_old =generate_redeem_script(weights_old);
|
||||
|
||||
// Old PW address
|
||||
std::string old_pw = p2wsh_address_from_redeem_script(redeem_old, bitcoin_network::testnet);
|
||||
// This address was filled with testnet transaction 508a36d65de66db7c57ee6c5502068ebdcba996ca2df23ef42d901ec8fba1766
|
||||
BOOST_REQUIRE(old_pw == "tb1qfhstznulf5cmjzahlkmnuuvs0tkjtwjlme3ugz8jzfjanf8h5rwsp45t7e");
|
||||
|
||||
bytes scriptPubKey = lock_script_for_redeem_script(redeem_old);
|
||||
|
||||
// key set for the new Primary Wallet
|
||||
std::vector<fc::ecc::private_key> priv_new;
|
||||
for(unsigned i = 16; i < 31; ++i)
|
||||
{
|
||||
const char* seed = reinterpret_cast<const char*>(&i);
|
||||
fc::sha256 h = fc::sha256::hash(seed, sizeof(i));
|
||||
priv_new.push_back(fc::ecc::private_key::generate_from_seed(h));
|
||||
}
|
||||
std::vector<fc::ecc::public_key> pub_new;
|
||||
for(auto& key: priv_new)
|
||||
pub_new.push_back(key.get_public_key());
|
||||
// new key weights
|
||||
std::vector<std::pair<fc::ecc::public_key, int> > weights_new;
|
||||
for(unsigned i = 0; i < 15; ++i)
|
||||
weights_new.push_back(std::make_pair(pub_new[i], 16 - i));
|
||||
// redeem script for new PW
|
||||
bytes redeem_new =generate_redeem_script(weights_new);
|
||||
// New PW address
|
||||
std::string new_pw = p2wsh_address_from_redeem_script(redeem_new, bitcoin_network::testnet);
|
||||
BOOST_REQUIRE(new_pw == "tb1qzegrz8r8z8ddfkql8595d90czng6eyjmx4ur73ls4pq57jg99qhsh9fd2y");
|
||||
|
||||
// try to move funds from old wallet to new one
|
||||
|
||||
// get unspent outputs for old wallet with list_uspent (address should be
|
||||
// added to wallet with import_address before). It should return
|
||||
// 1 UTXO: [508a36d65de66db7c57ee6c5502068ebdcba996ca2df23ef42d901ec8fba1766:0]
|
||||
// with 20000 satoshis
|
||||
// So, we creating a raw transaction with 1 input and one output that gets
|
||||
// 20000 - fee satoshis with createrawtransaction call (bitcoin_rpc_client::prepare_tx)
|
||||
// Here we just serialize the transaction without scriptSig in inputs then sign it.
|
||||
btc_outpoint outpoint;
|
||||
outpoint.hash = fc::uint256("508a36d65de66db7c57ee6c5502068ebdcba996ca2df23ef42d901ec8fba1766");
|
||||
// reverse hash due to the different from_hex algo
|
||||
std::reverse(outpoint.hash.data(), outpoint.hash.data() + outpoint.hash.data_size());
|
||||
outpoint.n = 0;
|
||||
btc_in input;
|
||||
input.prevout = outpoint;
|
||||
input.nSequence = 0xffffffff;
|
||||
btc_out output;
|
||||
output.nValue = 19000;
|
||||
output.scriptPubKey = lock_script_for_redeem_script(redeem_new);
|
||||
btc_tx tx;
|
||||
tx.nVersion = 2;
|
||||
tx.nLockTime = 0;
|
||||
tx.hasWitness = false;
|
||||
tx.vin.push_back(input);
|
||||
tx.vout.push_back(output);
|
||||
bytes unsigned_tx;
|
||||
tx.to_bytes(unsigned_tx);
|
||||
std::vector<uint64_t> in_amounts({20000});
|
||||
std::vector<fc::optional<fc::ecc::private_key>> keys_to_sign;
|
||||
for(auto key: priv_old)
|
||||
keys_to_sign.push_back(fc::optional<fc::ecc::private_key>(key));
|
||||
bytes signed_tx =sign_pw_transfer_transaction(unsigned_tx, in_amounts, redeem_old, keys_to_sign);
|
||||
// this is real testnet tx with id 1734a2f6192c3953c90f9fd7f69eba16eeb0922207f81f3af32d6534a6f8e850
|
||||
BOOST_CHECK(fc::to_hex((char*)&signed_tx[0], signed_tx.size()) == "020000000001016617ba8fec01d942ef23dfa26c99badceb682050c5e67ec5b76de65dd6368a500000000000ffffffff01384a0000000000002200201650311c6711dad4d81f3d0b4695f814d1ac925b35783f47f0a8414f4905282f10473044022028cf6df7ed5c2761d7aa2af20717c8b5ace168a7800d6a566f2c1ae28160cae502205e01a3d91f5b9870577e36fbc26ce0cecc3e628cc376c7016364ec3f370703140147304402205c9a88cbe41eb9c6a16ba1d747456222cbe951d04739d21309ef0c0cf00727f202202d06db830ee5823882c7b6f82b708111a8f37741878896cd3558fb91efe8076401473044022009c3184fc0385eb7ed8dc0374791cbdace0eff0dc27dd80ac68f8cb81110f700022042267e8a8788c314347234ea10db6c1ec21a2d423b784cbfbaadf3b2393c44630147304402202363ce306570dc0bbf6d18d41b67c6488a014a91d8e24c03670b4f65523aca12022029d04c114b8e93d982cadee89d80bb25c5c8bc437d6cd2bfce8e0d83a08d14410148304502210087b4742e5cf9c77ca9f99928e7c7087e7d786e09216485628509e4e0b2f29d7e02207daf2eaee9fe8bf117074be137b7ae4b8503a4f6d263424e8e6a16405d5b723c0147304402204f1c3ed8cf595bfaf79d90f4c55c04c17bb6d446e3b9beca7ee6ee7895c6b752022022ac032f219a81b2845d0a1abfb904e40036a3ad332e7dfada6fda21ef7080b501483045022100d020eca4ba1aa77de9caf98f3a29f74f55268276860b9fa35fa16cfc00219dd8022028237de6ad063116cf8182d2dd45a09cb90c2ec8104d793eb3635a1290027cd6014730440220322193b0feba7356651465b86463c7619cd3d96729df6242e9571c74ff1c3c2902206e1de8e77b71c7b6031a934b52321134b6a8d138e2124e90f6345decbd543efb01483045022100d70ade49b3f17812785a41711e107b27c3d4981f8e12253629c07ec46ee511af02203e1ea9059ed9165eeff827002c7399a30c478a9b6f2b958621bfbc6713ab4dd30147304402206f7f10d9993c7019360276bbe790ab587adadeab08088593a9a0c56524aca4df02207c147fe2e51484801a4e059e611e7514729d685a5df892dcf02ba59d455e678101483045022100d5071b8039364bfaa53ef5e22206f773539b082f28bd1fbaaea995fa28aae0f5022056edf7a7bdd8a9a54273a667be5bcd11191fc871798fb44f6e1e35c95d86a81201483045022100a39f8ffbcd9c3f0591fc731a9856c8e024041017cba20c9935f13e4abcf9e9dc0220786823b8cd55664ff9ad6277899aacfd56fa8e48c38881482418b7d50ca27211014730440220361d3b87fcc2b1c12a9e7c684c78192ccb7fe51b90c281b7058384b0b036927a0220434c9b403ee3802b4e5b53feb9bb37d2a9d8746c3688da993549dd9d9954c6800147304402206dc4c3a4407fe9cbffb724928aa0597148c14a20d0d7fbb36ad5d3e2a3abf85e022039ef7baebbf08494495a038b009c6d4ff4b91c38db840673b87f6c27c3b53e7e01483045022100cadac495ea78d0ce9678a4334b8c43f7fafeea5a59413cc2a0144addb63485f9022078ca133e020e3afd0e79936337afefc21d84d3839f5a225a0f3d3eebc15f959901fd5c02007c21030e88484f2bb5dcfc0b326e9eb565c27c8291efb064d060d226916857a2676e62ac635193687c2102151ad794a3aeb3cf9c190120da3d13d36cd8bdf21ca1ccb15debd61c601314b0ac635293687c2103b45a5955ea7847d121225c752edaeb4a5d731a056a951a876caaf6d1f69adb7dac635393687c2102def03a6ffade4ffb0017c8d93859a247badd60e2d76d00e2a3713f6621932ec1ac635493687c21035f17aa7d58b8c3ee0d87240fded52b27f3f12768a0a54ba2595e0a929dd87155ac635593687c2103c8582ac6b0bd20cc1b02c6a86bad2ea10cadb758fedd754ba0d97be85b63b5a7ac635693687c21028148a1f9669fc4471e76f7a371d7cc0563b26e0821d9633fd37649744ff54edaac635793687c2102f0313701b0035f0365a59ce1a3d7ae7045e1f2fb25c4656c08071e5baf51483dac635893687c21024c4c25d08173b3c4d4e1375f8107fd7040c2dc0691ae1bf6fe82b8c88a85185fac635993687c210360fe2daa8661a3d25d0df79875d70b1c3d443ade731caafda7488cb68b4071b0ac635a93687c210250e41a6a4abd7b0b3a49eaec24a6fafa99e5aa7b1e3a5aabe60664276df3d937ac635b93687c2103045a32125930ca103c7d7c79b6f379754796cd4ea7fb0059da926e415e3877d3ac635c93687c210344943249d7ca9b47316fef0c2a413dda3a75416a449a29f310ab7fc9d052ed70ac635d93687c2103c62967320b63df5136ff1ef4c7959ef5917ee5a44f75c83e870bc488143d4d69ac635e93687c21020429f776e15770e4dc52bd6f72e6ed6908d51de1c4a64878433c4e3860a48dc4ac635f93680150a000000000");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(pw_separate_sign)
|
||||
{
|
||||
// key set for the old Primary Wallet
|
||||
std::vector<fc::ecc::private_key> priv_old;
|
||||
for(unsigned i = 0; i < 15; ++i)
|
||||
{
|
||||
const char* seed = reinterpret_cast<const char*>(&i);
|
||||
fc::sha256 h = fc::sha256::hash(seed, sizeof(i));
|
||||
priv_old.push_back(fc::ecc::private_key::generate_from_seed(h));
|
||||
}
|
||||
// print old keys
|
||||
for(auto key: priv_old)
|
||||
{
|
||||
fc::sha256 secret = key.get_secret();
|
||||
bytes data({239});
|
||||
data.insert(data.end(), secret.data(), secret.data() + secret.data_size());
|
||||
fc::sha256 cs = fc::sha256::hash(fc::sha256::hash((char*)&data[0], data.size()));
|
||||
data.insert(data.end(), cs.data(), cs.data() + 4);
|
||||
}
|
||||
std::vector<fc::ecc::public_key> pub_old;
|
||||
for(auto& key: priv_old)
|
||||
pub_old.push_back(key.get_public_key());
|
||||
// old key weights
|
||||
std::vector<std::pair<fc::ecc::public_key, int> > weights_old;
|
||||
for(unsigned i = 0; i < 15; ++i)
|
||||
weights_old.push_back(std::make_pair(pub_old[i], i + 1));
|
||||
// redeem script for old PW
|
||||
bytes redeem_old =generate_redeem_script(weights_old);
|
||||
|
||||
// Old PW address
|
||||
std::string old_pw = p2wsh_address_from_redeem_script(redeem_old, bitcoin_network::testnet);
|
||||
// This address was filled with testnet transaction 508a36d65de66db7c57ee6c5502068ebdcba996ca2df23ef42d901ec8fba1766
|
||||
BOOST_REQUIRE(old_pw == "tb1qfhstznulf5cmjzahlkmnuuvs0tkjtwjlme3ugz8jzfjanf8h5rwsp45t7e");
|
||||
|
||||
bytes scriptPubKey = lock_script_for_redeem_script(redeem_old);
|
||||
|
||||
// key set for the new Primary Wallet
|
||||
std::vector<fc::ecc::private_key> priv_new;
|
||||
for(unsigned i = 16; i < 31; ++i)
|
||||
{
|
||||
const char* seed = reinterpret_cast<const char*>(&i);
|
||||
fc::sha256 h = fc::sha256::hash(seed, sizeof(i));
|
||||
priv_new.push_back(fc::ecc::private_key::generate_from_seed(h));
|
||||
}
|
||||
std::vector<fc::ecc::public_key> pub_new;
|
||||
for(auto& key: priv_new)
|
||||
pub_new.push_back(key.get_public_key());
|
||||
// new key weights
|
||||
std::vector<std::pair<fc::ecc::public_key, int> > weights_new;
|
||||
for(unsigned i = 0; i < 15; ++i)
|
||||
weights_new.push_back(std::make_pair(pub_new[i], 16 - i));
|
||||
// redeem script for new PW
|
||||
bytes redeem_new =generate_redeem_script(weights_new);
|
||||
// New PW address
|
||||
std::string new_pw = p2wsh_address_from_redeem_script(redeem_new, bitcoin_network::testnet);
|
||||
BOOST_REQUIRE(new_pw == "tb1qzegrz8r8z8ddfkql8595d90czng6eyjmx4ur73ls4pq57jg99qhsh9fd2y");
|
||||
|
||||
// try to move funds from old wallet to new one
|
||||
|
||||
// get unspent outputs for old wallet with list_uspent (address should be
|
||||
// added to wallet with import_address before). It should return
|
||||
// 1 UTXO: [508a36d65de66db7c57ee6c5502068ebdcba996ca2df23ef42d901ec8fba1766:0]
|
||||
// with 20000 satoshis
|
||||
// So, we creating a raw transaction with 1 input and one output that gets
|
||||
// 20000 - fee satoshis with createrawtransaction call (bitcoin_rpc_client::prepare_tx)
|
||||
// Here we just serialize the transaction without scriptSig in inputs then sign it.
|
||||
btc_outpoint outpoint;
|
||||
outpoint.hash = fc::uint256("508a36d65de66db7c57ee6c5502068ebdcba996ca2df23ef42d901ec8fba1766");
|
||||
// reverse hash due to the different from_hex algo
|
||||
std::reverse(outpoint.hash.data(), outpoint.hash.data() + outpoint.hash.data_size());
|
||||
outpoint.n = 0;
|
||||
btc_in input;
|
||||
input.prevout = outpoint;
|
||||
input.nSequence = 0xffffffff;
|
||||
btc_out output;
|
||||
output.nValue = 19000;
|
||||
output.scriptPubKey = lock_script_for_redeem_script(redeem_new);
|
||||
btc_tx tx;
|
||||
tx.nVersion = 2;
|
||||
tx.nLockTime = 0;
|
||||
tx.hasWitness = false;
|
||||
tx.vin.push_back(input);
|
||||
tx.vout.push_back(output);
|
||||
bytes unsigned_tx;
|
||||
tx.to_bytes(unsigned_tx);
|
||||
std::vector<uint64_t> in_amounts({20000});
|
||||
|
||||
// prepare tx with dummy signs
|
||||
bytes partially_signed_tx = add_dummy_signatures_for_pw_transfer(unsigned_tx, redeem_old, 15);
|
||||
|
||||
// sign with every old key one by one
|
||||
for(unsigned idx = 0; idx < 15; idx++)
|
||||
partially_signed_tx = partially_sign_pw_transfer_transaction(partially_signed_tx, in_amounts, priv_old[idx], idx);
|
||||
|
||||
// now this is real testnet tx with id 1734a2f6192c3953c90f9fd7f69eba16eeb0922207f81f3af32d6534a6f8e850
|
||||
BOOST_CHECK(fc::to_hex((char*)&partially_signed_tx[0], partially_signed_tx.size()) == "020000000001016617ba8fec01d942ef23dfa26c99badceb682050c5e67ec5b76de65dd6368a500000000000ffffffff01384a0000000000002200201650311c6711dad4d81f3d0b4695f814d1ac925b35783f47f0a8414f4905282f10473044022028cf6df7ed5c2761d7aa2af20717c8b5ace168a7800d6a566f2c1ae28160cae502205e01a3d91f5b9870577e36fbc26ce0cecc3e628cc376c7016364ec3f370703140147304402205c9a88cbe41eb9c6a16ba1d747456222cbe951d04739d21309ef0c0cf00727f202202d06db830ee5823882c7b6f82b708111a8f37741878896cd3558fb91efe8076401473044022009c3184fc0385eb7ed8dc0374791cbdace0eff0dc27dd80ac68f8cb81110f700022042267e8a8788c314347234ea10db6c1ec21a2d423b784cbfbaadf3b2393c44630147304402202363ce306570dc0bbf6d18d41b67c6488a014a91d8e24c03670b4f65523aca12022029d04c114b8e93d982cadee89d80bb25c5c8bc437d6cd2bfce8e0d83a08d14410148304502210087b4742e5cf9c77ca9f99928e7c7087e7d786e09216485628509e4e0b2f29d7e02207daf2eaee9fe8bf117074be137b7ae4b8503a4f6d263424e8e6a16405d5b723c0147304402204f1c3ed8cf595bfaf79d90f4c55c04c17bb6d446e3b9beca7ee6ee7895c6b752022022ac032f219a81b2845d0a1abfb904e40036a3ad332e7dfada6fda21ef7080b501483045022100d020eca4ba1aa77de9caf98f3a29f74f55268276860b9fa35fa16cfc00219dd8022028237de6ad063116cf8182d2dd45a09cb90c2ec8104d793eb3635a1290027cd6014730440220322193b0feba7356651465b86463c7619cd3d96729df6242e9571c74ff1c3c2902206e1de8e77b71c7b6031a934b52321134b6a8d138e2124e90f6345decbd543efb01483045022100d70ade49b3f17812785a41711e107b27c3d4981f8e12253629c07ec46ee511af02203e1ea9059ed9165eeff827002c7399a30c478a9b6f2b958621bfbc6713ab4dd30147304402206f7f10d9993c7019360276bbe790ab587adadeab08088593a9a0c56524aca4df02207c147fe2e51484801a4e059e611e7514729d685a5df892dcf02ba59d455e678101483045022100d5071b8039364bfaa53ef5e22206f773539b082f28bd1fbaaea995fa28aae0f5022056edf7a7bdd8a9a54273a667be5bcd11191fc871798fb44f6e1e35c95d86a81201483045022100a39f8ffbcd9c3f0591fc731a9856c8e024041017cba20c9935f13e4abcf9e9dc0220786823b8cd55664ff9ad6277899aacfd56fa8e48c38881482418b7d50ca27211014730440220361d3b87fcc2b1c12a9e7c684c78192ccb7fe51b90c281b7058384b0b036927a0220434c9b403ee3802b4e5b53feb9bb37d2a9d8746c3688da993549dd9d9954c6800147304402206dc4c3a4407fe9cbffb724928aa0597148c14a20d0d7fbb36ad5d3e2a3abf85e022039ef7baebbf08494495a038b009c6d4ff4b91c38db840673b87f6c27c3b53e7e01483045022100cadac495ea78d0ce9678a4334b8c43f7fafeea5a59413cc2a0144addb63485f9022078ca133e020e3afd0e79936337afefc21d84d3839f5a225a0f3d3eebc15f959901fd5c02007c21030e88484f2bb5dcfc0b326e9eb565c27c8291efb064d060d226916857a2676e62ac635193687c2102151ad794a3aeb3cf9c190120da3d13d36cd8bdf21ca1ccb15debd61c601314b0ac635293687c2103b45a5955ea7847d121225c752edaeb4a5d731a056a951a876caaf6d1f69adb7dac635393687c2102def03a6ffade4ffb0017c8d93859a247badd60e2d76d00e2a3713f6621932ec1ac635493687c21035f17aa7d58b8c3ee0d87240fded52b27f3f12768a0a54ba2595e0a929dd87155ac635593687c2103c8582ac6b0bd20cc1b02c6a86bad2ea10cadb758fedd754ba0d97be85b63b5a7ac635693687c21028148a1f9669fc4471e76f7a371d7cc0563b26e0821d9633fd37649744ff54edaac635793687c2102f0313701b0035f0365a59ce1a3d7ae7045e1f2fb25c4656c08071e5baf51483dac635893687c21024c4c25d08173b3c4d4e1375f8107fd7040c2dc0691ae1bf6fe82b8c88a85185fac635993687c210360fe2daa8661a3d25d0df79875d70b1c3d443ade731caafda7488cb68b4071b0ac635a93687c210250e41a6a4abd7b0b3a49eaec24a6fafa99e5aa7b1e3a5aabe60664276df3d937ac635b93687c2103045a32125930ca103c7d7c79b6f379754796cd4ea7fb0059da926e415e3877d3ac635c93687c210344943249d7ca9b47316fef0c2a413dda3a75416a449a29f310ab7fc9d052ed70ac635d93687c2103c62967320b63df5136ff1ef4c7959ef5917ee5a44f75c83e870bc488143d4d69ac635e93687c21020429f776e15770e4dc52bd6f72e6ed6908d51de1c4a64878433c4e3860a48dc4ac635f93680150a000000000");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(pw_separate_sign2)
|
||||
{
|
||||
// key set for the old Primary Wallet
|
||||
std::vector<fc::ecc::private_key> priv_old;
|
||||
for(unsigned i = 0; i < 15; ++i)
|
||||
{
|
||||
const char* seed = reinterpret_cast<const char*>(&i);
|
||||
fc::sha256 h = fc::sha256::hash(seed, sizeof(i));
|
||||
priv_old.push_back(fc::ecc::private_key::generate_from_seed(h));
|
||||
}
|
||||
// print old keys
|
||||
for(auto key: priv_old)
|
||||
{
|
||||
fc::sha256 secret = key.get_secret();
|
||||
bytes data({239});
|
||||
data.insert(data.end(), secret.data(), secret.data() + secret.data_size());
|
||||
fc::sha256 cs = fc::sha256::hash(fc::sha256::hash((char*)&data[0], data.size()));
|
||||
data.insert(data.end(), cs.data(), cs.data() + 4);
|
||||
}
|
||||
std::vector<fc::ecc::public_key> pub_old;
|
||||
for(auto& key: priv_old)
|
||||
pub_old.push_back(key.get_public_key());
|
||||
// old key weights
|
||||
std::vector<std::pair<fc::ecc::public_key, int> > weights_old;
|
||||
for(unsigned i = 0; i < 15; ++i)
|
||||
weights_old.push_back(std::make_pair(pub_old[i], i + 1));
|
||||
// redeem script for old PW
|
||||
bytes redeem_old =generate_redeem_script(weights_old);
|
||||
|
||||
// Old PW address
|
||||
std::string old_pw = p2wsh_address_from_redeem_script(redeem_old, bitcoin_network::testnet);
|
||||
// This address was filled with testnet transaction 508a36d65de66db7c57ee6c5502068ebdcba996ca2df23ef42d901ec8fba1766
|
||||
BOOST_REQUIRE(old_pw == "tb1qfhstznulf5cmjzahlkmnuuvs0tkjtwjlme3ugz8jzfjanf8h5rwsp45t7e");
|
||||
|
||||
bytes scriptPubKey = lock_script_for_redeem_script(redeem_old);
|
||||
|
||||
// key set for the new Primary Wallet
|
||||
std::vector<fc::ecc::private_key> priv_new;
|
||||
for(unsigned i = 16; i < 31; ++i)
|
||||
{
|
||||
const char* seed = reinterpret_cast<const char*>(&i);
|
||||
fc::sha256 h = fc::sha256::hash(seed, sizeof(i));
|
||||
priv_new.push_back(fc::ecc::private_key::generate_from_seed(h));
|
||||
}
|
||||
std::vector<fc::ecc::public_key> pub_new;
|
||||
for(auto& key: priv_new)
|
||||
pub_new.push_back(key.get_public_key());
|
||||
// new key weights
|
||||
std::vector<std::pair<fc::ecc::public_key, int> > weights_new;
|
||||
for(unsigned i = 0; i < 15; ++i)
|
||||
weights_new.push_back(std::make_pair(pub_new[i], 16 - i));
|
||||
// redeem script for new PW
|
||||
bytes redeem_new =generate_redeem_script(weights_new);
|
||||
// New PW address
|
||||
std::string new_pw = p2wsh_address_from_redeem_script(redeem_new, bitcoin_network::testnet);
|
||||
BOOST_REQUIRE(new_pw == "tb1qzegrz8r8z8ddfkql8595d90czng6eyjmx4ur73ls4pq57jg99qhsh9fd2y");
|
||||
|
||||
// try to move funds from old wallet to new one
|
||||
|
||||
// get unspent outputs for old wallet with list_uspent (address should be
|
||||
// added to wallet with import_address before). It should return
|
||||
// 1 UTXO: [508a36d65de66db7c57ee6c5502068ebdcba996ca2df23ef42d901ec8fba1766:0]
|
||||
// with 20000 satoshis
|
||||
// So, we creating a raw transaction with 1 input and one output that gets
|
||||
// 20000 - fee satoshis with createrawtransaction call (bitcoin_rpc_client::prepare_tx)
|
||||
// Here we just serialize the transaction without scriptSig in inputs then sign it.
|
||||
btc_outpoint outpoint;
|
||||
outpoint.hash = fc::uint256("508a36d65de66db7c57ee6c5502068ebdcba996ca2df23ef42d901ec8fba1766");
|
||||
// reverse hash due to the different from_hex algo
|
||||
std::reverse(outpoint.hash.data(), outpoint.hash.data() + outpoint.hash.data_size());
|
||||
outpoint.n = 0;
|
||||
btc_in input;
|
||||
input.prevout = outpoint;
|
||||
input.nSequence = 0xffffffff;
|
||||
btc_out output;
|
||||
output.nValue = 19000;
|
||||
output.scriptPubKey = lock_script_for_redeem_script(redeem_new);
|
||||
btc_tx tx;
|
||||
tx.nVersion = 2;
|
||||
tx.nLockTime = 0;
|
||||
tx.hasWitness = false;
|
||||
tx.vin.push_back(input);
|
||||
tx.vout.push_back(output);
|
||||
bytes unsigned_tx;
|
||||
tx.to_bytes(unsigned_tx);
|
||||
std::vector<uint64_t> in_amounts({20000});
|
||||
|
||||
// gather all signatures from all SONs separatelly
|
||||
std::vector<std::vector<bytes> > signature_set;
|
||||
for(auto key: priv_old)
|
||||
{
|
||||
std::vector<bytes> signatures = signatures_for_raw_transaction(unsigned_tx, in_amounts, redeem_old, key);
|
||||
signature_set.push_back(signatures);
|
||||
}
|
||||
|
||||
// create signed tx with all signatures
|
||||
bytes signed_tx = add_signatures_to_unsigned_tx(unsigned_tx, signature_set, redeem_old);
|
||||
|
||||
// now this is real testnet tx with id 1734a2f6192c3953c90f9fd7f69eba16eeb0922207f81f3af32d6534a6f8e850
|
||||
BOOST_CHECK(fc::to_hex((char*)&signed_tx[0], signed_tx.size()) == "020000000001016617ba8fec01d942ef23dfa26c99badceb682050c5e67ec5b76de65dd6368a500000000000ffffffff01384a0000000000002200201650311c6711dad4d81f3d0b4695f814d1ac925b35783f47f0a8414f4905282f10473044022028cf6df7ed5c2761d7aa2af20717c8b5ace168a7800d6a566f2c1ae28160cae502205e01a3d91f5b9870577e36fbc26ce0cecc3e628cc376c7016364ec3f370703140147304402205c9a88cbe41eb9c6a16ba1d747456222cbe951d04739d21309ef0c0cf00727f202202d06db830ee5823882c7b6f82b708111a8f37741878896cd3558fb91efe8076401473044022009c3184fc0385eb7ed8dc0374791cbdace0eff0dc27dd80ac68f8cb81110f700022042267e8a8788c314347234ea10db6c1ec21a2d423b784cbfbaadf3b2393c44630147304402202363ce306570dc0bbf6d18d41b67c6488a014a91d8e24c03670b4f65523aca12022029d04c114b8e93d982cadee89d80bb25c5c8bc437d6cd2bfce8e0d83a08d14410148304502210087b4742e5cf9c77ca9f99928e7c7087e7d786e09216485628509e4e0b2f29d7e02207daf2eaee9fe8bf117074be137b7ae4b8503a4f6d263424e8e6a16405d5b723c0147304402204f1c3ed8cf595bfaf79d90f4c55c04c17bb6d446e3b9beca7ee6ee7895c6b752022022ac032f219a81b2845d0a1abfb904e40036a3ad332e7dfada6fda21ef7080b501483045022100d020eca4ba1aa77de9caf98f3a29f74f55268276860b9fa35fa16cfc00219dd8022028237de6ad063116cf8182d2dd45a09cb90c2ec8104d793eb3635a1290027cd6014730440220322193b0feba7356651465b86463c7619cd3d96729df6242e9571c74ff1c3c2902206e1de8e77b71c7b6031a934b52321134b6a8d138e2124e90f6345decbd543efb01483045022100d70ade49b3f17812785a41711e107b27c3d4981f8e12253629c07ec46ee511af02203e1ea9059ed9165eeff827002c7399a30c478a9b6f2b958621bfbc6713ab4dd30147304402206f7f10d9993c7019360276bbe790ab587adadeab08088593a9a0c56524aca4df02207c147fe2e51484801a4e059e611e7514729d685a5df892dcf02ba59d455e678101483045022100d5071b8039364bfaa53ef5e22206f773539b082f28bd1fbaaea995fa28aae0f5022056edf7a7bdd8a9a54273a667be5bcd11191fc871798fb44f6e1e35c95d86a81201483045022100a39f8ffbcd9c3f0591fc731a9856c8e024041017cba20c9935f13e4abcf9e9dc0220786823b8cd55664ff9ad6277899aacfd56fa8e48c38881482418b7d50ca27211014730440220361d3b87fcc2b1c12a9e7c684c78192ccb7fe51b90c281b7058384b0b036927a0220434c9b403ee3802b4e5b53feb9bb37d2a9d8746c3688da993549dd9d9954c6800147304402206dc4c3a4407fe9cbffb724928aa0597148c14a20d0d7fbb36ad5d3e2a3abf85e022039ef7baebbf08494495a038b009c6d4ff4b91c38db840673b87f6c27c3b53e7e01483045022100cadac495ea78d0ce9678a4334b8c43f7fafeea5a59413cc2a0144addb63485f9022078ca133e020e3afd0e79936337afefc21d84d3839f5a225a0f3d3eebc15f959901fd5c02007c21030e88484f2bb5dcfc0b326e9eb565c27c8291efb064d060d226916857a2676e62ac635193687c2102151ad794a3aeb3cf9c190120da3d13d36cd8bdf21ca1ccb15debd61c601314b0ac635293687c2103b45a5955ea7847d121225c752edaeb4a5d731a056a951a876caaf6d1f69adb7dac635393687c2102def03a6ffade4ffb0017c8d93859a247badd60e2d76d00e2a3713f6621932ec1ac635493687c21035f17aa7d58b8c3ee0d87240fded52b27f3f12768a0a54ba2595e0a929dd87155ac635593687c2103c8582ac6b0bd20cc1b02c6a86bad2ea10cadb758fedd754ba0d97be85b63b5a7ac635693687c21028148a1f9669fc4471e76f7a371d7cc0563b26e0821d9633fd37649744ff54edaac635793687c2102f0313701b0035f0365a59ce1a3d7ae7045e1f2fb25c4656c08071e5baf51483dac635893687c21024c4c25d08173b3c4d4e1375f8107fd7040c2dc0691ae1bf6fe82b8c88a85185fac635993687c210360fe2daa8661a3d25d0df79875d70b1c3d443ade731caafda7488cb68b4071b0ac635a93687c210250e41a6a4abd7b0b3a49eaec24a6fafa99e5aa7b1e3a5aabe60664276df3d937ac635b93687c2103045a32125930ca103c7d7c79b6f379754796cd4ea7fb0059da926e415e3877d3ac635c93687c210344943249d7ca9b47316fef0c2a413dda3a75416a449a29f310ab7fc9d052ed70ac635d93687c2103c62967320b63df5136ff1ef4c7959ef5917ee5a44f75c83e870bc488143d4d69ac635e93687c21020429f776e15770e4dc52bd6f72e6ed6908d51de1c4a64878433c4e3860a48dc4ac635f93680150a000000000");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(pw_partially_sign)
|
||||
{
|
||||
// key set for the old Primary Wallet
|
||||
std::vector<fc::ecc::private_key> priv_old;
|
||||
for(unsigned i = 0; i < 15; ++i)
|
||||
{
|
||||
const char* seed = reinterpret_cast<const char*>(&i);
|
||||
fc::sha256 h = fc::sha256::hash(seed, sizeof(i));
|
||||
priv_old.push_back(fc::ecc::private_key::generate_from_seed(h));
|
||||
}
|
||||
// print old keys
|
||||
for(auto key: priv_old)
|
||||
{
|
||||
fc::sha256 secret = key.get_secret();
|
||||
bytes data({239});
|
||||
data.insert(data.end(), secret.data(), secret.data() + secret.data_size());
|
||||
fc::sha256 cs = fc::sha256::hash(fc::sha256::hash((char*)&data[0], data.size()));
|
||||
data.insert(data.end(), cs.data(), cs.data() + 4);
|
||||
}
|
||||
std::vector<fc::ecc::public_key> pub_old;
|
||||
for(auto& key: priv_old)
|
||||
pub_old.push_back(key.get_public_key());
|
||||
// old key weights
|
||||
std::vector<std::pair<fc::ecc::public_key, int> > weights_old;
|
||||
for(unsigned i = 0; i < 15; ++i)
|
||||
weights_old.push_back(std::make_pair(pub_old[i], i + 1));
|
||||
// redeem script for old PW
|
||||
bytes redeem_old =generate_redeem_script(weights_old);
|
||||
|
||||
// Old PW address
|
||||
std::string old_pw = p2wsh_address_from_redeem_script(redeem_old, bitcoin_network::testnet);
|
||||
// This address was filled with testnet transaction 508a36d65de66db7c57ee6c5502068ebdcba996ca2df23ef42d901ec8fba1766
|
||||
BOOST_REQUIRE(old_pw == "tb1qfhstznulf5cmjzahlkmnuuvs0tkjtwjlme3ugz8jzfjanf8h5rwsp45t7e");
|
||||
|
||||
bytes scriptPubKey = lock_script_for_redeem_script(redeem_old);
|
||||
|
||||
// key set for the new Primary Wallet
|
||||
std::vector<fc::ecc::private_key> priv_new;
|
||||
for(unsigned i = 16; i < 31; ++i)
|
||||
{
|
||||
const char* seed = reinterpret_cast<const char*>(&i);
|
||||
fc::sha256 h = fc::sha256::hash(seed, sizeof(i));
|
||||
priv_new.push_back(fc::ecc::private_key::generate_from_seed(h));
|
||||
}
|
||||
std::vector<fc::ecc::public_key> pub_new;
|
||||
for(auto& key: priv_new)
|
||||
pub_new.push_back(key.get_public_key());
|
||||
// new key weights
|
||||
std::vector<std::pair<fc::ecc::public_key, int> > weights_new;
|
||||
for(unsigned i = 0; i < 15; ++i)
|
||||
weights_new.push_back(std::make_pair(pub_new[i], 16 - i));
|
||||
// redeem script for new PW
|
||||
bytes redeem_new =generate_redeem_script(weights_new);
|
||||
// New PW address
|
||||
std::string new_pw = p2wsh_address_from_redeem_script(redeem_new, bitcoin_network::testnet);
|
||||
BOOST_REQUIRE(new_pw == "tb1qzegrz8r8z8ddfkql8595d90czng6eyjmx4ur73ls4pq57jg99qhsh9fd2y");
|
||||
|
||||
// try to move funds from old wallet to new one
|
||||
|
||||
// Spent 1 UTXO: [7007b77fcd5fe097d02679252aa112900d08ab20c06052f4148265b21b1f9fbf:0]
|
||||
// with 29999 satoshis
|
||||
// So, we creating a raw transaction with 1 input and one output that gets
|
||||
// 29999 - fee satoshis with createrawtransaction call (bitcoin_rpc_client::prepare_tx)
|
||||
btc_outpoint outpoint;
|
||||
outpoint.hash = fc::uint256("7007b77fcd5fe097d02679252aa112900d08ab20c06052f4148265b21b1f9fbf");
|
||||
// reverse hash due to the different from_hex algo
|
||||
std::reverse(outpoint.hash.data(), outpoint.hash.data() + outpoint.hash.data_size());
|
||||
outpoint.n = 0;
|
||||
btc_in input;
|
||||
input.prevout = outpoint;
|
||||
input.nSequence = 0xffffffff;
|
||||
btc_out output;
|
||||
output.nValue = 29000;
|
||||
output.scriptPubKey = lock_script_for_redeem_script(redeem_new);
|
||||
btc_tx tx;
|
||||
tx.nVersion = 2;
|
||||
tx.nLockTime = 0;
|
||||
tx.hasWitness = false;
|
||||
tx.vin.push_back(input);
|
||||
tx.vout.push_back(output);
|
||||
bytes unsigned_tx;
|
||||
tx.to_bytes(unsigned_tx);
|
||||
std::vector<uint64_t> in_amounts({29999});
|
||||
|
||||
// prepare tx with dummy signs
|
||||
bytes partially_signed_tx = add_dummy_signatures_for_pw_transfer(unsigned_tx, redeem_old, 15);
|
||||
|
||||
// sign with every old key one by one except the first one
|
||||
for(unsigned idx = 1; idx < 15; idx++)
|
||||
partially_signed_tx = partially_sign_pw_transfer_transaction(partially_signed_tx, in_amounts, priv_old[idx], idx);
|
||||
|
||||
// now this is real testnet tx with id e86455c40da6993b6fed70daea2046287b206ab5c16e1ab58c4dfb4a7d6efb84
|
||||
BOOST_CHECK(fc::to_hex((char*)&partially_signed_tx[0], partially_signed_tx.size()) == "02000000000101bf9f1f1bb2658214f45260c020ab080d9012a12a257926d097e05fcd7fb707700000000000ffffffff0148710000000000002200201650311c6711dad4d81f3d0b4695f814d1ac925b35783f47f0a8414f4905282f10483045022100c4c567419754c5c1768e959a35633012e8d22ccc90d7cd1b88d6d430a513fbbd0220729c2a3520d0cae7dd6dcd928624ffa3e0b6ce0c4f5c340653a6c18549182588014830450221008c868ea2cdf5b23bdf9e6c7d7c283b8424aeb4aec43621424baef1ee77dd399a02205431f608006f0f0dcd392fab4f25328808b45d4a73852a197e947b289faefece01483045022100aecac85bbb81bc0a4e127c15090c5ab82a62b9e27a9a6eb8eddf8de294aa9d920220482f2ba8d7b62e9f3f7a68b0ef3236bc56e44481d3eb59f62d1daf4b191dc86001483045022100eb27943f8b511a36b1a843f9b3ddf6930aece5a3c0be697dbafc921924fc049c022065ba3e1e4ad57f56337143136c5d3ee3f56dd60f36e798f07b5646e29343d7320147304402206e24158484ebb2cd14b9c410ecd04841d806d8464ce9a827533484c8ad8d921b022021baec9cd0ad46e7b19c8de7df286093b835df5c6243e90b14f5748dc1b7c13901473044022067bfaf0e39d72e49a081d4e43828746ab7524c4764e445173dd96cc7e6187d46022063ef107375cc45d1c26b1e1c87b97694f71645187ad871db9c05b8e981a0da8601483045022100da0162de3e4a5268b616b9d01a1a4f64b0c371c6b44fb1f740a264455f2bc20d02203a0b45a98a341722ad65ae4ad68538d617b1cfbb229751f875615317eaf15dd4014830450221008220c4f97585e67966d4435ad8497eb89945f13dd8ff24048b830582349041a002204cb03f7271895637a31ce6479d15672c2d70528148e3cd6196e6f722117745c50147304402203e83ab4b15bb0680f82779335acf9a3ce45316150a4538d5e3d25cb863fcec5702204b3913874077ed2cae4e10f8786053b6f157973a54d156d5863f13accca595f50147304402201420d2a2830278ffff5842ecb7173a23642f179435443e780b3d1fe04be5c32e02203818202390e0e63b4309b89f9cce08c0f4dfa539c2ed59b05e24325671e2747c0147304402205624ca9d47ae04afd8fff705706d6853f8c679abb385f19e01c36f9380a0bad602203dc817fc55497e4c1759a3dbfff1662faca593a9f10d3a9b3e24d5ee3165d4400147304402203a959f9a34587c56b86826e6ab65644ab19cbd09ca078459eb59956b02bc753002206df5ded568d0e3e3645f8cb8ca02874dd1bfa82933eb5e01ff2e5a773633e51601483045022100a84ed5be60b9e095d40f3f6bd698425cb9c4d8f95e8b43ca6c5120a6c599e9eb022064c703952d18d753f9198d78188a26888e6b06c832d93f8075311d57a13240160147304402202e71d3af33a18397b90072098881fdbdb8d6e4ffa34d21141212dd815c97d00f02207195f1c06a8f44ca72af15fdaba89b07cf6daef9be981c432b9f5c10f1e374200100fd5c02007c21030e88484f2bb5dcfc0b326e9eb565c27c8291efb064d060d226916857a2676e62ac635193687c2102151ad794a3aeb3cf9c190120da3d13d36cd8bdf21ca1ccb15debd61c601314b0ac635293687c2103b45a5955ea7847d121225c752edaeb4a5d731a056a951a876caaf6d1f69adb7dac635393687c2102def03a6ffade4ffb0017c8d93859a247badd60e2d76d00e2a3713f6621932ec1ac635493687c21035f17aa7d58b8c3ee0d87240fded52b27f3f12768a0a54ba2595e0a929dd87155ac635593687c2103c8582ac6b0bd20cc1b02c6a86bad2ea10cadb758fedd754ba0d97be85b63b5a7ac635693687c21028148a1f9669fc4471e76f7a371d7cc0563b26e0821d9633fd37649744ff54edaac635793687c2102f0313701b0035f0365a59ce1a3d7ae7045e1f2fb25c4656c08071e5baf51483dac635893687c21024c4c25d08173b3c4d4e1375f8107fd7040c2dc0691ae1bf6fe82b8c88a85185fac635993687c210360fe2daa8661a3d25d0df79875d70b1c3d443ade731caafda7488cb68b4071b0ac635a93687c210250e41a6a4abd7b0b3a49eaec24a6fafa99e5aa7b1e3a5aabe60664276df3d937ac635b93687c2103045a32125930ca103c7d7c79b6f379754796cd4ea7fb0059da926e415e3877d3ac635c93687c210344943249d7ca9b47316fef0c2a413dda3a75416a449a29f310ab7fc9d052ed70ac635d93687c2103c62967320b63df5136ff1ef4c7959ef5917ee5a44f75c83e870bc488143d4d69ac635e93687c21020429f776e15770e4dc52bd6f72e6ed6908d51de1c4a64878433c4e3860a48dc4ac635f93680150a000000000");
|
||||
}
|
||||
|
|
@ -407,143 +407,143 @@ BOOST_AUTO_TEST_CASE(get_account_history_additional) {
|
|||
} FC_LOG_AND_RETHROW()
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(track_account) {
|
||||
try {
|
||||
graphene::app::history_api hist_api(app);
|
||||
//BOOST_AUTO_TEST_CASE(track_account) {
|
||||
// try {
|
||||
// graphene::app::history_api hist_api(app);
|
||||
//
|
||||
// // account_id_type() is not tracked
|
||||
//
|
||||
// // account_id_type() creates alice(not tracked account)
|
||||
// const account_object& alice = create_account("alice");
|
||||
// auto alice_id = alice.id;
|
||||
//
|
||||
// //account_id_type() creates some ops
|
||||
// create_bitasset("CNY", account_id_type());
|
||||
// create_bitasset("USD", account_id_type());
|
||||
//
|
||||
// // account_id_type() creates dan(account tracked)
|
||||
// const account_object& dan = create_account("dan");
|
||||
// auto dan_id = dan.id;
|
||||
//
|
||||
// // dan makes 1 op
|
||||
// create_bitasset("EUR", dan_id);
|
||||
//
|
||||
// generate_block( ~database::skip_fork_db );
|
||||
//
|
||||
// // anything against account_id_type() should be {}
|
||||
// vector<operation_history_object> histories =
|
||||
// hist_api.get_account_history(account_id_type(), operation_history_id_type(0), 10, operation_history_id_type(0));
|
||||
// BOOST_CHECK_EQUAL(histories.size(), 0u);
|
||||
// histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 10, operation_history_id_type(0));
|
||||
// BOOST_CHECK_EQUAL(histories.size(), 0u);
|
||||
// histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 1, operation_history_id_type(2));
|
||||
// BOOST_CHECK_EQUAL(histories.size(), 0u);
|
||||
//
|
||||
// // anything against alice should be {}
|
||||
// histories = hist_api.get_account_history(alice_id, operation_history_id_type(0), 10, operation_history_id_type(0));
|
||||
// BOOST_CHECK_EQUAL(histories.size(), 0u);
|
||||
// histories = hist_api.get_account_history(alice_id, operation_history_id_type(1), 10, operation_history_id_type(0));
|
||||
// BOOST_CHECK_EQUAL(histories.size(), 0u);
|
||||
// histories = hist_api.get_account_history(alice_id, operation_history_id_type(1), 1, operation_history_id_type(2));
|
||||
// BOOST_CHECK_EQUAL(histories.size(), 0u);
|
||||
//
|
||||
// // dan should have history
|
||||
// histories = hist_api.get_account_history(dan_id, operation_history_id_type(0), 10, operation_history_id_type(0));
|
||||
// BOOST_CHECK_EQUAL(histories.size(), 2u);
|
||||
// BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u);
|
||||
// BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);
|
||||
//
|
||||
// // create more ops, starting with an untracked account
|
||||
// create_bitasset( "BTC", account_id_type() );
|
||||
// create_bitasset( "GBP", dan_id );
|
||||
//
|
||||
// generate_block( ~database::skip_fork_db );
|
||||
//
|
||||
// histories = hist_api.get_account_history(dan_id, operation_history_id_type(0), 10, operation_history_id_type(0));
|
||||
// BOOST_CHECK_EQUAL(histories.size(), 3u);
|
||||
// BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u);
|
||||
// BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u);
|
||||
// BOOST_CHECK_EQUAL(histories[2].id.instance(), 3u);
|
||||
//
|
||||
// db.pop_block();
|
||||
//
|
||||
// // Try again, should result in same object IDs
|
||||
// create_bitasset( "BTC", account_id_type() );
|
||||
// create_bitasset( "GBP", dan_id );
|
||||
//
|
||||
// generate_block();
|
||||
//
|
||||
// histories = hist_api.get_account_history(dan_id, operation_history_id_type(0), 10, operation_history_id_type(0));
|
||||
// BOOST_CHECK_EQUAL(histories.size(), 3u);
|
||||
// BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u);
|
||||
// BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u);
|
||||
// BOOST_CHECK_EQUAL(histories[2].id.instance(), 3u);
|
||||
// } catch (fc::exception &e) {
|
||||
// edump((e.to_detail_string()));
|
||||
// throw;
|
||||
// }
|
||||
//}
|
||||
|
||||
// account_id_type() is not tracked
|
||||
|
||||
// account_id_type() creates alice(not tracked account)
|
||||
const account_object& alice = create_account("alice");
|
||||
auto alice_id = alice.id;
|
||||
|
||||
//account_id_type() creates some ops
|
||||
create_bitasset("CNY", account_id_type());
|
||||
create_bitasset("USD", account_id_type());
|
||||
|
||||
// account_id_type() creates dan(account tracked)
|
||||
const account_object& dan = create_account("dan");
|
||||
auto dan_id = dan.id;
|
||||
|
||||
// dan makes 1 op
|
||||
create_bitasset("EUR", dan_id);
|
||||
|
||||
generate_block( ~database::skip_fork_db );
|
||||
|
||||
// anything against account_id_type() should be {}
|
||||
vector<operation_history_object> histories =
|
||||
hist_api.get_account_history(account_id_type(), operation_history_id_type(0), 10, operation_history_id_type(0));
|
||||
BOOST_CHECK_EQUAL(histories.size(), 0u);
|
||||
histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 10, operation_history_id_type(0));
|
||||
BOOST_CHECK_EQUAL(histories.size(), 0u);
|
||||
histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 1, operation_history_id_type(2));
|
||||
BOOST_CHECK_EQUAL(histories.size(), 0u);
|
||||
|
||||
// anything against alice should be {}
|
||||
histories = hist_api.get_account_history(alice_id, operation_history_id_type(0), 10, operation_history_id_type(0));
|
||||
BOOST_CHECK_EQUAL(histories.size(), 0u);
|
||||
histories = hist_api.get_account_history(alice_id, operation_history_id_type(1), 10, operation_history_id_type(0));
|
||||
BOOST_CHECK_EQUAL(histories.size(), 0u);
|
||||
histories = hist_api.get_account_history(alice_id, operation_history_id_type(1), 1, operation_history_id_type(2));
|
||||
BOOST_CHECK_EQUAL(histories.size(), 0u);
|
||||
|
||||
// dan should have history
|
||||
histories = hist_api.get_account_history(dan_id, operation_history_id_type(0), 10, operation_history_id_type(0));
|
||||
BOOST_CHECK_EQUAL(histories.size(), 2u);
|
||||
BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u);
|
||||
BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);
|
||||
|
||||
// create more ops, starting with an untracked account
|
||||
create_bitasset( "BTC", account_id_type() );
|
||||
create_bitasset( "GBP", dan_id );
|
||||
|
||||
generate_block( ~database::skip_fork_db );
|
||||
|
||||
histories = hist_api.get_account_history(dan_id, operation_history_id_type(0), 10, operation_history_id_type(0));
|
||||
BOOST_CHECK_EQUAL(histories.size(), 3u);
|
||||
BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u);
|
||||
BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u);
|
||||
BOOST_CHECK_EQUAL(histories[2].id.instance(), 3u);
|
||||
|
||||
db.pop_block();
|
||||
|
||||
// Try again, should result in same object IDs
|
||||
create_bitasset( "BTC", account_id_type() );
|
||||
create_bitasset( "GBP", dan_id );
|
||||
|
||||
generate_block();
|
||||
|
||||
histories = hist_api.get_account_history(dan_id, operation_history_id_type(0), 10, operation_history_id_type(0));
|
||||
BOOST_CHECK_EQUAL(histories.size(), 3u);
|
||||
BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u);
|
||||
BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u);
|
||||
BOOST_CHECK_EQUAL(histories[2].id.instance(), 3u);
|
||||
} catch (fc::exception &e) {
|
||||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(track_account2) {
|
||||
try {
|
||||
graphene::app::history_api hist_api(app);
|
||||
|
||||
// account_id_type() is tracked
|
||||
|
||||
// account_id_type() creates alice(tracked account)
|
||||
const account_object& alice = create_account("alice");
|
||||
auto alice_id = alice.id;
|
||||
|
||||
//account_id_type() creates some ops
|
||||
create_bitasset("CNY", account_id_type());
|
||||
create_bitasset("USD", account_id_type());
|
||||
|
||||
// alice makes 1 op
|
||||
create_bitasset("EUR", alice_id);
|
||||
|
||||
// account_id_type() creates dan(account not tracked)
|
||||
const account_object& dan = create_account("dan");
|
||||
auto dan_id = dan.id;
|
||||
|
||||
generate_block();
|
||||
|
||||
// all account_id_type() should have 4 ops {4,2,1,0}
|
||||
vector<operation_history_object> histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(0), 10, operation_history_id_type(0));
|
||||
BOOST_CHECK_EQUAL(histories.size(), 4u);
|
||||
BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u);
|
||||
BOOST_CHECK_EQUAL(histories[1].id.instance(), 2u);
|
||||
BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u);
|
||||
BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u);
|
||||
|
||||
// all alice account should have 2 ops {3, 0}
|
||||
histories = hist_api.get_account_history(alice_id, operation_history_id_type(0), 10, operation_history_id_type(0));
|
||||
BOOST_CHECK_EQUAL(histories.size(), 2u);
|
||||
BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u);
|
||||
BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u);
|
||||
|
||||
// alice first op should be {0}
|
||||
histories = hist_api.get_account_history(alice_id, operation_history_id_type(0), 1, operation_history_id_type(1));
|
||||
BOOST_CHECK_EQUAL(histories.size(), 1u);
|
||||
BOOST_CHECK_EQUAL(histories[0].id.instance(), 0u);
|
||||
|
||||
// alice second op should be {3}
|
||||
histories = hist_api.get_account_history(alice_id, operation_history_id_type(1), 1, operation_history_id_type(0));
|
||||
BOOST_CHECK_EQUAL(histories.size(), 1u);
|
||||
BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u);
|
||||
|
||||
// anything against dan should be {}
|
||||
histories = hist_api.get_account_history(dan_id, operation_history_id_type(0), 10, operation_history_id_type(0));
|
||||
BOOST_CHECK_EQUAL(histories.size(), 0u);
|
||||
histories = hist_api.get_account_history(dan_id, operation_history_id_type(1), 10, operation_history_id_type(0));
|
||||
BOOST_CHECK_EQUAL(histories.size(), 0u);
|
||||
histories = hist_api.get_account_history(dan_id, operation_history_id_type(1), 1, operation_history_id_type(2));
|
||||
BOOST_CHECK_EQUAL(histories.size(), 0u);
|
||||
|
||||
} catch (fc::exception &e) {
|
||||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
//BOOST_AUTO_TEST_CASE(track_account2) {
|
||||
// try {
|
||||
// graphene::app::history_api hist_api(app);
|
||||
//
|
||||
// // account_id_type() is tracked
|
||||
//
|
||||
// // account_id_type() creates alice(tracked account)
|
||||
// const account_object& alice = create_account("alice");
|
||||
// auto alice_id = alice.id;
|
||||
//
|
||||
// //account_id_type() creates some ops
|
||||
// create_bitasset("CNY", account_id_type());
|
||||
// create_bitasset("USD", account_id_type());
|
||||
//
|
||||
// // alice makes 1 op
|
||||
// create_bitasset("EUR", alice_id);
|
||||
//
|
||||
// // account_id_type() creates dan(account not tracked)
|
||||
// const account_object& dan = create_account("dan");
|
||||
// auto dan_id = dan.id;
|
||||
//
|
||||
// generate_block();
|
||||
//
|
||||
// // all account_id_type() should have 4 ops {4,2,1,0}
|
||||
// vector<operation_history_object> histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(0), 10, operation_history_id_type(0));
|
||||
// BOOST_CHECK_EQUAL(histories.size(), 4u);
|
||||
// BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u);
|
||||
// BOOST_CHECK_EQUAL(histories[1].id.instance(), 2u);
|
||||
// BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u);
|
||||
// BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u);
|
||||
//
|
||||
// // all alice account should have 2 ops {3, 0}
|
||||
// histories = hist_api.get_account_history(alice_id, operation_history_id_type(0), 10, operation_history_id_type(0));
|
||||
// BOOST_CHECK_EQUAL(histories.size(), 2u);
|
||||
// BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u);
|
||||
// BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u);
|
||||
//
|
||||
// // alice first op should be {0}
|
||||
// histories = hist_api.get_account_history(alice_id, operation_history_id_type(0), 1, operation_history_id_type(1));
|
||||
// BOOST_CHECK_EQUAL(histories.size(), 1u);
|
||||
// BOOST_CHECK_EQUAL(histories[0].id.instance(), 0u);
|
||||
//
|
||||
// // alice second op should be {3}
|
||||
// histories = hist_api.get_account_history(alice_id, operation_history_id_type(1), 1, operation_history_id_type(0));
|
||||
// BOOST_CHECK_EQUAL(histories.size(), 1u);
|
||||
// BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u);
|
||||
//
|
||||
// // anything against dan should be {}
|
||||
// histories = hist_api.get_account_history(dan_id, operation_history_id_type(0), 10, operation_history_id_type(0));
|
||||
// BOOST_CHECK_EQUAL(histories.size(), 0u);
|
||||
// histories = hist_api.get_account_history(dan_id, operation_history_id_type(1), 10, operation_history_id_type(0));
|
||||
// BOOST_CHECK_EQUAL(histories.size(), 0u);
|
||||
// histories = hist_api.get_account_history(dan_id, operation_history_id_type(1), 1, operation_history_id_type(2));
|
||||
// BOOST_CHECK_EQUAL(histories.size(), 0u);
|
||||
//
|
||||
// } catch (fc::exception &e) {
|
||||
// edump((e.to_detail_string()));
|
||||
// throw;
|
||||
// }
|
||||
//}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(get_account_history_operations) {
|
||||
try {
|
||||
|
|
|
|||
138
tests/tests/sidechain_addresses_test.cpp
Normal file
138
tests/tests/sidechain_addresses_test.cpp
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include "../common/database_fixture.hpp"
|
||||
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
#include <graphene/chain/sidechain_address_object.hpp>
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
|
||||
using namespace graphene::chain;
|
||||
using namespace graphene::chain::test;
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE( sidechain_addresses_tests, database_fixture )
|
||||
|
||||
BOOST_AUTO_TEST_CASE( sidechain_address_add_test ) {
|
||||
|
||||
BOOST_TEST_MESSAGE("sidechain_address_add_test");
|
||||
|
||||
generate_block();
|
||||
set_expiration(db, trx);
|
||||
|
||||
ACTORS((alice));
|
||||
|
||||
generate_block();
|
||||
set_expiration(db, trx);
|
||||
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Send sidechain_address_add_operation");
|
||||
|
||||
sidechain_address_add_operation op;
|
||||
|
||||
op.sidechain_address_account = alice_id;
|
||||
op.sidechain = graphene::peerplays_sidechain::sidechain_type::bitcoin;
|
||||
op.deposit_address = "deposit_address";
|
||||
op.withdraw_address = "withdraw_address";
|
||||
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, alice_private_key);
|
||||
PUSH_TX(db, trx, ~0);
|
||||
}
|
||||
generate_block();
|
||||
|
||||
BOOST_TEST_MESSAGE("Check sidechain_address_add_operation results");
|
||||
|
||||
const auto& idx = db.get_index_type<sidechain_address_index>().indices().get<by_account_and_sidechain>();
|
||||
BOOST_REQUIRE( idx.size() == 1 );
|
||||
auto obj = idx.find( boost::make_tuple( alice_id, graphene::peerplays_sidechain::sidechain_type::bitcoin ) );
|
||||
BOOST_REQUIRE( obj != idx.end() );
|
||||
BOOST_CHECK( obj->sidechain_address_account == alice_id );
|
||||
BOOST_CHECK( obj->sidechain == graphene::peerplays_sidechain::sidechain_type::bitcoin );
|
||||
BOOST_CHECK( obj->deposit_address == "deposit_address" );
|
||||
BOOST_CHECK( obj->withdraw_address == "withdraw_address" );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( sidechain_address_update_test ) {
|
||||
|
||||
BOOST_TEST_MESSAGE("sidechain_address_update_test");
|
||||
|
||||
INVOKE(sidechain_address_add_test);
|
||||
|
||||
GET_ACTOR(alice);
|
||||
|
||||
const auto& idx = db.get_index_type<sidechain_address_index>().indices().get<by_account_and_sidechain>();
|
||||
BOOST_REQUIRE( idx.size() == 1 );
|
||||
auto obj = idx.find( boost::make_tuple( alice_id, graphene::peerplays_sidechain::sidechain_type::bitcoin ) );
|
||||
BOOST_REQUIRE( obj != idx.end() );
|
||||
|
||||
std::string new_deposit_address = "new_deposit_address";
|
||||
std::string new_withdraw_address = "new_withdraw_address";
|
||||
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Send sidechain_address_update_operation");
|
||||
|
||||
sidechain_address_update_operation op;
|
||||
op.sidechain_address_id = sidechain_address_id_type(0);
|
||||
op.sidechain_address_account = obj->sidechain_address_account;
|
||||
op.sidechain = obj->sidechain;
|
||||
op.deposit_address = new_deposit_address;
|
||||
op.withdraw_address = new_withdraw_address;
|
||||
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, alice_private_key);
|
||||
PUSH_TX(db, trx, ~0);
|
||||
}
|
||||
generate_block();
|
||||
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Check sidechain_address_update_operation results");
|
||||
|
||||
const auto& idx = db.get_index_type<sidechain_address_index>().indices().get<by_account_and_sidechain>();
|
||||
BOOST_REQUIRE( idx.size() == 1 );
|
||||
auto obj = idx.find( boost::make_tuple( alice_id, graphene::peerplays_sidechain::sidechain_type::bitcoin ) );
|
||||
BOOST_REQUIRE( obj != idx.end() );
|
||||
BOOST_CHECK( obj->sidechain_address_account == obj->sidechain_address_account );
|
||||
BOOST_CHECK( obj->sidechain == obj->sidechain );
|
||||
BOOST_CHECK( obj->deposit_address == new_deposit_address );
|
||||
BOOST_CHECK( obj->withdraw_address == new_withdraw_address );
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( sidechain_address_delete_test ) {
|
||||
|
||||
BOOST_TEST_MESSAGE("sidechain_address_delete_test");
|
||||
|
||||
INVOKE(sidechain_address_add_test);
|
||||
|
||||
GET_ACTOR(alice);
|
||||
|
||||
const auto& idx = db.get_index_type<sidechain_address_index>().indices().get<by_account_and_sidechain>();
|
||||
BOOST_REQUIRE( idx.size() == 1 );
|
||||
auto obj = idx.find( boost::make_tuple( alice_id, graphene::peerplays_sidechain::sidechain_type::bitcoin ) );
|
||||
BOOST_REQUIRE( obj != idx.end() );
|
||||
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Send sidechain_address_delete_operation");
|
||||
|
||||
sidechain_address_delete_operation op;
|
||||
op.sidechain_address_id = sidechain_address_id_type(0);
|
||||
op.sidechain_address_account = obj->sidechain_address_account;
|
||||
op.sidechain = obj->sidechain;
|
||||
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, alice_private_key);
|
||||
PUSH_TX(db, trx, ~0);
|
||||
}
|
||||
generate_block();
|
||||
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Check sidechain_address_delete_operation results");
|
||||
|
||||
const auto& idx = db.get_index_type<sidechain_address_index>().indices().get<by_account_and_sidechain>();
|
||||
BOOST_REQUIRE( idx.size() == 0 );
|
||||
auto obj = idx.find( boost::make_tuple( alice_id, graphene::peerplays_sidechain::sidechain_type::bitcoin ) );
|
||||
BOOST_REQUIRE( obj == idx.end() );
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
|
|
@ -83,12 +83,16 @@ BOOST_AUTO_TEST_CASE( create_son_test ) {
|
|||
|
||||
// alice became son
|
||||
{
|
||||
flat_map<graphene::peerplays_sidechain::sidechain_type, string> sidechain_public_keys;
|
||||
sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin address";
|
||||
|
||||
son_create_operation op;
|
||||
op.owner_account = alice_id;
|
||||
op.url = test_url;
|
||||
op.deposit = deposit;
|
||||
op.pay_vb = payment;
|
||||
op.signing_key = alice_public_key;
|
||||
op.sidechain_public_keys = sidechain_public_keys;
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, alice_private_key);
|
||||
PUSH_TX(db, trx, ~0);
|
||||
|
|
@ -101,6 +105,7 @@ BOOST_AUTO_TEST_CASE( create_son_test ) {
|
|||
BOOST_REQUIRE( obj != idx.end() );
|
||||
BOOST_CHECK( obj->url == test_url );
|
||||
BOOST_CHECK( obj->signing_key == alice_public_key );
|
||||
BOOST_CHECK( obj->sidechain_public_keys.at(graphene::peerplays_sidechain::sidechain_type::bitcoin) == "bitcoin address" );
|
||||
BOOST_CHECK( obj->deposit.instance == deposit.instance.value );
|
||||
BOOST_CHECK( obj->pay_vb.instance == payment.instance.value );
|
||||
}
|
||||
|
|
@ -113,10 +118,14 @@ BOOST_AUTO_TEST_CASE( update_son_test ) {
|
|||
std::string new_url = "https://anewurl.com";
|
||||
|
||||
{
|
||||
flat_map<graphene::peerplays_sidechain::sidechain_type, string> sidechain_public_keys;
|
||||
sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "new bitcoin address";
|
||||
|
||||
son_update_operation op;
|
||||
op.son_id = son_id_type(0);
|
||||
op.owner_account = alice_id;
|
||||
op.new_url = new_url;
|
||||
op.son_id = son_id_type(0);
|
||||
op.new_sidechain_public_keys = sidechain_public_keys;
|
||||
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, alice_private_key);
|
||||
|
|
@ -129,6 +138,7 @@ BOOST_AUTO_TEST_CASE( update_son_test ) {
|
|||
auto obj = idx.find( alice_id );
|
||||
BOOST_REQUIRE( obj != idx.end() );
|
||||
BOOST_CHECK( obj->url == new_url );
|
||||
BOOST_CHECK( obj->sidechain_public_keys.at(graphene::peerplays_sidechain::sidechain_type::bitcoin) == "new bitcoin address" );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( delete_son_test ) {
|
||||
|
|
@ -174,6 +184,71 @@ catch (fc::exception &e) {
|
|||
throw;
|
||||
} }
|
||||
|
||||
BOOST_AUTO_TEST_CASE( delete_son_test_with_consensus_account ) {
|
||||
try {
|
||||
INVOKE(create_son_test);
|
||||
GET_ACTOR(alice);
|
||||
GET_ACTOR(bob);
|
||||
|
||||
const auto& idx = db.get_index_type<son_index>().indices().get<by_account>();
|
||||
BOOST_REQUIRE( idx.size() == 1 );
|
||||
auto obj = idx.find( alice_id );
|
||||
BOOST_REQUIRE( obj != idx.end() );
|
||||
|
||||
const auto& sidx = db.get_index_type<son_stats_index>().indices().get<by_id>();
|
||||
BOOST_REQUIRE( sidx.size() == 1 );
|
||||
auto son_stats_obj = sidx.find( obj->statistics );
|
||||
BOOST_REQUIRE( son_stats_obj != sidx.end() );
|
||||
|
||||
// Modify SON's status to active
|
||||
db.modify( *obj, [&]( son_object& _s)
|
||||
{
|
||||
_s.status = son_status::in_maintenance;
|
||||
});
|
||||
|
||||
db.modify( *son_stats_obj, [&]( son_statistics_object& _s)
|
||||
{
|
||||
_s.last_down_timestamp = fc::time_point_sec(db.head_block_time() - db.get_global_properties().parameters.son_deregister_time());
|
||||
});
|
||||
|
||||
auto deposit_vesting = db.get<vesting_balance_object>(vesting_balance_id_type(0));
|
||||
auto now = db.head_block_time();
|
||||
BOOST_CHECK_EQUAL(deposit_vesting.is_withdraw_allowed(now, asset(50)), false); // cant withdraw
|
||||
|
||||
{
|
||||
trx.clear();
|
||||
son_delete_operation op;
|
||||
op.owner_account = alice_id;
|
||||
op.son_id = son_id_type(0);
|
||||
op.payer = db.get_global_properties().parameters.son_account();
|
||||
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, bob_private_key);
|
||||
PUSH_TX(db, trx, ~0);
|
||||
}
|
||||
generate_block();
|
||||
|
||||
BOOST_REQUIRE( idx.size() == 0 );
|
||||
|
||||
deposit_vesting = db.get<vesting_balance_object>(vesting_balance_id_type(0));
|
||||
BOOST_CHECK_EQUAL(deposit_vesting.policy.get<linear_vesting_policy>().vesting_cliff_seconds,
|
||||
db.get_global_properties().parameters.son_vesting_period()); // in linear policy
|
||||
|
||||
now = db.head_block_time();
|
||||
BOOST_CHECK_EQUAL(deposit_vesting.is_withdraw_allowed(now, asset(50)), false); // but still cant withdraw
|
||||
|
||||
generate_blocks(now + fc::seconds(db.get_global_properties().parameters.son_vesting_period()));
|
||||
generate_block();
|
||||
|
||||
deposit_vesting = db.get<vesting_balance_object>(vesting_balance_id_type(0));
|
||||
now = db.head_block_time();
|
||||
BOOST_CHECK_EQUAL(deposit_vesting.is_withdraw_allowed(now, asset(50)), true); // after 2 days withdraw is allowed
|
||||
}
|
||||
catch (fc::exception &e) {
|
||||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
} }
|
||||
|
||||
BOOST_AUTO_TEST_CASE( update_delete_not_own ) { // fee payer needs to be the son object owner
|
||||
try {
|
||||
|
||||
|
|
@ -335,7 +410,7 @@ BOOST_AUTO_TEST_CASE( son_pay_test )
|
|||
op.amount = asset(50*GRAPHENE_BLOCKCHAIN_PRECISION);
|
||||
op.balance_type = vesting_balance_type::son;
|
||||
op.policy = dormant_vesting_policy_initializer {};
|
||||
|
||||
|
||||
trx.operations.push_back(op);
|
||||
for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);
|
||||
set_expiration(db, trx);
|
||||
|
|
@ -352,7 +427,7 @@ BOOST_AUTO_TEST_CASE( son_pay_test )
|
|||
op.owner = bob_id;
|
||||
op.amount = asset(1*GRAPHENE_BLOCKCHAIN_PRECISION);
|
||||
op.balance_type = vesting_balance_type::normal;
|
||||
|
||||
|
||||
trx.operations.push_back(op);
|
||||
for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);
|
||||
set_expiration(db, trx);
|
||||
|
|
@ -438,6 +513,9 @@ BOOST_AUTO_TEST_CASE( son_pay_test )
|
|||
// Check if the signed transaction statistics are reset for both SONs
|
||||
BOOST_REQUIRE_EQUAL(son_stats_obj1->txs_signed, 0);
|
||||
BOOST_REQUIRE_EQUAL(son_stats_obj2->txs_signed, 0);
|
||||
|
||||
BOOST_REQUIRE_EQUAL(son_stats_obj1->total_txs_signed, 2);
|
||||
BOOST_REQUIRE_EQUAL(son_stats_obj2->total_txs_signed, 3);
|
||||
// Check that Alice and Bob are paid for signing the transactions in the previous day/cycle
|
||||
BOOST_REQUIRE_EQUAL(db.get_balance(obj1->son_account, asset_id_type()).amount.value, 80+obj1_balance);
|
||||
BOOST_REQUIRE_EQUAL(db.get_balance(obj2->son_account, asset_id_type()).amount.value, 120+obj2_balance);
|
||||
|
|
@ -448,208 +526,273 @@ BOOST_AUTO_TEST_CASE( son_pay_test )
|
|||
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( son_witness_proposal_test )
|
||||
{
|
||||
BOOST_AUTO_TEST_CASE( son_heartbeat_test ) {
|
||||
|
||||
try
|
||||
{
|
||||
const dynamic_global_property_object& dpo = db.get_dynamic_global_properties();
|
||||
generate_blocks(HARDFORK_SON_TIME);
|
||||
generate_block();
|
||||
generate_block();
|
||||
set_expiration(db, trx);
|
||||
INVOKE(create_son_test);
|
||||
GET_ACTOR(alice);
|
||||
|
||||
ACTORS((alice)(bob));
|
||||
|
||||
upgrade_to_lifetime_member(alice);
|
||||
upgrade_to_lifetime_member(bob);
|
||||
|
||||
transfer( committee_account, alice_id, asset( 1000*GRAPHENE_BLOCKCHAIN_PRECISION ) );
|
||||
transfer( committee_account, bob_id, asset( 1000*GRAPHENE_BLOCKCHAIN_PRECISION ) );
|
||||
|
||||
set_expiration(db, trx);
|
||||
generate_block();
|
||||
// Now create SONs
|
||||
std::string test_url1 = "https://create_son_test1";
|
||||
std::string test_url2 = "https://create_son_test2";
|
||||
|
||||
// create deposit vesting
|
||||
vesting_balance_id_type deposit1;
|
||||
{
|
||||
vesting_balance_create_operation op;
|
||||
op.creator = alice_id;
|
||||
op.owner = alice_id;
|
||||
op.amount = asset(50*GRAPHENE_BLOCKCHAIN_PRECISION);
|
||||
op.balance_type = vesting_balance_type::son;
|
||||
op.policy = dormant_vesting_policy_initializer {};
|
||||
|
||||
trx.operations.push_back(op);
|
||||
for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);
|
||||
set_expiration(db, trx);
|
||||
processed_transaction ptx = PUSH_TX(db, trx, ~0);
|
||||
trx.clear();
|
||||
deposit1 = ptx.operation_results[0].get<object_id_type>();
|
||||
}
|
||||
|
||||
// create payment vesting
|
||||
vesting_balance_id_type payment1;
|
||||
{
|
||||
vesting_balance_create_operation op;
|
||||
op.creator = alice_id;
|
||||
op.owner = alice_id;
|
||||
op.amount = asset(1*GRAPHENE_BLOCKCHAIN_PRECISION);
|
||||
op.balance_type = vesting_balance_type::normal;
|
||||
|
||||
trx.operations.push_back(op);
|
||||
for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);
|
||||
set_expiration(db, trx);
|
||||
processed_transaction ptx = PUSH_TX(db, trx, ~0);
|
||||
trx.clear();
|
||||
payment1 = ptx.operation_results[0].get<object_id_type>();
|
||||
}
|
||||
|
||||
// create deposit vesting
|
||||
vesting_balance_id_type deposit2;
|
||||
{
|
||||
vesting_balance_create_operation op;
|
||||
op.creator = bob_id;
|
||||
op.owner = bob_id;
|
||||
op.amount = asset(50*GRAPHENE_BLOCKCHAIN_PRECISION);
|
||||
op.balance_type = vesting_balance_type::son;
|
||||
op.policy = dormant_vesting_policy_initializer {};
|
||||
|
||||
trx.operations.push_back(op);
|
||||
for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);
|
||||
set_expiration(db, trx);
|
||||
processed_transaction ptx = PUSH_TX(db, trx, ~0);
|
||||
trx.clear();
|
||||
deposit2 = ptx.operation_results[0].get<object_id_type>();
|
||||
}
|
||||
|
||||
// create payment vesting
|
||||
vesting_balance_id_type payment2;
|
||||
{
|
||||
vesting_balance_create_operation op;
|
||||
op.creator = bob_id;
|
||||
op.owner = bob_id;
|
||||
op.amount = asset(1*GRAPHENE_BLOCKCHAIN_PRECISION);
|
||||
op.balance_type = vesting_balance_type::normal;
|
||||
|
||||
trx.operations.push_back(op);
|
||||
for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);
|
||||
set_expiration(db, trx);
|
||||
processed_transaction ptx = PUSH_TX(db, trx, ~0);
|
||||
trx.clear();
|
||||
payment2 = ptx.operation_results[0].get<object_id_type>();
|
||||
}
|
||||
|
||||
// alice becomes son
|
||||
{
|
||||
son_create_operation op;
|
||||
// Send Heartbeat for an inactive SON
|
||||
son_heartbeat_operation op;
|
||||
op.owner_account = alice_id;
|
||||
op.url = test_url1;
|
||||
op.deposit = deposit1;
|
||||
op.pay_vb = payment1;
|
||||
op.fee = asset(0);
|
||||
op.signing_key = alice_public_key;
|
||||
op.son_id = son_id_type(0);
|
||||
op.ts = fc::time_point::now();
|
||||
|
||||
trx.operations.push_back(op);
|
||||
for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);
|
||||
set_expiration(db, trx);
|
||||
sign(trx, alice_private_key);
|
||||
PUSH_TX(db, trx, ~0);
|
||||
// Expect an exception
|
||||
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0), fc::exception);
|
||||
trx.clear();
|
||||
}
|
||||
|
||||
// bob becomes son
|
||||
{
|
||||
son_create_operation op;
|
||||
op.owner_account = bob_id;
|
||||
op.url = test_url2;
|
||||
op.deposit = deposit2;
|
||||
op.pay_vb = payment2;
|
||||
op.fee = asset(0);
|
||||
op.signing_key = bob_public_key;
|
||||
// Try to go in maintenance for an inactive SON
|
||||
son_maintenance_operation op;
|
||||
op.owner_account = alice_id;
|
||||
op.son_id = son_id_type(0);
|
||||
op.request_type = son_maintenance_request_type::request_maintenance;
|
||||
|
||||
trx.operations.push_back(op);
|
||||
for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);
|
||||
sign(trx, bob_private_key);
|
||||
PUSH_TX(db, trx, ~0);
|
||||
set_expiration(db, trx);
|
||||
sign(trx, alice_private_key);
|
||||
// Expect an exception
|
||||
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0), fc::exception);
|
||||
trx.clear();
|
||||
}
|
||||
|
||||
generate_block();
|
||||
// Check if SONs are created properly
|
||||
|
||||
const auto& idx = db.get_index_type<son_index>().indices().get<by_account>();
|
||||
BOOST_REQUIRE( idx.size() == 2 );
|
||||
// Alice's SON
|
||||
auto obj1 = idx.find( alice_id );
|
||||
BOOST_REQUIRE( obj1 != idx.end() );
|
||||
BOOST_CHECK( obj1->url == test_url1 );
|
||||
BOOST_CHECK( obj1->signing_key == alice_public_key );
|
||||
BOOST_CHECK( obj1->deposit.instance == deposit1.instance.value );
|
||||
BOOST_CHECK( obj1->pay_vb.instance == payment1.instance.value );
|
||||
// Bob's SON
|
||||
auto obj2 = idx.find( bob_id );
|
||||
BOOST_REQUIRE( obj2 != idx.end() );
|
||||
BOOST_CHECK( obj2->url == test_url2 );
|
||||
BOOST_CHECK( obj2->signing_key == bob_public_key );
|
||||
BOOST_CHECK( obj2->deposit.instance == deposit2.instance.value );
|
||||
BOOST_CHECK( obj2->pay_vb.instance == payment2.instance.value );
|
||||
// Get the statistics object for the SONs
|
||||
BOOST_REQUIRE( idx.size() == 1 );
|
||||
auto obj = idx.find( alice_id );
|
||||
BOOST_REQUIRE( obj != idx.end() );
|
||||
|
||||
const auto& sidx = db.get_index_type<son_stats_index>().indices().get<by_id>();
|
||||
BOOST_REQUIRE( sidx.size() == 2 );
|
||||
auto son_stats_obj1 = sidx.find( obj1->statistics );
|
||||
auto son_stats_obj2 = sidx.find( obj2->statistics );
|
||||
BOOST_REQUIRE( son_stats_obj1 != sidx.end() );
|
||||
BOOST_REQUIRE( son_stats_obj2 != sidx.end() );
|
||||
BOOST_REQUIRE( sidx.size() == 1 );
|
||||
auto son_stats_obj = sidx.find( obj->statistics );
|
||||
BOOST_REQUIRE( son_stats_obj != sidx.end() );
|
||||
|
||||
|
||||
// Modify SON's status to in_maintenance
|
||||
db.modify( *obj1, [&]( son_object& _s)
|
||||
// Modify SON's status to active
|
||||
db.modify( *obj, [&]( son_object& _s)
|
||||
{
|
||||
_s.status = son_status::in_maintenance;
|
||||
_s.status = son_status::active;
|
||||
});
|
||||
|
||||
// Modify the Alice's SON down timestamp to now-12 hours
|
||||
db.modify( *son_stats_obj1, [&]( son_statistics_object& _s)
|
||||
db.modify( *son_stats_obj, [&]( son_statistics_object& _s)
|
||||
{
|
||||
_s.last_down_timestamp = fc::time_point_sec(db.head_block_time() - fc::hours(12));
|
||||
_s.last_down_timestamp = fc::time_point_sec(db.head_block_time());
|
||||
});
|
||||
|
||||
// Modify SON's status to in_maintenance
|
||||
db.modify( *obj2, [&]( son_object& _s)
|
||||
{
|
||||
_s.status = son_status::in_maintenance;
|
||||
});
|
||||
|
||||
// Modify the Bob's SON down timestamp to now-12 hours
|
||||
db.modify( *son_stats_obj2, [&]( son_statistics_object& _s)
|
||||
{
|
||||
_s.last_down_timestamp = fc::time_point_sec(db.head_block_time() - fc::hours(12));
|
||||
});
|
||||
|
||||
const auto& son_proposal_idx = db.get_index_type<son_proposal_index>().indices().get<by_id>();
|
||||
const auto& proposal_idx = db.get_index_type<proposal_index>().indices().get<by_id>();
|
||||
|
||||
BOOST_CHECK( son_proposal_idx.size() == 0 && proposal_idx.size() == 0 );
|
||||
|
||||
generate_block();
|
||||
witness_id_type proposal_initiator = dpo.current_witness;
|
||||
|
||||
BOOST_CHECK( son_proposal_idx.size() == 2 && proposal_idx.size() == 2 );
|
||||
|
||||
for(size_t i = 0 ; i < 3 * db.get_global_properties().active_witnesses.size() ; i++ )
|
||||
{
|
||||
generate_block();
|
||||
if( dpo.current_witness != proposal_initiator)
|
||||
{
|
||||
BOOST_CHECK( son_proposal_idx.size() == 2 && proposal_idx.size() == 2 );
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
BOOST_CHECK( son_proposal_idx.size() == 0 && proposal_idx.size() == 0 );
|
||||
BOOST_REQUIRE( idx.size() == 0 );
|
||||
generate_block();
|
||||
} FC_LOG_AND_RETHROW()
|
||||
// Request SON Maintenance
|
||||
son_maintenance_operation op;
|
||||
op.owner_account = alice_id;
|
||||
op.son_id = son_id_type(0);
|
||||
op.request_type = son_maintenance_request_type::request_maintenance;
|
||||
|
||||
trx.operations.push_back(op);
|
||||
set_expiration(db, trx);
|
||||
sign(trx, alice_private_key);
|
||||
PUSH_TX( db, trx, ~0);
|
||||
generate_block();
|
||||
trx.clear();
|
||||
BOOST_CHECK( obj->status == son_status::request_maintenance);
|
||||
}
|
||||
|
||||
{
|
||||
generate_block();
|
||||
// Cancel SON Maintenance request
|
||||
son_maintenance_operation op;
|
||||
op.owner_account = alice_id;
|
||||
op.son_id = son_id_type(0);
|
||||
op.request_type = son_maintenance_request_type::cancel_request_maintenance;
|
||||
|
||||
trx.operations.push_back(op);
|
||||
set_expiration(db, trx);
|
||||
sign(trx, alice_private_key);
|
||||
PUSH_TX( db, trx, ~0);
|
||||
generate_block();
|
||||
trx.clear();
|
||||
BOOST_CHECK( obj->status == son_status::active);
|
||||
}
|
||||
|
||||
// Modify SON's status to in_maintenance
|
||||
db.modify( *obj, [&]( son_object& _s)
|
||||
{
|
||||
_s.status = son_status::in_maintenance;
|
||||
});
|
||||
|
||||
uint64_t downtime = 0;
|
||||
|
||||
{
|
||||
generate_block();
|
||||
// Send Heartbeat for an in_maintenance SON
|
||||
son_heartbeat_operation op;
|
||||
op.owner_account = alice_id;
|
||||
op.son_id = son_id_type(0);
|
||||
op.ts = (db.head_block_time()+fc::seconds(2*db.block_interval()));
|
||||
|
||||
trx.operations.push_back(op);
|
||||
set_expiration(db, trx);
|
||||
sign(trx, alice_private_key);
|
||||
PUSH_TX( db, trx, ~0);
|
||||
generate_block();
|
||||
trx.clear();
|
||||
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime, op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.sec_since_epoch());
|
||||
downtime += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.sec_since_epoch();
|
||||
BOOST_CHECK( obj->status == son_status::inactive);
|
||||
BOOST_CHECK( son_stats_obj->last_active_timestamp == op.ts);
|
||||
}
|
||||
|
||||
// Modify SON's status to in_maintenance
|
||||
db.modify( *obj, [&]( son_object& _s)
|
||||
{
|
||||
_s.status = son_status::in_maintenance;
|
||||
});
|
||||
|
||||
// SON is selected as one of the active SONs
|
||||
db.modify( db.get_global_properties(), [&]( global_property_object& _gpo )
|
||||
{
|
||||
son_info son_inf;
|
||||
son_inf.son_id = son_id_type(0);
|
||||
_gpo.active_sons.push_back(son_inf);
|
||||
});
|
||||
|
||||
{
|
||||
generate_block();
|
||||
// Send Heartbeat for an in_maintenance SON
|
||||
son_heartbeat_operation op;
|
||||
op.owner_account = alice_id;
|
||||
op.son_id = son_id_type(0);
|
||||
op.ts = (db.head_block_time()+fc::seconds(2*db.block_interval()));
|
||||
|
||||
trx.operations.push_back(op);
|
||||
set_expiration(db, trx);
|
||||
sign(trx, alice_private_key);
|
||||
PUSH_TX( db, trx, ~0);
|
||||
generate_block();
|
||||
trx.clear();
|
||||
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime, downtime + op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.sec_since_epoch());
|
||||
downtime += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.sec_since_epoch();
|
||||
BOOST_CHECK( obj->status == son_status::active);
|
||||
BOOST_CHECK( son_stats_obj->last_active_timestamp == op.ts);
|
||||
}
|
||||
|
||||
{
|
||||
generate_block();
|
||||
// Send Heartbeat for an active SON
|
||||
son_heartbeat_operation op;
|
||||
op.owner_account = alice_id;
|
||||
op.son_id = son_id_type(0);
|
||||
op.ts = (db.head_block_time()+fc::seconds(2*db.block_interval()));
|
||||
|
||||
trx.operations.push_back(op);
|
||||
set_expiration(db, trx);
|
||||
sign(trx, alice_private_key);
|
||||
PUSH_TX( db, trx, ~0);
|
||||
generate_block();
|
||||
trx.clear();
|
||||
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime, downtime);
|
||||
BOOST_CHECK( obj->status == son_status::active);
|
||||
BOOST_CHECK( son_stats_obj->last_active_timestamp == op.ts);
|
||||
}
|
||||
} FC_LOG_AND_RETHROW()
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( son_report_down_test ) {
|
||||
|
||||
try
|
||||
{
|
||||
INVOKE(son_heartbeat_test);
|
||||
GET_ACTOR(alice);
|
||||
GET_ACTOR(bob);
|
||||
|
||||
generate_block();
|
||||
|
||||
const auto& idx = db.get_index_type<son_index>().indices().get<by_account>();
|
||||
BOOST_REQUIRE( idx.size() == 1 );
|
||||
auto obj = idx.find( alice_id );
|
||||
BOOST_REQUIRE( obj != idx.end() );
|
||||
|
||||
const auto& sidx = db.get_index_type<son_stats_index>().indices().get<by_id>();
|
||||
BOOST_REQUIRE( sidx.size() == 1 );
|
||||
auto son_stats_obj = sidx.find( obj->statistics );
|
||||
BOOST_REQUIRE( son_stats_obj != sidx.end() );
|
||||
|
||||
BOOST_CHECK( obj->status == son_status::active);
|
||||
|
||||
{
|
||||
// Check that transaction fails if down_ts < last_active_timestamp
|
||||
generate_block();
|
||||
// Send Report Down Operation for an active status SON
|
||||
son_report_down_operation op;
|
||||
op.payer = db.get_global_properties().parameters.son_account();
|
||||
op.son_id = son_id_type(0);
|
||||
op.down_ts = fc::time_point_sec(son_stats_obj->last_active_timestamp - fc::seconds(1));
|
||||
|
||||
trx.operations.push_back(op);
|
||||
set_expiration(db, trx);
|
||||
sign(trx, bob_private_key);
|
||||
// Expect an exception
|
||||
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0), fc::exception);
|
||||
trx.clear();
|
||||
}
|
||||
|
||||
{
|
||||
// Check that transaction fails if payer is not db.get_global_properties().parameters.son_account().
|
||||
generate_block();
|
||||
// Send Report Down Operation for an active status SON
|
||||
son_report_down_operation op;
|
||||
op.payer = alice_id;
|
||||
op.son_id = son_id_type(0);
|
||||
op.down_ts = son_stats_obj->last_active_timestamp;
|
||||
|
||||
trx.operations.push_back(op);
|
||||
set_expiration(db, trx);
|
||||
sign(trx, alice_private_key);
|
||||
// Expect an exception
|
||||
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0), fc::exception);
|
||||
trx.clear();
|
||||
}
|
||||
|
||||
{
|
||||
// Check that transaction succeeds after getting enough approvals on db.get_global_properties().parameters.son_account().
|
||||
generate_block();
|
||||
// Send Report Down Operation for an active status SON
|
||||
son_report_down_operation op;
|
||||
op.payer = db.get_global_properties().parameters.son_account();
|
||||
op.son_id = son_id_type(0);
|
||||
op.down_ts = son_stats_obj->last_active_timestamp;
|
||||
|
||||
trx.operations.push_back(op);
|
||||
set_expiration(db, trx);
|
||||
sign(trx, bob_private_key);
|
||||
PUSH_TX( db, trx, ~0);
|
||||
generate_block();
|
||||
trx.clear();
|
||||
|
||||
BOOST_CHECK( obj->status == son_status::in_maintenance);
|
||||
BOOST_CHECK( son_stats_obj->last_down_timestamp == op.down_ts);
|
||||
}
|
||||
|
||||
{
|
||||
// Check that transaction fails if report down sent for an in_maintenance SON.
|
||||
generate_block();
|
||||
// Send Report Down Operation for an active status SON
|
||||
son_report_down_operation op;
|
||||
op.payer = db.get_global_properties().parameters.son_account();
|
||||
op.son_id = son_id_type(0);
|
||||
op.down_ts = son_stats_obj->last_active_timestamp;
|
||||
|
||||
trx.operations.push_back(op);
|
||||
set_expiration(db, trx);
|
||||
sign(trx, bob_private_key);
|
||||
// Expect an exception
|
||||
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0), fc::exception);
|
||||
trx.clear();
|
||||
}
|
||||
} FC_LOG_AND_RETHROW()
|
||||
} BOOST_AUTO_TEST_SUITE_END()
|
||||
|
|
|
|||
225
tests/tests/son_wallet_tests.cpp
Normal file
225
tests/tests/son_wallet_tests.cpp
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include "../common/database_fixture.hpp"
|
||||
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
#include <graphene/chain/son_wallet_object.hpp>
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
|
||||
using namespace graphene;
|
||||
using namespace graphene::chain;
|
||||
using namespace graphene::chain::test;
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE( son_wallet_tests, database_fixture )
|
||||
|
||||
BOOST_AUTO_TEST_CASE( son_wallet_recreate_test ) {
|
||||
|
||||
BOOST_TEST_MESSAGE("son_wallet_recreate_test");
|
||||
|
||||
generate_blocks(HARDFORK_SON_TIME);
|
||||
generate_block();
|
||||
set_expiration(db, trx);
|
||||
|
||||
ACTORS((alice)(bob));
|
||||
|
||||
upgrade_to_lifetime_member(alice);
|
||||
upgrade_to_lifetime_member(bob);
|
||||
|
||||
transfer( committee_account, alice_id, asset( 500000*GRAPHENE_BLOCKCHAIN_PRECISION ) );
|
||||
transfer( committee_account, bob_id, asset( 500000*GRAPHENE_BLOCKCHAIN_PRECISION ) );
|
||||
|
||||
generate_block();
|
||||
set_expiration(db, trx);
|
||||
|
||||
std::string test_url = "https://create_son_test";
|
||||
|
||||
// create deposit vesting
|
||||
vesting_balance_id_type deposit_alice;
|
||||
{
|
||||
vesting_balance_create_operation op;
|
||||
op.creator = alice_id;
|
||||
op.owner = alice_id;
|
||||
op.amount = asset(500 * GRAPHENE_BLOCKCHAIN_PRECISION);
|
||||
op.balance_type = vesting_balance_type::son;
|
||||
op.policy = dormant_vesting_policy_initializer {};
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, alice_private_key);
|
||||
processed_transaction ptx = PUSH_TX(db, trx, ~0);
|
||||
trx.clear();
|
||||
deposit_alice = ptx.operation_results[0].get<object_id_type>();
|
||||
}
|
||||
generate_block();
|
||||
set_expiration(db, trx);
|
||||
|
||||
// create payment normal vesting
|
||||
vesting_balance_id_type payment_alice;
|
||||
{
|
||||
vesting_balance_create_operation op;
|
||||
op.creator = alice_id;
|
||||
op.owner = alice_id;
|
||||
op.amount = asset(500 * GRAPHENE_BLOCKCHAIN_PRECISION);
|
||||
op.balance_type = vesting_balance_type::normal;
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, alice_private_key);
|
||||
processed_transaction ptx = PUSH_TX(db, trx, ~0);
|
||||
trx.clear();
|
||||
payment_alice = ptx.operation_results[0].get<object_id_type>();
|
||||
}
|
||||
|
||||
generate_block();
|
||||
set_expiration(db, trx);
|
||||
|
||||
// alice becomes son
|
||||
{
|
||||
flat_map<graphene::peerplays_sidechain::sidechain_type, string> sidechain_public_keys;
|
||||
sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin address";
|
||||
|
||||
son_create_operation op;
|
||||
op.owner_account = alice_id;
|
||||
op.url = test_url;
|
||||
op.deposit = deposit_alice;
|
||||
op.pay_vb = payment_alice;
|
||||
op.signing_key = alice_public_key;
|
||||
op.sidechain_public_keys = sidechain_public_keys;
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, alice_private_key);
|
||||
PUSH_TX(db, trx, ~0);
|
||||
trx.clear();
|
||||
}
|
||||
generate_block();
|
||||
set_expiration(db, trx);
|
||||
|
||||
// create deposit vesting
|
||||
vesting_balance_id_type deposit_bob;
|
||||
{
|
||||
vesting_balance_create_operation op;
|
||||
op.creator = bob_id;
|
||||
op.owner = bob_id;
|
||||
op.amount = asset(500 * GRAPHENE_BLOCKCHAIN_PRECISION);
|
||||
op.balance_type = vesting_balance_type::son;
|
||||
op.policy = dormant_vesting_policy_initializer {};
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, bob_private_key);
|
||||
processed_transaction ptx = PUSH_TX(db, trx, ~0);
|
||||
trx.clear();
|
||||
deposit_bob = ptx.operation_results[0].get<object_id_type>();
|
||||
}
|
||||
generate_block();
|
||||
set_expiration(db, trx);
|
||||
|
||||
// create payment normal vesting
|
||||
vesting_balance_id_type payment_bob ;
|
||||
{
|
||||
vesting_balance_create_operation op;
|
||||
op.creator = bob_id;
|
||||
op.owner = bob_id;
|
||||
op.amount = asset(500 * GRAPHENE_BLOCKCHAIN_PRECISION);
|
||||
op.balance_type = vesting_balance_type::normal;
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, bob_private_key);
|
||||
processed_transaction ptx = PUSH_TX(db, trx, ~0);
|
||||
trx.clear();
|
||||
payment_bob = ptx.operation_results[0].get<object_id_type>();
|
||||
}
|
||||
generate_block();
|
||||
set_expiration(db, trx);
|
||||
|
||||
// bob becomes son
|
||||
{
|
||||
flat_map<graphene::peerplays_sidechain::sidechain_type, string> sidechain_public_keys;
|
||||
sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin address";
|
||||
|
||||
son_create_operation op;
|
||||
op.owner_account = bob_id;
|
||||
op.url = test_url;
|
||||
op.deposit = deposit_bob;
|
||||
op.pay_vb = payment_bob;
|
||||
op.signing_key = bob_public_key;
|
||||
op.sidechain_public_keys = sidechain_public_keys;
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, alice_private_key);
|
||||
PUSH_TX(db, trx, ~0);
|
||||
trx.clear();
|
||||
}
|
||||
generate_block();
|
||||
set_expiration(db, trx);
|
||||
|
||||
generate_blocks(60);
|
||||
set_expiration(db, trx);
|
||||
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Send son_wallet_recreate_operation");
|
||||
|
||||
son_wallet_recreate_operation op;
|
||||
|
||||
op.payer = db.get_global_properties().parameters.son_account();
|
||||
|
||||
{
|
||||
son_info si;
|
||||
si.son_id = son_id_type(0);
|
||||
si.total_votes = 1000;
|
||||
si.signing_key = alice_public_key;
|
||||
si.sidechain_public_keys[peerplays_sidechain::sidechain_type::bitcoin] = "";
|
||||
op.sons.push_back(si);
|
||||
}
|
||||
|
||||
{
|
||||
son_info si;
|
||||
si.son_id = son_id_type(1);
|
||||
si.total_votes = 1000;
|
||||
si.signing_key = bob_public_key;
|
||||
si.sidechain_public_keys[peerplays_sidechain::sidechain_type::bitcoin] = "";
|
||||
op.sons.push_back(si);
|
||||
}
|
||||
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, alice_private_key);
|
||||
PUSH_TX(db, trx, ~0);
|
||||
}
|
||||
generate_block();
|
||||
|
||||
BOOST_TEST_MESSAGE("Check son_wallet_recreate_operation results");
|
||||
|
||||
const auto& idx = db.get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
BOOST_REQUIRE( idx.size() == 1 );
|
||||
auto obj = idx.find(son_wallet_id_type(0));
|
||||
BOOST_REQUIRE( obj != idx.end() );
|
||||
BOOST_REQUIRE( obj->expires == time_point_sec::maximum() );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( son_wallet_update_test ) {
|
||||
|
||||
BOOST_TEST_MESSAGE("son_wallet_update_test");
|
||||
|
||||
INVOKE(son_wallet_recreate_test);
|
||||
GET_ACTOR(alice);
|
||||
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Send son_wallet_update_operation");
|
||||
|
||||
son_wallet_update_operation op;
|
||||
|
||||
op.payer = db.get_global_properties().parameters.son_account();
|
||||
op.son_wallet_id = son_wallet_id_type(0);
|
||||
op.sidechain = graphene::peerplays_sidechain::sidechain_type::bitcoin;
|
||||
op.address = "bitcoin address";
|
||||
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, alice_private_key);
|
||||
PUSH_TX(db, trx, ~0);
|
||||
}
|
||||
generate_block();
|
||||
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Check son_wallet_update_operation results");
|
||||
|
||||
const auto& idx = db.get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
BOOST_REQUIRE( idx.size() == 1 );
|
||||
auto obj = idx.find(son_wallet_id_type(0));
|
||||
BOOST_REQUIRE( obj != idx.end() );
|
||||
BOOST_REQUIRE( obj->addresses.at(graphene::peerplays_sidechain::sidechain_type::bitcoin) == "bitcoin address" );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
Loading…
Reference in a new issue